Transforming OpenID Connect ID Tokens Into SAML Assertions
This chapter builds on the example in Acting As an OpenID Connect Relying Party to transform OpenID Connect ID tokens into SAML 2.0 assertions.
Many enterprises use existing or legacy, SAML 2.0-based SSO, but many mobile and social applications are managed by OpenID Connect. Use the IG TokenTransformationFilter to bridge the gap between OpenID Connect and SAML 2.0 frameworks.
The following figure illustrates the data flow. For a more detailed view of the flow, see "Flow of Events".
A user tries to access to a protected resource.
If the user is not authenticated, the OAuth2ClientFilter redirects the request to AM. After authentication, AM asks for the user's consent to give IG access to private information.
If the user consents, AM returns an id_token to the OAuth2ClientFilter. The filter opens the id_token JWT and makes it available in
attributes.openid.id_token
andattributes.openid.id_token_claims
for downstream filters.The TokenTransformationFilter calls the AM STS to transform the id_token into a SAML 2.0 assertion.
The STS validates the signature, decodes the payload, and verifies that the user issued the transaction. The STS then issues a SAML assertion to IG on behalf of the user.
The TokenTransformationFilter makes the result of the token transformation available to downstream handlers in the
issuedToken
property of the${contexts.sts}
context.
The following sequence diagram shows a more detailed view of the flow:
Set up an AM Security Token Service (STS), where the subject confirmation method is Bearer. For more information about setting up a REST STS instance, see AM's Security Token Service (STS) Guide.
Set up AM as described in "Use AM As a Single OpenID Connect Provider".
Select Applications > Agents > Identity Gateway, add an agent with the following values:
Agent ID:
ig_agent
Password:
password
Leave all other values as default.
Select Applications > Agents > Java (or J2EE).
Add an agent with the following values:
Agent ID:
ig_agent
Agent URL:
http://openig.example.com:8080/agentapp
Server URL:
http://openam.example.com:8088/openam
Password:
password
On the Global tab, deselect Agent Configuration Change Notification.
This option stops IG from being notified about agent configuration changes in AM, because they are not required by IG.
Create a Bearer Module:
In the top level realm, select Authentication > Modules, and add a module with the following values:
Module name:
oidc
Type:
OpenID Connect id_token bearer
In the configuration page, enter the following values:
OpenID Connect validation configuration type:
Client Secret
OpenID Connect validation configuration value:
password
This is the password of the OAuth 2.0/OpenID Connect client.
Client secret:
password
Name of OpenID Connect ID Token Issuer:
http://openam.example.com:8088/openam/oauth2
Audience name:
oidc_client
This is the name of the OAuth 2.0/OpenID Connect client.
List of accepted authorized parties:
oidc_client
Leave all other values as default, and save your settings.
Create an instance of STS REST.
In the top level realm, select STS, and add a Rest STS instance with the following values:
Deployment URL Element:
openig
This value identifies the STS instance and is used by the
instance
parameter in the TokenTransformationFilter.SAML2 Token
SAML2 issuer Id:
OpenAM
Service Provider Entity Id:
openig_sp
NameIdFormat: Select
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
Note
For STS, it isn't necessary to create a SAML SP configuration in AM.
OpenID Connect Token
OpenIdConnect Token Provider Issuer Id:
oidc
Token signature algorithm: Enter a value that is consistent with "Using AM As a Single OpenID Connect Provider", for example,
HMAC SHA 256
Client Secret:
password
Issued Tokens Audience:
oidc_client
On the SAML 2 Token tab, add the following Attribute Mappings:
Key:
userName
, Value:uid
Key:
password
, Value:mail
Log out of AM.
Set up IG:
Set an environment variable for
oidc_client
andig_agent
, and then restart IG:$
export OIDC_SECRET_ID='cGFzc3dvcmQ='
$export AGENT_SECRET_ID='cGFzc3dvcmQ='
Add the following route to IG:
$HOME/.openig/config/routes/50-idtoken.json
%appdata%\OpenIG\config\routes\50-idtoken.json
{ "name": "50-idtoken", "baseURI": "http://app.example.com:8081", "condition": "${matches(request.uri.path, '^/home/id_token')}", "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": "OAuth2ClientFilter-1", "type": "OAuth2ClientFilter", "config": { "clientEndpoint": "/home/id_token", "failureHandler": { "type": "StaticResponseHandler", "config": { "status": 500, "headers": { "Content-Type": [ "text/plain" ] }, "entity": "An error occurred during the OAuth2 setup." } }, "registrations": [ { "name": "oidc-user-info-client", "type": "ClientRegistration", "config": { "clientId": "oidc_client", "clientSecretId": "oidc.secret.id", "secretsProvider": "SystemAndEnvSecretStore-1", "issuer": { "name": "Issuer", "type": "Issuer", "config": { "wellKnownEndpoint": "http://openam.example.com:8088/openam/oauth2/.well-known/openid-configuration" } }, "scopes": [ "openid", "profile", "email" ], "tokenEndpointAuthMethod": "client_secret_basic" } } ], "requireHttps": false, "cacheExpiration": "disabled" } }, { "name": "TokenTransformationFilter-1", "type": "TokenTransformationFilter", "config": { "idToken": "${attributes.openid.id_token}", "instance": "openig", "amService": "AmService-1" } } ], "handler": { "type": "StaticResponseHandler", "config": { "reason": "Found", "status": 200, "headers": { "Content-Type": [ "text/plain" ] }, "entity": "{\"id_token\":\n\"${attributes.openid.id_token}\"} \n\n\n{\"saml_assertions\":\n\"${contexts.sts.issuedToken}\"}" } } } } }
For information about how to set up the IG route in Studio, see "Token Transformation in Structured Editor".
Notice the following features of the route:
The route matches requests to
/home/id_token
.The AmService in the heap is used for authentication and REST STS requests.
The OAuth2ClientFilter enables IG to act as an OpenID Connect relying party:
The client endpoint is set to
/home/id_token
, so the service URIs for this filter on the IG server are/home/id_token/login
,/home/id_token/logout
, and/home/id_token/callback
.For convenience in this test,
requireHttps
is false. In production environments, set it to true. So that you see the delegated authorization process when you make a request,requireLogin
is true.The target for storing authorization state information is
${attributes.openid}
. Subsequent filters and handlers can find access tokens and user information at this target.
The ClientRegistration holds configuration provided in "Using AM As a Single OpenID Connect Provider", and used by IG to connect with AM.
The TokenTransformationFilter transforms an id_token into a SAML assertion:
The
id_token
parameter defines where this filter gets the id_token created by theOAuth2ClientFilter
.The TokenTransformationFilter makes the result of the token transformation available to downstream handlers in the
issuedToken
property of the${contexts.sts}
context.The
instance
parameter must match theDeployment URL Element
for the REST STS instance.
Errors that occur during token transformation cause an error response to be returned to the client and an error message to be logged for the IG administrator.
When the request succeeds, a StaticResponseHandler retrieves and displays the id_token from the target
{attributes.openid.id_token}
.
Test the setup:
Go to http://openig.example.com:8080/home/id_token.
The AM login screen is displayed.
Log in to AM as username
george
, passwordC0stanza
.An OpenID Connect request to access private information is displayed.
Select Allow.
The id_token and SAML assertions are displayed:
{"id_token": "eyAidHlwIjogIkpXVCIsICJhbGciOiAiSFMyNTYiIH0.eyAiYXRfaGFzaCI6ICJ . . ."} {"saml_assertions": <"saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version= . . ."}