Identity Gateway 2024.3

Identity Cloud

This guide provides examples of how to integrate your business application and APIs with Identity Cloud for Single Sign-On and API Security, with ForgeRock Identity Gateway. It is for ForgeRock Identity Cloud evaluators, administrators, and architects.

Example installation for this guide

Unless otherwise stated, the examples in this guide assume the following installation:

When using the ForgeRock Identity Cloud, you need to know the value of the following properties:

  • The root URL of your ForgeRock Identity Cloud. For example, https://myTenant.forgeblocks.com.

    The URL of the Access Management component of the ForgeRock Identity Cloud is the root URL of your Identity Cloud followed by /am. For example, https://myTenant.forgeblocks.com/am.

  • The realm where you work. The examples in this document use alpha.

    Prefix each realm in the hierarchy with the realms keyword. For example, /realms/root/realms/alpha.

If you use a different configuration, substitute in the procedures accordingly.

Authenticate an IG agent to Identity Cloud

IG agents are automatically authenticated to Identity Cloud by a non-configurable authentication module. Authentication chains and modules are deprecated in Identity Cloud and replaced by journeys.

You can now authenticate IG agents to Identity Cloud with a journey. The procedure is currently optional, but will be required when authentication chains and modules are removed in a future release of Identity Cloud.

For more information, refer to Identity Cloud’s Journeys.

This section describes how to create a journey to authenticate an IG agent to Identity Cloud. The journey has the following requirements:

  • It must be called Agent

  • Its nodes must pass the agent credentials to the Agent Data Store Decision node.

When you define a journey in Identity Cloud, that same journey is used for all instances of IG, Java agent, and Web agent. Consider this point if you change the journey configuration.

  1. Log in to the Identity Cloud admin UI as an administrator.

  2. Click Journeys > New Journey.

  3. Add a journey with the following information and click Create journey:

    • Name: Agent

    • Identity Object: The user or device to authenticate.

    • (Optional) Description: Authenticate an IG agent to Identity Cloud

    The journey designer is displayed, with the Start entry point connected to the Failure exit point, and a Success node.

  4. Using the Filter nodes bar, find and then drag the following nodes from the Components panel into the designer area:

    • Zero Page Login Collector node to check whether the agent credentials are provided in the incoming authentication request, and use their values in the following nodes.

      This node is required for compatibility with Java agent and Web agent.

    • Page node to collect the agent credentials if they are not provided in the incoming authentication request, and use their values in the following nodes.

    • Agent Data Store Decision node to verify the agent credentials match the registered IG agent profile.

    Many nodes can be configured in the panel on the right side of the page. Unless otherwise stated, do not configure the nodes, and use only the default values.
  5. Drag the following nodes from the Components panel into the Page node:

    • Platform Username node to prompt the user to enter their username.

    • /auth-node-ref/latest/auth-node-platform-password.html[Platform Password] node to prompt the user to enter their password.

  6. Connect the nodes as follows and save the journey:

    A journey that can be used to authenticate an agent to Identity Cloud.

Register an IG agent in Identity Cloud

This procedure registers an agent that acts on behalf of IG.

  1. Log in to the Identity Cloud admin UI as an administrator.

  2. Click verified_user Gateways & Agents > New Gateway/Agent > Identity Gateway > Next, and add an agent profile:

    • ID: agent-name

    • Password: agent-password

    • Redirect URLs: URL for CDSSO

    Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
  3. Click Save Profile > Done. The agent profile page is displayed.

  4. Click open_in_new Native Consoles > Access Management and make the following optional changes in the AM admin UI.

    Change Action

    Store the agent password in AM’s secret service.

    Set a Secret Label Identifier, and configure a mapping to the corresponding secret. If AM finds a matching secret in a secret store, it uses that secret instead of the agent password configured in Step 2.

    The secret label has the format am.application.agents.identifier.secret, where identifier is the Secret Label Identifier.

    The Secret Label Identifier can contain only characters a-z, A-Z, 0-9, and periods (.). It can’t start or end with a period.

    Note the following points:

    • Set a Secret Label Identifier that clearly identifies the agent.

    • If you update or delete the Secret Label Identifier, AM updates or deletes the corresponding mapping for the previous identifier provided no other agent shares the mapping.

    • When you rotate a secret, update the corresponding mapping.

    Direct login to a custom URL instead of the default AM login page.

    Configure Login URL Template for CDSSO.

    Apply a different introspection scope.

    Click Token Introspection and select a scope from the drop-down list.

Set up a demo user in Identity Cloud

This procedure sets up a demo user in the alpha realm.

  1. Log in to the Identity Cloud admin UI as an administrator.

  2. Go to group Identities > Manage > settings_system_daydream Alpha realm - Users, and add a user with the following values:

    • Username: demo

    • First name: demo

    • Last name: user

    • Email Address: demo@example.com

    • Password: Ch4ng3!t

About Identity Gateway and the ForgeRock Identity Cloud

ForgeRock Identity Cloud simplifies the consumption of ForgeRock as an Identity Platform. However, many organizations have business web applications and APIs deployed across multiple clouds, or on-premise.

Identity Gateway facilitates non-intrusive integration of your web applications and APIs with the Identity Cloud, for SSO and API Security. The following image illustrates how Identity Gateway bridges your business to the ForgeRock Identity Cloud:

Identity Gateway bridges business applications and APIs to the ForgeRock Identity Cloud.

For information about the ForgeRock Identity Cloud, refer to the ForgeRock Identity Cloud Docs.

OAuth 2.0

This example sets up OAuth 2.0, using the standard introspection endpoint, where ForgeRock Identity Cloud is the Authorization Server, and Identity Gateway is the resource server.

For more information about Identity Gateway as an OAuth 2.0 resource server, refer to Validate access tokens through the introspection endpoint.

This procedure uses the Resource Owner Password Credentials grant type. According to information in the The OAuth 2.0 Authorization Framework, minimize use of this grant type and utilize other grant types whenever possible.

Before you start, prepare Identity Cloud, IG, and the sample application as described in Example installation for this guide.

  1. Set up Identity Cloud:

    1. Log in to the Identity Cloud admin UI as an administrator.

    2. Make sure you are managing the alpha realm. If not, click the current realm at the top of the screen, and switch realm.

    3. Go to group Identities > Manage > settings_system_daydream Alpha realm - Users, and add a user with the following values:

      • Username: demo

      • First name: demo

      • Last name: user

      • Email Address: demo@example.com

      • Password: Ch4ng3!t

    4. Go to Applications > Custom Application > OIDC - OpenId Connect > Web and add a web application with the following values:

      • Name: oauth2-client

      • Owners: demo user

      • Client Secret: password

      • Sign On > Grant Types: Authorization Code, Resource owner Password Credentials

      • Sign On > Scopes: mail

        For more information, refer to Identity Cloud’s Application management.

    5. Register an IG agent with the following values, as described in Register an IG agent in Identity Cloud:

      • ID: ig_agent

      • Password: password

    6. (Optional) Authenticate the agent to Identity Cloud as described in Authenticate an IG agent to Identity Cloud.

      IG agents are automatically authenticated to Identity Cloud by a deprecated authentication module in Identity Cloud. This step is currently optional, but will be required when authentication chains and modules are removed in a future release of Identity Cloud.
  2. Set up Identity Gateway:

    1. Set up IG for HTTPS, as described in Configure IG for HTTPS (server-side).

    2. Set an environment variable for the IG agent password, and then restart IG:

      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      bash

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

    3. Add the following route to Identity Gateway, replacing the value for the property amInstanceUrl:

      $HOME/.openig/config/routes/oauth2rs-idc.json
      {
        "name": "oauth2rs-idc",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/oauth2rs-idc')}",
        "properties": {
          "amInstanceUrl": "https://myTenant.forgeblocks.com/am"
        },
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "url": "&{amInstanceUrl}",
              "realm": "/alpha",
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "OAuth2ResourceServerFilter-1",
                "type": "OAuth2ResourceServerFilter",
                "config": {
                  "scopes": [
                    "mail"
                  ],
                  "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>Decoded access_token: ${contexts.oauth2.accessToken.info}</h2></body></html>"
              }
            }
          }
        }
      }
      json

      Notice the following features of the route compared to rs-introspect.json in Validate access tokens through the introspection endpoint, where a local Access Management instance is the Authorization Server:

      • The AmService URL points to Access Management in the Identity Cloud.

      • The AmService realm points to the realm where you have configured your web application and the IG agent.

  3. Test the setup:

    1. In a terminal, export an environment variable for the URL of Access Management in Identity Cloud:

      $ export amInstanceUrl='myAmInstanceUrl'
    2. Use a curl command similar to the following to retrieve an access token:

      $ mytoken=$(curl -s \
      --user "oauth2-client:password" \
      --data 'grant_type=password&username=demo&password=Ch4ng3!t&scope=mail' \
      $amInstanceUrl/oauth2/realms/alpha/access_token | jq -r ".access_token")
    3. Validate the access token returned in the previous step:

      $ curl -v \
      --cacert /path/to/secrets/ig.example.com-certificate.pem \
      --header "Authorization: Bearer ${mytoken}" \
      https://ig.example.com:8443/oauth2rs-idc 
      
      {
        active = true,
        scope = mail,
        realm = /alpha,
        client_id = oauth2-client,
        ...
      }

Identity Cloud as an OpenID Connect provider

This example sets up ForgeRock Identity Cloud as an OpenID Connect identity provider, and Identity Gateway as a relying party.

For more information about Identity Gateway and OpenID Connect, refer to OpenID Connect.

Before you start, prepare Identity Cloud, IG, and the sample application as described in Example installation for this guide.

  1. Set up Identity Cloud:

    1. Log in to the Identity Cloud admin UI as an administrator.

    2. Make sure you are managing the alpha realm. If not, click the current realm at the top of the screen, and switch realm.

    3. Go to group Identities > Manage > settings_system_daydream Alpha realm - Users, and add a user with the following values:

      • Username: demo

      • First name: demo

      • Last name: user

      • Email Address: demo@example.com

      • Password: Ch4ng3!t

    4. Go to Applications > Custom Application > OIDC - OpenId Connect > Web and add a web application with the following values:

      • Name: oidc_client

      • Owners: demo user

      • Client Secret: password

      • Sign On > Sign-in URLs: https://ig.example.com:8443/home/id_token/callback

      • Sign On > Grant Types: Authorization Code

      • Sign On > Scopes: openid, profile, email

      • Show advanced settings > Authentication > Implied Consent: On

    For more information, refer to Identity Cloud’s Application management.

  2. Set up Identity Gateway:

    1. Set an environment variable for the oidc_client password, and then restart IG:

      $ export OIDC_SECRET_ID='cGFzc3dvcmQ='
      bash
    1. Add the following route to IG to serve the sample application .css and other static resources:

      $HOME/.openig/config/routes/00-static-resources.json
      {
        "name" : "00-static-resources",
        "baseURI" : "http://app.example.com:8081",
        "condition": "${find(request.uri.path,'^/css') or matchesWithRegex(request.uri.path, '^/.*\\\\.ico$') or matchesWithRegex(request.uri.path, '^/.*\\\\.gif$')}",
        "handler": "ReverseProxyHandler"
      }
      json
    2. Add the following route to Identity Gateway, replacing the value for the property amInstanceUrl:

      $HOME/.openig/config/routes/oidc-idc.json
      {
        "name": "oidc-idc",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/home/id_token')}",
        "properties": {
          "amInstanceUrl": "https://myTenant.forgeblocks.com/am"
        },
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AuthenticatedRegistrationHandler-1",
            "type": "Chain",
            "config": {
              "filters": [
                {
                  "name": "ClientSecretBasicAuthenticationFilter-1",
                  "type": "ClientSecretBasicAuthenticationFilter",
                  "config": {
                    "clientId": "oidc_client",
                    "clientSecretId": "oidc.secret.id",
                    "secretsProvider": "SystemAndEnvSecretStore-1"
                  }
                }
              ],
              "handler": "ForgeRockClientHandler"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "AuthorizationCodeOAuth2ClientFilter-1",
                "type": "AuthorizationCodeOAuth2ClientFilter",
                "config": {
                  "clientEndpoint": "/home/id_token",
                  "failureHandler": {
                    "type": "StaticResponseHandler",
                    "config": {
                      "status": 500,
                      "headers": {
                        "Content-Type": [
                          "text/plain"
                        ]
                      },
                      "entity": "Error in OAuth 2.0 setup."
                    }
                  },
                  "registrations": [
                    {
                      "name": "oauth2-client",
                      "type": "ClientRegistration",
                      "config": {
                        "clientId": "oidc_client",
                        "issuer": {
                          "name": "Issuer",
                          "type": "Issuer",
                          "config": {
                            "wellKnownEndpoint": "&{amInstanceUrl}/oauth2/realms/alpha/.well-known/openid-configuration"
                          }
                        },
                        "scopes": [
                          "openid",
                          "profile",
                          "email"
                        ],
                        "authenticatedRegistrationHandler": "AuthenticatedRegistrationHandler-1"
                      }
                    }
                  ],
                  "requireHttps": false,
                  "cacheExpiration": "disabled"
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      json

      Compared to 07-openid.json in AM as a single OpenID Connect provider, where Access Management is running locally, the ClientRegistration wellKnownEndpoint points to Identity Cloud.

  3. Test the setup:

    1. In your browser’s privacy or incognito mode, go to https://ig.example.com:8443/home/id_token.

      The Identity Cloud login page is displayed.

    2. Log in to Identity Cloud as user demo, password Ch4ng3!t. The home page of the sample application is displayed.

Cross-domain single sign-on

For organizations relying on AM’s session and policy services with SSO, consider cross-Domain Single Sign-On (CDSSO) as an alternative to SSO through OpenID Connect.

This example sets up ForgeRock Identity Cloud as an SSO authentication server for requests processed by Identity Gateway. For more information about about Identity Gateway and CDSSO, refer to Authenticate with CDSSO.

Before you start, prepare Identity Cloud, IG, and the sample application as described in Example installation for this guide.

  1. Set up Identity Cloud:

    1. Log in to the Identity Cloud admin UI as an administrator.

    2. Make sure you are managing the alpha realm. If not, click the current realm at the top of the screen, and switch realm.

    3. Go to group Identities > Manage > settings_system_daydream Alpha realm - Users, and add a user with the following values:

      • Username: demo

      • First name: demo

      • Last name: user

      • Email Address: demo@example.com

      • Password: Ch4ng3!t

    4. Register an IG agent with the following values, as described in Register an IG agent in Identity Cloud:

      • ID: ig_agent

      • Password: password

      • Redirect URLs: https://ig.ext.com:8443/home/cdsso/redirect

    5. (Optional) Authenticate the agent to Identity Cloud as described in Authenticate an IG agent to Identity Cloud.

      IG agents are automatically authenticated to Identity Cloud by a deprecated authentication module in Identity Cloud. This step is currently optional, but will be required when authentication chains and modules are removed in a future release of Identity Cloud.
    6. Add a Validation Service:

      1. In Identity Cloud, select open_in_new Native Consoles > Access Management. The AM admin UI is displayed.

      2. Select Services, and add a validation service with the following Valid goto URL Resources:

        • https://ig.ext.com:8443/*

        • https://ig.ext.com:8443/*?*

  2. Set up Identity Gateway:

    1. Set up IG for HTTPS, as described in Configure IG for HTTPS (server-side).

    2. Add the following session configuration to admin.json, to ensure that the browser passes the session cookie in the form-POST to the redirect endpoint (step 6 of Information flow during CDSSO):

      {
        "connectors": […​],
        "session": {
          "cookie": {
            "sameSite": "none",
            "secure": true
          }
        },
        "heap": […​]
      }

      This step is required for the following reasons:

      • When sameSite is strict or lax, the browser does not send the session cookie, which contains the nonce used in validation. If IG doesn’t find the nonce, it assumes that the authentication failed.

      • When secure is false, the browser is likely to reject the session cookie.

        For more information, refer to admin.json.

    3. Set an environment variable for the IG agent password, and then restart IG:

      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      bash

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

    4. Add the following route to IG to serve the sample application .css and other static resources:

      $HOME/.openig/config/routes/00-static-resources.json
      {
        "name" : "00-static-resources",
        "baseURI" : "http://app.example.com:8081",
        "condition": "${find(request.uri.path,'^/css') or matchesWithRegex(request.uri.path, '^/.*\\\\.ico$') or matchesWithRegex(request.uri.path, '^/.*\\\\.gif$')}",
        "handler": "ReverseProxyHandler"
      }
      json
    5. Add the following route to Identity Gateway, and correct the value for the property amInstanceUrl:

      $HOME/.openig/config/routes/cdsso-idc.json
      {
        "name": "cdsso-idc",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/home/cdsso')}",
        "properties": {
          "amInstanceUrl": "https://myTenant.forgeblocks.com/am"
        },
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "url": "&{amInstanceUrl}",
              "realm": "/alpha",
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "sessionCache": {
                "enabled": false
              }
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "CrossDomainSingleSignOnFilter-1",
                "type": "CrossDomainSingleSignOnFilter",
                "config": {
                  "redirectEndpoint": "/home/cdsso/redirect",
                  "authCookie": {
                    "path": "/home",
                    "name": "ig-token-cookie"
                  },
                  "amService": "AmService-1"
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      json

      Notice the following features of the route compared to cdsso.json in CDSSO for IG in standalone mode, where Access Management is running locally:

      • The AmService URL points to Access Management in the Identity Cloud.

      • The AmService realm points to the realm where you configure your IG agent.

    6. Restart IG.

  3. Test the setup:

    1. In your browser’s privacy or incognito mode, go to https://ig.ext.com:8443/home/cdsso.

      The Identity Cloud login page is displayed.

    2. Log in to Identity Cloud as user demo, password Ch4ng3!t.

      Access Management calls /home/cdsso/redirect, and includes the CDSSO token. The CrossDomainSingleSignOnFilter passes the request to sample app.

Policy enforcement

The following procedure gives an example of how to request and enforce policy decisions from Identity Cloud.

Enforce a simple policy

Before you start, set up and test the example in Cross-domain single sign-on.

  1. Set up Identity Cloud:

    1. In the Identity Cloud admin UI, select open_in_new Native Consoles > Access Management. The AM admin UI is displayed.

    2. Select Authorization > Policy Sets > New Policy Set, and add a policy set with the following values:

      • Id : PEP-CDSSO

      • Resource Types : URL

    3. In the new policy set, add a policy with the following values:

      • Name : CDSSO

      • Resource Type : URL

      • Resource pattern : *://*:*/*

      • Resource value : http://app.example.com:8081/home/cdsso

        This policy protects the home page of the sample application.

    4. On the Actions tab, add an action to allow HTTP GET.

    5. On the Subjects tab, remove any default subject conditions, add a subject condition for all Authenticated Users.

  2. Set up IG:

    1. Replace cdsso-idc.json with the following route, and correct the value for the property amInstanceUrl:

      $HOME/.openig/config/routes/pep-cdsso-idc.json
      {
        "name": "pep-cdsso-idc",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/home/cdsso')}",
        "properties": {
          "amInstanceUrl": "https://myTenant.forgeblocks.com/am"
        },
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "url": "&{amInstanceUrl}",
              "realm": "/alpha",
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "sessionCache": {
                "enabled": false
              }
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "CrossDomainSingleSignOnFilter-1",
                "type": "CrossDomainSingleSignOnFilter",
                "config": {
                  "redirectEndpoint": "/home/cdsso/redirect",
                  "authCookie": {
                    "path": "/home",
                    "name": "ig-token-cookie"
                  },
                  "amService": "AmService-1"
                }
              },
              {
                "name": "PolicyEnforcementFilter-1",
                "type": "PolicyEnforcementFilter",
                "config": {
                  "application": "PEP-CDSSO",
                  "ssoTokenSubject": "${contexts.cdsso.token}",
                  "amService": "AmService-1"
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      json

      Note the following feature of the route compared to cdsso-idc.json:

      • The CrossDomainSingleSignOnFilter is followed by a PolicyEnforcementFilter to enforce the policy PEP-CDSSO.

  3. Test the setup:

    1. Go to https://ig.ext.com:8443/home/cdsso.

      If you have warnings that the site is not secure respond to the warnings to access the site.

      IG redirects you to Identity Cloud for authentication.

    2. Log in to Identity Cloud as user demo, password Ch4ng3!t.

      Identity Cloud redirects you back to the request URL, and IG requests a policy decision. Identity Cloud returns a policy decision that grants access to the sample application.

Step up authorization for a transaction

Before you start, set up and test the example in pep.adoc#pep-cdsso.

  1. In the Identity Cloud admin UI, select code Scripts > Auth Scripts > New Script > Journey Decision Node > Next, and add a default Journey Decision Node Script script called TxTestPassword:

    /*
      - Data made available by nodes that have already executed are available in the sharedState variable.
      - The script should set outcome to either "true" or "false".
     */
    
    var givenPassword = nodeState.get("password").asString()
    
    if (givenPassword.equals("7890")) {
      outcome = "true"
    } else {
      outcome = "false"
    }
    javascript
  2. Configure a journey:

    1. Click account_tree Journeys and add a journey with the following configuration:

      • Name: Tx01_Tree

      • Identity Object: Alpha realm users

        The journey canvas is displayed.

    2. In Nodes > Basic Authentication, drag a Password Collector node onto the canvas.

    3. In Nodes > Utilities, drag a Scripted decision node onto the canvas.

    4. Configure the scripted decision node as follows:

      • Script: select TxTestPassword

      • Outcomes: enter true and false

    5. Connect the nodes as shown:

      Authentication journey

      For information about configuring trees, refer to ForgeRock Identity Cloud Docs

  3. Edit the authorization policy:

    1. In the Identity Cloud admin UI, select open_in_new Native Consoles > Access Management. The AM admin UI is displayed.

    2. Select Authorization > Policy Sets > PEP-CDSSO, and add the following environment condition to the CDSSO policy:

      • All of

      • Type: Transaction

      • Script name: Authenticate to tree

      • Strategy Specifier: Tx01_Tree

  4. Test the setup:

    1. In a browser, go to https://ig.ext.com:8443/home/cdsso.

      If you have not previously authenticated to Identity Cloud, the CrossDomainSingleSignOnFilter redirects the request to Identity Cloud for authentication.

    2. Log in to Identity Cloud as user demo, password Ch4ng3!t.

    3. Enter the password 7890 required by the script TxTestPassword.

      Identity Cloud redirects you back to the request URL, and IG requests a policy decision. Identity Cloud returns a policy decision based on the authentication journey.

Pass runtime data downstream in a JWT

This example sets up Identity Cloud as an identity provider, to pass identity or other runtime information downstream, in a JWT signed with a PEM.

For more information about using runtime data, refer to Passing data along the chain. To help with development, the sample application includes a /jwt endpoint to display the JWT, verify its signature, and decrypt it.

Before you start, prepare Identity Cloud, IG, and the sample application as described in Example installation for this guide.

  1. Set up secrets:

    1. Locate a directory for secrets, and go to it:

      $ cd /path/to/secrets
      bash
    2. Create the following secret key and certificate pair as PEM files:

      $ openssl req \
      -newkey rsa:2048 \
      -new \
      -nodes \
      -x509 \
      -days 3650 \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout ig.example.com-key.pem \
      -out ig.example.com-certificate.pem
      bash

      Two PEM files are created, one for the secret key, and another for the associated certificate.

    3. Map the key and certificate to the same secret ID in IG:

      $ cat ig.example.com-key.pem ig.example.com-certificate.pem > key.manager.secret.id.pem
      bash
    4. Generate PEM files to sign and verify the JWT:

      $ openssl req \
      -newkey rsa:2048 \
      -new \
      -nodes \
      -x509 \
      -days 3650 \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout id.key.for.signing.jwt.pem \
      -out id.key.for.verifying.jwt.pem
      bash
    5. Make sure the following files have been added to your secrets directory:

      • id.key.for.signing.jwt.pem

      • id.key.for.verifying.jwt.pem

      • key.manager.secret.id.pem

      • ig.example.com-certificate.pem

      • ig.example.com-key.pem

  2. Set up Identity Cloud:

    1. Log in to the Identity Cloud admin UI as an administrator.

    2. Go to group Identities > Manage > settings_system_daydream Alpha realm - Users, and add a user with the following values:

      • Username: demo

      • First name: demo

      • Last name: user

      • Email Address: demo@example.com

      • Password: Ch4ng3!t

    3. Register an IG agent with the following values, as described in Register an IG agent in Identity Cloud:

      • ID: ig_agent_jwt

      • Password: password

      • Redirect URLs: https://ig.example.com:8443/jwt/redirect

    4. (Optional) Authenticate the agent to Identity Cloud as described in Authenticate an IG agent to Identity Cloud.

      IG agents are automatically authenticated to Identity Cloud by a deprecated authentication module in Identity Cloud. This step is currently optional, but will be required when authentication chains and modules are removed in a future release of Identity Cloud.
    5. Add a Validation Service:

      1. In Identity Cloud, select open_in_new Native Consoles > Access Management. The AM admin UI is displayed.

      2. Select Services, and add a validation service with the following Valid goto URL Resources:

        • https://ig.example.com:8443/*

        • https://ig.example.com:8443/*?*

  3. Set up IG:

    1. Set up TLS by adding the following file to IG, replacing the value for the property secretsDir:

      $HOME/.openig/config/admin.json
      {
        "mode": "DEVELOPMENT",
        "properties": {
          "secretsDir": "/path/to/secrets"
        },
        "connectors": [
          {
            "port": 8080
          },
          {
            "port": 8443,
            "tls": "ServerTlsOptions-1"
          }
        ],
        "session": {
          "cookie": {
            "sameSite": "none",
            "secure": true
          }
        },
        "heap": [
          {
            "name": "ServerTlsOptions-1",
            "type": "ServerTlsOptions",
            "config": {
              "keyManager": {
                "type": "SecretsKeyManager",
                "config": {
                  "signingSecretId": "key.manager.secret.id",
                  "secretsProvider": "ServerIdentityStore"
                }
              }
            }
          },
          {
            "name": "ServerIdentityStore",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "PLAIN",
              "directory": "&{secretsDir}",
              "suffix": ".pem",
              "mappings": [{
                "secretId": "key.manager.secret.id",
                "format": {
                  "type": "PemPropertyFormat"
                }
              }]
            }
          }
        ]
      }
      json
    2. Set an environment variable for the IG agent password, and then restart IG:

      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      bash

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

    3. Add the following route to IG to serve the sample application .css and other static resources:

      $HOME/.openig/config/routes/00-static-resources.json
      {
        "name" : "00-static-resources",
        "baseURI" : "http://app.example.com:8081",
        "condition": "${find(request.uri.path,'^/css') or matchesWithRegex(request.uri.path, '^/.*\\\\.ico$') or matchesWithRegex(request.uri.path, '^/.*\\\\.gif$')}",
        "handler": "ReverseProxyHandler"
      }
      json
    4. Add the following route to IG, replacing the value for the properties secretsDir and amInstanceUrl:

      $HOME/.openig/config/routes/jwt-idc.json
      {
        "name": "jwt-idc",
        "condition": "${find(request.uri.path, '/jwt')}",
        "baseURI": "http://app.example.com:8081",
        "properties": {
          "secretsDir": "/path/to/secrets",
          "amInstanceUrl": "https://myTenant.forgeblocks.com/am"
        },
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "url": "&{amInstanceUrl}",
              "realm": "/alpha",
              "agent": {
                "username": "ig_agent_jwt",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "sessionCache": {
                "enabled": false
              }
            }
          },
          {
            "name": "pemPropertyFormat",
            "type": "PemPropertyFormat"
          },
          {
            "name": "FileSystemSecretStore-1",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "PLAIN",
              "directory": "&{secretsDir}",
              "suffix": ".pem",
              "mappings": [{
                "secretId": "id.key.for.signing.jwt",
                "format": "pemPropertyFormat"
              }]
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "CrossDomainSingleSignOnFilter-1",
                "type": "CrossDomainSingleSignOnFilter",
                "config": {
                  "redirectEndpoint": "/jwt/redirect",
                  "authCookie": {
                    "path": "/jwt",
                    "name": "ig-token-cookie"
                  },
                  "amService": "AmService-1"
                }
              },
              {
                "name": "UserProfileFilter",
                "type": "UserProfileFilter",
                "config": {
                  "username": "${contexts.ssoToken.info.uid}",
                  "userProfileService": {
                    "type": "UserProfileService",
                    "config": {
                      "amService": "AmService-1"
                    }
                  }
                }
              },
              {
                "name": "JwtBuilderFilter-1",
                "type": "JwtBuilderFilter",
                "config": {
                  "template": {
                    "name": "${contexts.userProfile.commonName}",
                    "email": "${contexts.userProfile.rawInfo.mail[0]}"
                  },
                  "secretsProvider": "FileSystemSecretStore-1",
                  "signature": {
                    "secretId": "id.key.for.signing.jwt",
                    "algorithm": "RS512"
                  }
                }
              },
              {
                "name": "HeaderFilter-1",
                "type": "HeaderFilter",
                "config": {
                  "messageType": "REQUEST",
                  "add": {
                    "x-openig-user": ["${contexts.jwtBuilder.value}"]
                  }
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      json
  4. Test the setup:

    1. Go to https://ig.example.com:8443/jwt.

      If you receive warnings that the site is not secure, respond to the warnings to access the site. The Identity Cloud login page is displayed.

    2. Log in to Identity Cloud as user demo, password Ch4ng3!t. The sample app displays the signed JWT along with its header and payload.

    3. In USE PEM FILE, enter the absolute path to id.key.for.verifying.jwt.pem to verify the JWT signature.

Secure the OAuth 2.0 access token endpoint

This section uses a GrantSwapJwtAssertionOAuth2ClientFilter to transform requests for OAuth 2.0 access tokens into secure JWT bearer grant type requests. It propagates the transformed requests to Identity Cloud to obtain an access token.

Use GrantSwapJwtAssertionOAuth2ClientFilter to increase the security of less-secure grant-type requests, such as Client credentials grant requests or Resource owner password credentials grant requests.

The GrantSwapJwtAssertionOAuth2ClientFilter obtains access tokens from the /oauth2/access_token endpoint. To prevent unwanted or malicious access to the endpoint, make sure only a well-defined set of clients can use this filter.

Consider the following options to secure access to the GrantSwapJwtAssertionOAuth2ClientFilter:

  • Deploy IG on a trusted network.

  • Use mutual TLS (mTLS) and X.509 certificates for authentication between clients and IG. For more information, refer to OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens.

  • Configure an AllowOnlyFilter in front of the GrantSwapJwtAssertionOAuth2ClientFilter to control access within a route.

  • Define restrictive Route conditions to allow access only for expected grant-type requests. For example, define a route condition that requires a specific client ID, grant-type, or scope.

  • Configure a ScriptableFilter in front of the GrantSwapJwtAssertionOAuth2ClientFilter to validate requests.

The following figure shows the flow of information for a grant swap:

GrantSwapJwtAssertionOAuth2ClientFilter

Before you start, prepare Identity Cloud, IG, and the sample application as described in Example installation for this guide.

  1. Set up Identity Cloud:

    1. Log in to the Identity Cloud admin UI as an administrator.

    2. Create a service account with the following values, as described in Create a new service account:

      • Name: myServiceAccount

      • Scopes: fr:idm:* All Identity Management APIs

        The service account ID is displayed and you are prompted to download the private key. The public key is held in Identity Cloud.

        For more information, refer to Service accounts.

    3. Make a note of the service account ID and download the private key to your secrets directory.

    4. Rename the key to match the regex format (\.[a-zA-Z0-9])*. For example, rename myServiceAccount_privateKey.jwk to privateKey.jwk.

  2. Set up IG:

    1. Set up IG for HTTPS, as described in Configure IG for HTTPS (server-side).

    2. Add the following route to IG:

      $HOME/.openig/config/routes/grant-swap.json
      {
        "name" : "grant-swap",
        "properties": {
          "idcInstanceUrl": "https://myTenant.forgeblocks.com",
          "issuer": "service-account-id",
          "secretsDir": "path-to-secrets",
          "privateKeyFilename": "privateKey.jwk"
        },
        "condition" : "#{find(request.uri.path, '^/am/oauth2/access_token') && request.entity.form['grant_type'][0] == 'client_credentials'}",
        "baseURI" : "&{idcInstanceUrl}:443/",
        "heap" : [ {
          "name": "JwkPropertyFormat-01",
          "type": "JwkPropertyFormat"
        },
          {
            "name": "FileSystemSecretStore-01",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "JwkPropertyFormat-01",
              "directory": "&{secretsDir}",
              "mappings": [ {
                "secretId": "&{privateKeyFilename}",
                "format": "JwkPropertyFormat-01"
              }
              ]
            }
          }
        ],
        "handler" : {
          "type" : "Chain",
          "capture" : "all",
          "config" : {
            "filters" : [
              {
                "name" : "GrantSwapJwtAssertionOAuth2ClientFilter-01",
                "description": "access /access_token endpoint with jwt-bearer-profile",
                "type" : "GrantSwapJwtAssertionOAuth2ClientFilter",
                "capture" : "all",
                "config" : {
                  "clientId" : "service-account",
                  "assertion" : {
                    "issuer" : "&{issuer}",
                    "audience" : "&{idcInstanceUrl}/am/oauth2/access_token",
                    "subject" : "&{issuer}",
                    "expiryTime": "2 minutes"
                  },
                  "signature": {
                    "secretId": "&{privateKeyFilename}",
                    "includeKeyId": false
                  },
                  "secretsProvider": "FileSystemSecretStore-01",
                  "scopes" : {
                    "type": "RequestFormResourceAccess"
                  }
                }
              }
            ],
            "handler" : "ForgeRockClientHandler"
          }
        }
      }
      json
    3. In the route, replace the values for the following properties with your values:

      • idcInstanceUrl: The root URL of your Identity Cloud.

      • issuer: The ID of the service account created in Identity Cloud

      • secretsDir: The directory containing the downloaded private key

      • privateKeyFilename: The filename of the downloaded private key

    4. Notice the following features of the route:

      • The condition intercepts only client_credentials grant-type requests on the path /am/oauth2/access_token. A more secure condition can be set on the client ID.

      • Requests are rebased to the Identity Cloud URL.

      • A FileSystemSecretStore loads the private-key JWK used to sign the JWT.

      • The GrantSwapJwtAssertionOAuth2ClientFilter:

        • Requires the core JWT claims issuer, subject, audience, and expiryTime.

        • Uses RequestFormResourceAccess to extract scopes from the inbound request for inclusion in the JWT-assertion grant-type request propagated to AM.

        • Signs the JWT with the JWK provided by the service account.

      • The GrantSwapJwtAssertionOAuth2ClientFilter clientId refers to the OAuth 2.0 client ID created by AM. The value must be service-account.

    5. Add the following route to IG to return a standard OAuth 2.0 error response if the request fails the route condition:

      $HOME/.openig/config/routes/zz-returns-invalid-request.json
      {
        "name" : "zz-returns-invalid-request",
        "handler" : {
          "type" : "StaticResponseHandler",
          "capture" : "all",
          "config" : {
            "status": 400,
            "headers": {"Content-Type": ["application/json; charset=UTF-8"]},
            "entity": "{\"error\": \"Invalid_request\", \"error_description\": \"Invalid request\"}"
          }
        }
      }
      json
  3. Test the setup by accessing the route with a curl command similar to this:

    $ curl  \
        --cacert /path/to/secrets/ig.example.com-certificate.pem \
        --location \
        --request POST 'https://ig.example.com:8443/am/oauth2/access_token' \
        --header 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode 'client_id=myServiceAccount' \
        --data-urlencode 'grant_type=client_credentials' \
        --data-urlencode 'scope=fr:idm:*'
    
    {"access_token":"eyJ...","scope":"fr:idm:*","token_type":"Bearer","expires_in":899}

    The command makes a client_credentials grant-type request on the path /am/oauth2/access_token, supplying the client ID and scopes. IG transforms the request into a JWT-assertion grant-type request and propagates it to Identity Cloud.

    Because the service account in Identity Cloud supports the requested scope, the GrantSwapJwtAssertionOAuth2ClientFilter returns an access token.