PingAM 7.5.1

Hybrid grant

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:

  1. 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.

  2. 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:

  • Avoid requesting access tokens with the first request.

  • Protect against cross-site scripting (XSS) attacks, which could leak tokens in the redirection URL fragment to other systems.

  • Implement Cross-Origin Resource Sharing (CORS) to make OIDC requests across domains.

  • For public client RPs, use PKCE to mitigate against interception attacks.

Hybrid flow
Figure 1. Hybrid flow
  1. 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.

  2. 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…​

  3. …​to the OP.

  4. The OP authenticates the end user, confirms resource access, and gathers consent if necessary.

  5. On success, the OP redirects the end user to the RP.

  6. The OP appends an authorization code and tokens to the URL fragment.

  7. The RP stores the authorization code for future use and validates the ID token to get the subject ID.

  8. With the ID token, the RP provides services to the end user.

  9. 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.

  10. AM returns an access token.

  11. The RP sends a request to the /oauth2/userinfo endpoint with the access token for authorization.

  12. 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:

  1. 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

  2. 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

  3. 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

  4. Save your changes to the client profile.

  5. Create an end user profile and record the username and password.

Get a code and an ID token using a browser

  1. 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 and nonce parameters are optional and included to protect against CSRF attacks.

  2. Sign in as the end user and grant consent if necessary.

    AM redirects to the redirect_uri.

  3. 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

  1. 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"}
  2. 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 and nonce 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:

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>"
}