Using OAuth 2.0 client credentials
This example shows how a client service accesses an OAuth 2.0-protected resource by using its OAuth 2.0 client credentials.
-
Set up the AM as an authorization server:
-
Select Applications > Agents > Identity Gateway, and 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:
-
Linux
-
Windows
$HOME/.openig/config/routes/oauth2-protected-resource.json
%appdata%\OpenIG\config\routes\oauth2-protected-resource.json
{ "name": "oauth2-protected-resource", "condition": "${find(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://am.example.com:8088/openam/", "version": "7.2" } } ], "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; charset=UTF-8" ] }, "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 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:
-
Linux
-
Windows
$HOME/.openig/config/routes/client-credentials.json
%appdata%\OpenIG\config\routes\client-credentials.json
{ "name": "client-credentials", "baseURI": "http://ig.example.com:8080", "condition" : "${find(request.uri.path, '^/client-credentials')}", "heap" : [ { "name" : "clientSecretAccessTokenExchangeHandler", "type" : "Chain", "capture" : "all", "config" : { "filters" : [ { "type" : "ClientSecretBasicAuthenticationFilter", "config" : { "clientId" : "client-service", "clientSecretId" : "client.secret.id", "secretsProvider" : { "type" : "Base64EncodedSecretStore", "config" : { "secrets" : { "client.secret.id" : "cGFzc3dvcmQ=" } } } } } ], "handler" : "ForgeRockClientHandler" } }, { "name" : "oauth2EnabledClientHandler", "type" : "Chain", "capture" : "all", "config" : { "filters" : [ { "type" : "ClientCredentialsOAuth2ClientFilter", "config" : { "tokenEndpoint" : "http://am.example.com:8088/openam/oauth2/access_token", "endpointHandler": "clientSecretAccessTokenExchangeHandler", "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.
-
The ClientCredentialsOAuth2ClientFilter calls the clientSecretAccessTokenExchangeHandler to exchange tokens on the authorization endpoint.
-
The clientSecretAccessTokenExchangeHandler calls a ClientSecretBasicAuthenticationFilter to authenticate the client through the HTTP basic access authentication scheme, and a ForgeRockClientHandler to propagate the request.
-
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://ig.example.com:8080/client-credentials. A message shows that access is granted.