ClientCredentialsOAuth2ClientFilter

Authenticates OAuth 2.0 clients by using the client's OAuth 2.0 credentials to obtain an access_token from an authorization server, and injecting the access_token into the inbound request as a Bearer Authorization header.

The filter obtains the client's access_token by using the client_credentials grant type, where the credentials are sent with the client_secret_basic method. The filter refreshes the access_token as required.

Use this filter in a service-to-service context, where services need to access resources protected by OAuth 2.0.

Usage

{
  "name": string,
  "type": "ClientCredentialsOAuth2ClientFilter",
  "config": {
    "clientId": configuration expression<sting>,
    "clientSecretId": configuration expression<secret-id>,
    "secretsProvider": SecretsProvider reference,
    "tokenEndpoint": configuration expression<url>,
    "scopes": [ configuration expression<string>, ... ],
    "handler": Handler reference or inline Handler declaration
  }
}

Properties

"clientId": configuration expression<string>, required

The ID of the OAuth 2.0 client registered with the authorization server.

"clientSecretId": configuration expression<secret-id>, required

The ID to use when querying the secretsProvider for the client secret.

"secretsProvider": SecretsProvider reference, required

The "SecretsProvider" to use to resolve queried secrets, such as passwords and cryptographic keys. Provide either the name of a SecretsProvider object defined in the heap, or specify a SecretsProvider object inline.

"tokenEndpoint": configuration expression<url>, required

The URL to the authorization server's OAuth 2.0 token endpoint.

"scopes": array of configuration expression<string>, optional

Array of scope strings to request from the authorization server.

Default: Empty, request no scopes.

"handler": Handler reference or inline Handler declaration, optional

The Handler to use to access the authorization server's OAuth 2.0 token endpoint. Provide either the name of a handler object defined in the heap, or specify a handler object inline.

Default: ClientHandler

Log Level

To facilitate debugging secrets for this filter, in logback.xml add a logger defined by the fully qualified package name of the secrets API backend. The following line in logback.xml sets the log level to ALL:

<logger name="org.forgerock.secrets.oauth2" level="ALL">

Examples

The following example shows the flow of information when a client service accesses an OAuth 2.0-protected resource, using its OAuth 2.0 client credentials:

Accessing an OAuth 2.0-Protected Resource Using OAuth 2.0 Client Credentials
Set Up the Example
  1. Set up the AM as an Authorization Server:

    1. Select Applications > Agents > Identity Gateway, add an agent with the following values:

      • Agent ID: ig_agent

      • Password: password

      • Token Introspection: Realm Only

    2. Create an OAuth 2.0 Authorization Server:

      1. Select  Services > Add a Service > OAuth2 Provider.

      2. Add a service with the default values.

    3. Create an OAuth 2.0 client to request access_tokens, using client credentials for authentication:

      1. Select Applications > OAuth 2.0 > Clients, and add a client with the following values:

        • Client ID: client-service

        • Client secret: password

        • Scope(s): client-scope

      2. (From AM 6.5) On the Advanced tab, select the following value:

        • Grant Types: Client Credentials

  2. Set up IG:

    1. Set an environment variable for the IG agent password, and then restart IG:

      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

    2. Add the following route to IG:

      $HOME/.openig/config/routes/oauth2-protected-resource.json
      %appdata%\OpenIG\config\routes\oauth2-protected-resource.json
      {
        "name": "oauth2-protected-resource",
        "condition": "${matches(request.uri.path, '^/oauth2-protected-resource')}",
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "url": "http://openam.example.com:8088/openam/",
              "version": "7"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "OAuth2ResourceServerFilter-1",
                "type": "OAuth2ResourceServerFilter",
                "config": {
                  "scopes": [ "client-scope" ],
                  "requireHttps": false,
                  "realm": "OpenIG",
                  "accessTokenResolver": {
                    "name": "TokenIntrospectionAccessTokenResolver-1",
                    "type": "TokenIntrospectionAccessTokenResolver",
                    "config": {
                      "amService": "AmService-1",
                      "providerHandler": {
                        "type": "Chain",
                        "config": {
                          "filters": [
                            {
                              "type": "HttpBasicAuthenticationClientFilter",
                              "config": {
                                "username": "ig_agent",
                                "passwordSecretId": "agent.secret.id",
                                "secretsProvider": "SystemAndEnvSecretStore-1"
                              }
                            }
                          ],
                          "handler": "ForgeRockClientHandler"
                        }
                      }
                    }
                  }
                }
              }
            ],
            "handler": {
              "type": "StaticResponseHandler",
              "config": {
                "status": 200,
                "headers": {
                  "Content-Type": [ "text/html" ]
                },
                "entity": "<html><body><h2>Access Granted</h2></body></html>"
              }
            }
          }
        }
      }

      Notice the following features of the route:

      • The route matches requests to /oauth2-protected-resource.

      • The OAuth2ResourceServerFilter expects an OAuth 2.0 access_token in the header of the incoming authorization request, with the scope client-scope.

        The filter uses a TokenIntrospectionAccessTokenResolver to resolve the access_token. The introspect endpoint is protected with HTTP Basic Authentication, and the providerHandler uses an HttpBasicAuthenticationClientFilter to provide the resource server credentials.

        For convenience in this test, "requireHttps" is false. In production environments, set it to true.

      • After the filter successfully validates the access_token, it creates a new context from the authorization server response, containing information about the access_token.

      • The StaticResponseHandler returns a message that access is granted.

    3. Add the following route to IG:

      $HOME/.openig/config/routes/client-credentials.json
      %appdata%\OpenIG\config\routes\client-credentials.json
      {
        "name": "client-credentials",
        "baseURI": "http://openig.example.com:8080",
        "condition" : "${matches(request.uri.path, '^/client-credentials')}",
        "heap": [
          {
            "name": "oauth2EnabledClientHandler",
            "type": "Chain",
            "capture": "all",
            "config": {
              "filters": [
                {
                  "type": "ClientCredentialsOAuth2ClientFilter",
                  "config": {
                    "clientId": "client-service",
                    "clientSecretId": "client.secret.id",
                    "secretsProvider": {
                      "type": "Base64EncodedSecretStore",
                      "config": {
                        "secrets": {
                          "client.secret.id": "cGFzc3dvcmQ="
                        }
                      }
                    },
                    "tokenEndpoint": "http://openam.example.com:8088/openam/oauth2/access_token",
                    "scopes" : ["client-scope"]
                  }
                }
              ],
              "handler": "ForgeRockClientHandler"
            }
          }
        ],
        "handler": {
          "type": "ScriptableHandler",
          "config": {
            "type": "application/x-groovy",
            "clientHandler": "oauth2EnabledClientHandler",
            "source": [
              "request.uri.path = '/oauth2-protected-resource'",
              "return http.send(context, request);"
            ]
          }
        }
      }

      Note the following features of the route:

      • The route matches requests to /client-credentials.

      • The ScriptableHandler rewrites the request to target it to /oauth2-protected-resource, and then calls the HTTP client, that has been redefined to use the oauth2EnabledClientHandler.

      • The oauth2EnabledClientHandler calls the ClientCredentialsOAuth2ClientFilter to obtain an access_token from AM by using the client's OAuth 2.0 credentials, and then injects it into the request as a Bearer Authorization header.

      • The route oauth2-protected-resource.json uses the AM introspection endpoint to resolve the access_token and display its contents.

  3. Test the setup by accessing the route on http://openig.example.com:8080/client-credentials.

    A message shows that access is granted.

Read a different version of :