Search code examples
javaoauth-2.0java-ee-8forgerockpac4j

Pac4J: Custom ForgeRock OAuth2 with UI and API


We use a custom product in house called https://www.forgerock.com/ ForgeRock and it supports Oauth2. Following the Pac4J code base I was able to create a custom ForgeRock Client.

public class ForgeRockClient extends OAuth20Client {

   public static final String DEFAULT_SCOPE = "read write";

   private final String baseUrl;

   public ForgeRockClient(final String url, final String key, final String secret) {
      setScope(DEFAULT_SCOPE);
      setKey(key);
      setSecret(secret);
      this.baseUrl = url;
   }

   @Override
   protected void internalInit(final boolean forceReinit) {
      String tokenEndpoint = this.baseUrl + "/oauth2/access_token";
      String authorizeEndpoint = this.baseUrl + "/oauth2/authorize";
      String revokeEndpoint = this.baseUrl + "/oauth2/revoke";
      String userEndpoint = this.baseUrl + "/oauth2/userinfo";
      ForgeRockApi api = new ForgeRockApi(tokenEndpoint, authorizeEndpoint, revokeEndpoint);
      configuration.setApi(api);
      configuration.setProfileDefinition(new ForgeRockProfileDefinition(userEndpoint));
      configuration.setWithState(true);
      configuration.setTokenAsHeader(true);
      defaultLogoutActionBuilder((ctx, session, profile, targetUrl) -> Optional
               .of(HttpActionHelper.buildRedirectUrlAction(ctx, this.baseUrl + "/logout")));

      super.internalInit(forceReinit);
   }

   public String getScope() {
      return getConfiguration().getScope();
   }

   public void setScope(final String scope) {
      getConfiguration().setScope(scope);
   }
}

This is working properly and my UI now redirects to Forgerock, I login and it redirects back and I get my proper UserProfile. Now my next part is the first thing our app does is then makes an API call to a REST Service and in our old SSO we forwarded the cookies and the REST Service would authenticate with ForeRock using that cookie.

My question is how using the AccessToken I have from my UI login can I submit to a REST API that I also control and want to verify the AccessToken is valid and get the information out of it. I don't know if Pac4J has what I need to do this because most of the code I have seen is about OAuth2 indirect client for UI access.

Any thoughts or help would be appreciated? I have seen others mention using Authorization: Bearer <Token> to send to the REST Service?

SO far I have just been researching the issue and I can't seem to find a common consensus or answer and was hoping someone from Pac4J or other OAuth2 experts could shed some light on the problem?


Solution

  • In OAuth2 words, your application is the Client and you are trying to obtain a Resource (some data) on behalf of the user from the Resource Server (API). The Access Token is proofing that the user allowed this access.

    What you typically do is pass the access token as a Bearer token to the API (cf. https://www.oauth.com/oauth2-servers/accessing-data/making-api-requests/).

    What the Resource Server then needs to do is verify the token (cf. https://www.oauth.com/oauth2-servers/the-resource-server/). It depends on the configuration of ForgeRock AM, how the access token is configured to look like. Most likely it is probably a referencial token (i.e. no signed JWT containing the content of the token). That means the token needs to be send to the ForgeRock AM Token Introspection API (cf. https://backstage.forgerock.com/docs/am/7/oauth2-guide/varlist-oauth2-introspect-endpoint.html):

    $ curl \
    --request POST \
    --header "Authorization: Basic ZGVtbzpDaDRuZzMxdA==" \
    --data "token=f9063e26-3a29-41ec-86de-1d0d68aa85e9" \
    "https://openam.example.com:8443/openam/oauth2/introspect"
    {
        "active": true,
        "scope": "write",
        "client_id": "myClient",
        "user_id": "demo",
        "username": "demo",
        "token_type": "Bearer",
        "exp": 1419356238,
        "sub": "demo",
        "iss": "https://openam.example.com:8443/openam/oauth2"
        "cnf": {
            "jwk": {
                "alg": "RS512",
                "e": "AQAB",
                "n": "k7qLlj...G2oucQ",
                "kty": "RSA",
                "use": "sig",
                "kid": "myJWK"
            },
        "auth_level": 0
        }
    }
    

    The resulting body is giving the content of the token. Based on the content the Resource Server can decide if the client is authorized to access the information.