Hybrid grant
- Endpoints
OpenID Connect (OIDC) authentication using the hybrid flow lets a relying party (RP) choose when to request access and ID tokens.
The hybrid grant flow is a two-step process:
-
The RP first requests a code and tokens by setting the response type:
Response type OpenID provider (OP) returns code id_token
An authorization code and an ID token
code token
An authorization code and an access token
code token id_token
An authorization code, and access token, and an ID token
AM returns the code and the requested tokens in the fragment of the redirection URL.
-
After the first request but before the authorization code expires (default: 120 seconds), the RP makes a second request to exchange the authorization code for additional tokens.
Follow these security recommendations when implementing the hybrid flow:
|
-
The end user wants to access services the RP provides. The RP requires an account to provide access to the services.
The end user makes a request to the RP to access their information stored at the OP.
-
To access the end user’s information at the OP, the RP needs authorization from the end user. The RP redirects the end user’s browser…
-
…to the OP.
-
The OP authenticates the end user, confirms resource access, and gathers consent if necessary.
-
On success, the OP redirects the end user to the RP.
-
The OP appends an authorization code and tokens to the URL fragment.
-
The RP stores the authorization code for future use and validates the ID token to get the subject ID.
-
With the ID token, the RP provides services to the end user.
-
Before the authorization code expires, the RP exchanges it for an access token, which the RP can use to get more information about the end user.
-
AM returns an access token.
-
The RP sends a request to the /oauth2/userinfo endpoint with the access token for authorization.
-
If the access token is valid, the
/oauth2/userinfo
endpoint returns any additional claims.The RP can use the subject ID and the additional claims to identify the end user.
Demonstrate the flow
Prepare the demonstration
Complete these steps to prepare the hybrid flow demonstration:
-
Register the RP as a confidential client application with the following settings:
- Client ID
-
myClient
- Client Secret
-
forgerock
- Redirection URIs
-
https://www.example.com:443/callback
- Scopes
-
openid
profile
-
On the Core tab of the client profile, update this setting as appropriate for your use case and save your changes:
- Authorization code lifetime (seconds)
-
Default: 120
-
Switch to the Advanced tab of the client profile and add these settings:
- Grant Types
-
Authorization Code
Implicit
- Response Types
-
code id_token
code token
code token id_token
-
Save your changes to the client profile.
-
Create an end user profile and record the username and password.
Get a code and an ID token using a browser
-
As RP, browse to the /oauth2/authorize endpoint with at least the following parameters:
-
client_id:
myClient
-
response_type:
code id_token
-
scope:
openid profile
-
redirect_uri:
https://www.example.com:443/callback
For example:
https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/authorize \ ?client_id=myClient \ &response_type=code%20id_token \ &scope=openid%20profile \ &state=abc123 \ &nonce=123abc \ &redirect_uri=https://www.example.com:443/callback
The URL is split and spaces added for readability purposes.
The
scope
parameter is optional if default values are configured in the authorization server or the client.The
state
andnonce
parameters are optional and included to protect against CSRF attacks. -
-
Sign in as the end user and grant consent if necessary.
AM redirects to the
redirect_uri
. -
Inspect the URL in the browser:
https://www.example.com:443/callback#code=<authorization-code>&id_token=<id-token>…
Get a code and an ID token using REST
-
Authenticate as the end user:
$ curl \ -i \ --request POST \ --header "Content-Type: application/json" \ --header "X-OpenAM-Username: <end-user-id>" \ --header "X-OpenAM-Password: <end-user-password>" \ --header "Accept-API-Version: resource=2.0, protocol=1.0" \ 'https://openam.example.com:8443/openam/json/realms/root/realms/alpha/authenticate' {"tokenId":"<tokenId>","successUrl":"/enduser/?realm=/alpha","realm":"/alpha"}
-
As RP, make an HTTP POST request to the /oauth2/authorize endpoint with the following parameters:
-
scope:
openid profile
-
response_type:
code id_token
-
client_id:
myClient
-
csrf:
<tokenId>
-
redirect_uri:
https://www.example.com:443/callback
-
decision:
allow
For example:
$ curl \ --dump-header - \ --request POST \ --Cookie "iPlanetDirectoryPro=<tokenId>" \ --data "scope=openid profile" \ --data "response_type=code id_token" \ --data "client_id=myClient" \ --data "csrf=<tokenId>" \ --data "redirect_uri=https://www.example.com:443/callback" \ --data "state=abc123" \ --data "nonce=123abc" \ --data "decision=allow" \ "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/authorize"
The URL is split and spaces added for readability purposes.
The
scope
parameter is optional if default values are configured in the authorization server or the client.The
state
andnonce
parameters are optional and included to protect against CSRF attacks.AM returns an HTTP 302 response with the code and ID token in the redirection URL fragment:
HTTP/1.1 302 Found … Location: https://www.example.com:443/callback#code=<authorization-code>&id_token=<id-token>… …
-
Exchange the code for an access token
Choose one of the following options:
-
Use the authorization code grant to exchange an authorization code for an access token.
-
Use the authorization code grant with PKCE to exchange an authorization code for an access token.
Additional OIDC claims
An RP can request additional claims about the end user with the access token at the /oauth2/userinfo endpoint:
$ curl \
--request GET \
--header "Authorization Bearer <access-token>" \
"https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/userinfo"
{
"name": "<end-user-display-name>",
"family_name": "<end-user-family-name>",
"given_name": "<end-user-given-name>",
"sub": "<end-user-id>",
"subname": "<end-user-id>"
}