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>, requiredThe ID of the OAuth 2.0 client registered with the authorization server.
"clientSecretId"
: configuration expression<secret-id>, requiredThe ID to use when querying the
secretsProvider
for the client secret."secretsProvider"
: SecretsProvider reference, requiredThe "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>, requiredThe URL to the authorization server's OAuth 2.0 token endpoint.
"scopes"
: array of configuration expression<string>, optionalArray of scope strings to request from the authorization server.
Default: Empty, request no scopes.
"handler"
: Handler reference or inline Handler declaration, optionalThe 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:
Set up the AM as an Authorization Server:
Select Applications > Agents > Identity Gateway, add an agent with the following values:
Agent ID:
ig_agent
Password:
password
Token Introspection:
Realm Only
Create an OAuth 2.0 Authorization Server:
Select Services > Add a Service > OAuth2 Provider.
Add a service with the default values.
Create an OAuth 2.0 client to request access_tokens, using client credentials for authentication:
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
(From AM 6.5) On the Advanced tab, select the following value:
Grant Types:
Client Credentials
Set up IG:
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.
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 scopeclient-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.
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.
Test the setup by accessing the route on http://openig.example.com:8080/client-credentials.
A message shows that access is granted.