Identity Gateway 2024.3

OpenID Connect

The following sections provide an overview of how IG supports OpenID Connect 1.0, and examples of to set up IG as an OpenID Connect relying party in different deployment scenarios:

About IG with OpenID Connect

IG supports OpenID Connect 1.0, an authentication layer built on OAuth 2.0. OpenID Connect 1.0 is a specific implementation of OAuth 2.0, where the identity provider holds the protected resource that the third-party application wants to access. For more information, refer to OpenID Connect.

OpenID Connect refers to the following entities:

  • End user : An OAuth 2.0 resource owner whose user information the application needs to access.

    The end user wants to use an application through an existing identity provider account without signing up and creating credentials for another web service.

  • Relying Party (RP): An OAuth 2.0 client that needs access to the end user’s protected user information.

    For example, an online mail application needs to know which end user is accessing the application in order to present the correct inbox.

    As another example, an online shopping site needs to know which end user is accessing the site in order to present the right offerings, account, and shopping cart.

  • OpenID Provider (OP): An OAuth 2.0 Authorization Server and also resource server that holds the user information and grants access.

    The OP requires the end user to give the RP permission to access to some of its user information. Because OpenID Connect 1.0 defines unique identification for an account (subject identifier + issuer identifier), the RP can use that identification to bind its own user profile to a remote identity.

    For the online mail application, this key could be used to access the mailboxes and related account information. For the online shopping site, this key could be used to access the offerings, account, shopping cart and others. The key makes it possible to serve users as if they had local accounts.

  • UserInfo : The protected resource that the third-party application wants to access. The information about the authenticated end user is expressed in a standard format. The user-info endpoint is hosted on the Authorization Server and is protected with OAuth 2.0.

When IG acts as an OpenID Connect relying party, its role is to retrieve user information from the OpenID provider, and then to inject that information into the context for use by subsequent filters and handlers.

AM as an OpenID Connect provider

This section gives an example of how to set up AM as an OpenID Connect identity provider, and IG as a relying party for browser requests to the home page of the sample application.

The following sequence diagram shows the flow of information for a request to access the home page of the sample application, using AM as a single, preregistered OpenID Connect identity provider, and IG as a relying party:

oidc-am

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

  1. Set Up AM as an OpenID Connect provider:

    1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

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

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

    2. Create an OAuth 2.0 Authorization Server:

      1. Select Services > Add a Service > OAuth2 Provider.

      2. Add a service with the default values.

    3. Create an OAuth 2.0 Client to request OAuth 2.0 access tokens:

      1. Select Applications > OAuth 2.0 > Clients.

      2. Add a client with the following values:

        • Client ID: oidc_client

        • Client secret: password

        • Redirection URIs: https://ig.example.com:8443/home/id_token/callback

        • Scope(s): openid, profile, and email

      3. On the Advanced tab, select the following values:

        • Grant Types: Authorization Code

      4. On the Signing and Encryption tab, change ID Token Signing Algorithm to HS256, HS384, or HS512. The algorithm must be HMAC.

  2. Set up IG:

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

    2. Set an environment variable for oidc_client, and then restart IG:

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

      • Linux

      • Windows

      $HOME/.openig/config/routes/00-static-resources.json
      %appdata%\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"
      }
    4. Add the following route to IG:

      • Linux

      • Windows

      $HOME/.openig/config/routes/07-openid.json
      %appdata%\OpenIG\config\routes\07-openid.json
      {
        "name": "07-openid",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/home/id_token')}",
        "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": "oidc-user-info-client",
                      "type": "ClientRegistration",
                      "config": {
                        "clientId": "oidc_client",
                        "issuer": {
                          "name": "Issuer",
                          "type": "Issuer",
                          "config": {
                            "wellKnownEndpoint": "http://am.example.com:8088/openam/oauth2/.well-known/openid-configuration"
                          }
                        },
                        "scopes": [
                          "openid",
                          "profile",
                          "email"
                        ],
                        "authenticatedRegistrationHandler": "AuthenticatedRegistrationHandler-1"
                      }
                    }
                  ],
                  "requireHttps": false,
                  "cacheExpiration": "disabled"
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      For information about how to set up the IG route in Studio, see OpenID Connect in Structured Editor.

      Notice the following features about the route:

      • The route matches requests to /home/id_token.

      • The AuthorizationCodeOAuth2ClientFilter enables IG to act as a relying party. It uses a single client registration that is defined inline and refers to the AM server configured in AM as a single OpenID Connect provider.

      • The filter has a base client endpoint of /home/id_token, which creates the following service URIs:

        • Requests to /home/id_token/login start the delegated authorization process.

        • Requests to /home/id_token/callback are expected as redirects from the OAuth 2.0 Authorization Server (OpenID Connect provider). This is why the redirect URI in the client profile in AM is set to https://ig.example.com:8443/home/id_token/callback.

        • Requests to /home/id_token/logout remove the authorization state for the end user, and redirect to the specified URL if a goto parameter is provided.

          These endpoints are implicitly reserved. Attempts to access them directly can cause undefined errors.

      • 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" has the default value true.

      • The target for storing authorization state information is ${attributes.openid}. This is where subsequent filters and handlers can find access tokens and user information.

  3. Test the setup:

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

      The AM login page is displayed.

    2. Log in to AM as user demo, password Ch4ng31t, and then allow the application to access user information.

      The home page of the sample application is displayed.

Authenticate automatically to the sample application

To authenticate automatically to the sample application, change the last name of the user demo to match the password Ch4ng31t, and add a StaticRequestFilter like the following to the end of the chain in 07-openid.json:

{
  "type": "StaticRequestFilter",
  "config": {
    "method": "POST",
    "uri": "http://app.example.com:8081/login",
    "form": {
      "username": [
        "${attributes.openid.user_info.sub}"
      ],
      "password": [
        "${attributes.openid.user_info.family_name}"
      ]
    }
  }
}

The StaticRequestFilter retrieves the username and password from the context, and replaces the original HTTP GET request with an HTTP POST login request containing credentials.

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='
    1. Add the following route to IG to serve the sample application .css and other static resources:

      • Linux

      • Windows

      $HOME/.openig/config/routes/00-static-resources.json
      %appdata%\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"
      }
    2. Add the following route to Identity Gateway, replacing the value for the property amInstanceUrl:

      • Linux

      • Windows

      $HOME/.openig/config/routes/oidc-idc.json
      %appdata%\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"
          }
        }
      }

      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.

PingOne as an OpenID Connect provider

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

Before you start, prepare IG and the sample application as described in the Quick install.

  1. Set up the PingOne environment:

  2. Create a PingOne OIDC web application.

    Learn more from PingOne’s Creating a web application.

    1. In the test environment, create a web application with the following values:

      • Application Name: oidc_client

      • Description: OIDC client

      • Application Type: OIDC Web App

    2. In the application, select the Overview panel and click Protocol OpenID Connect.

    3. In the Redirect URIs field, add https://ig.example.com:8443/home/id_token/callback and then save the application.

      Learn more from PingOne’s Editing an application - OIDC.

    4. At the top-right of the page, click the slider to enable the application.

    5. Go to the Configuration panel and make a note of the following values in the URLs drop-down list:

      • OIDC Discovery Endpoint

      • Client ID

      • Client Secret

        The values are used in the IG setup.

  3. 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 to serve the sample application .css and other static resources:

      • Linux

      • Windows

      $HOME/.openig/config/routes/00-static-resources.json
      %appdata%\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"
      }
    3. Base64-encode the Client Secret for the web application created in the previous step, and then set the value as an environment variable:

      $ export OIDC_SECRET_ID='Yy5...A=='
    4. Add the following route to IG, replacing the values of the following properties with values for the web application created in the previous step:

      • OIDC_Discovery_Endpoint: OIDC Discovery Endpoint

      • Client_ID: Client ID

        • Linux

        • Windows

        $HOME/.openig/config/routes/oidc-ping.json
        %appdata%\OpenIG\config\routes\oidc-ping.json
        {
          "name": "oidc-ping",
          "baseURI": "http://app.example.com:8081",
          "condition": "${find(request.uri.path, '^/home/id_token')}",
          "properties": {
            "OIDC_Discovery_Endpoint": "OIDC Discovery endpoint of the web app",
            "Client_ID": "Client ID of the web app"
          },
          "heap": [
            {
              "name": "SystemAndEnvSecretStore-1",
              "type": "SystemAndEnvSecretStore"
            },
            {
              "name": "AuthenticatedRegistrationHandler-1",
              "type": "Chain",
              "config": {
                "filters": [
                  {
                    "name": "ClientSecretBasicAuthenticationFilter-1",
                    "type": "ClientSecretBasicAuthenticationFilter",
                    "config": {
                      "clientId": "&{Client_ID}",
                      "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/html; charset=UTF-8" ]
                        },
                        "entity": "<html><body>Error in OAuth 2.0 setup.<br> ${contexts.oauth2Failure.exception.message}</body></html>"
                      }
                    },
                    "registrations": [
                      {
                        "name": "oauth2-client",
                        "type": "ClientRegistration",
                        "config": {
                          "clientId": "${Client_ID}",
                          "issuer": {
                            "name": "PingOne",
                            "type": "Issuer",
                            "config": {
                              "wellKnownEndpoint": "&{OIDC_Discovery_Endpoint}"
                            }
                          },
                          "scopes": [
                            "openid"
                          ],
                          "authenticatedRegistrationHandler": "AuthenticatedRegistrationHandler-1"
                        }
                      }
                    ],
                    "requireHttps": false,
                    "cacheExpiration": "disabled"
                  }
                }
              ],
              "handler": "ReverseProxyHandler"
            }
          }
        }
    5. Restart IG.

  4. Test the setup:

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

      The PingOne login page is displayed.

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

    3. If prompted by PingOne, change the password of the demo user.

      The home page of the sample application is displayed.

Multiple OpenID Connect providers

This section gives an example of using OpenID Connect with two identity providers.

Client registrations for an AM provider and Identity Cloud provider are declared in the heap. The Nascar page helps the user to choose an identity provider.

  1. Set up AM as the first identity provider, as described in AM as a single OpenID Connect provider.

  2. Set up Identity Cloud as a second identity provider, as described in Identity Cloud as an OpenID Connect provider.

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

    • Linux

    • Windows

    $HOME/.openig/config/routes/07-openid-nascar.json
    %appdata%\OpenIG\config\routes\07-openid-nascar.json
    {
      "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"
          }
        },
        {
          "name": "openam",
          "type": "ClientRegistration",
          "config": {
            "clientId": "oidc_client",
            "issuer": {
              "name": "am_issuer",
              "type": "Issuer",
              "config": {
                "wellKnownEndpoint": "http://am.example.com:8088/openam/oauth2/.well-known/openid-configuration"
              }
            },
            "scopes": [
              "openid",
              "profile",
              "email"
            ],
            "authenticatedRegistrationHandler": "AuthenticatedRegistrationHandler-1"
          }
        },
        {
          "name": "idcloud",
          "type": "ClientRegistration",
          "config": {
            "clientId": "oidc_client",
            "issuer": {
              "name": "idc_issuer",
              "type": "Issuer",
              "config": {
                "wellKnownEndpoint": "&{amInstanceUrl}/oauth2/realms/alpha/.well-known/openid-configuration"
              }
            },
            "scopes": [
              "openid",
              "profile",
              "email"
            ],
            "authenticatedRegistrationHandler": "AuthenticatedRegistrationHandler-1"
          }
        },
        {
          "name": "NascarPage",
          "type": "StaticResponseHandler",
          "config": {
            "status": 200,
            "headers": {
              "Content-Type": [ "text/html; charset=UTF-8" ]
            },
            "entity": [
              "<html>",
              "  <body>",
              "    <p><a href='/home/id_token/login?registration=oidc_client&issuer=am_issuer&goto=${urlEncodeQueryParameterNameOrValue('https://ig.example.com:8443/home/id_token')}'>Access Management login</a></p>",
              "    <p><a href='/home/id_token/login?registration=oidc_client&issuer=idc_issuer&goto=${urlEncodeQueryParameterNameOrValue('https://ig.example.com:8443/home/id_token')}'>Identity Cloud login</a></p>",
              "  </body>",
              "</html>"
            ]
          }
        }
      ],
      "name": "07-openid-nascar",
      "baseURI": "http://app.example.com:8081",
      "condition": "${find(request.uri.path, '^/home/id_token')}",
      "properties": {
        "amInstanceUrl": "https://myTenant.forgeblocks.com/am"
      },
      "handler": {
        "type": "Chain",
        "config": {
          "filters": [
            {
              "type": "AuthorizationCodeOAuth2ClientFilter",
              "config": {
                "clientEndpoint": "/home/id_token",
                "failureHandler": {
                  "type": "StaticResponseHandler",
                  "config": {
                    "comment": "Trivial failure handler for debugging only",
                    "status": 500,
                    "headers": {
                      "Content-Type": [ "text/plain; charset=UTF-8" ]
                    },
                    "entity": "${contexts.oauth2Failure.error}: ${contexts.oauth2Failure.description}"
                  }
                },
                "loginHandler": "NascarPage",
                "registrations": [ "openam", "idcloud" ],
                "requireHttps": false,
                "cacheExpiration": "disabled"
              }
            }
          ],
          "handler": "ReverseProxyHandler"
        }
      }
    }

    Consider the differences with 07-openid.json:

    • The heap objects openam and idcloud define client registrations.

    • The StaticResponseHandler provides links to the client registrations.

    • The AuthorizationCodeOAuth2ClientFilter uses a loginHandler to allow users to choose a client registration and therefore an identity provider.

  4. Test the setup:

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

      The Nascar page offers the choice of identity provider.

    2. Using the following credentials, select a provider, log in, and allow the application to access user information:

      • AM: user demo, password Ch4ng31t.

      • Identity Cloud: user demo, password Ch4ng3!t

        The home page of the sample application is displayed.

Discovery and dynamic registration with OpenID Connect providers

OpenID Connect defines mechanisms for discovering and dynamically registering with an identity provider that isn’t known in advance, as specified in the following publications: OpenID Connect Discovery, OpenID Connect Dynamic Client Registration, and OAuth 2.0 Dynamic Client Registration Protocol.

In dynamic registration, issuer and client registrations are generated dynamically. They are held in memory and can be reused, but don’t persist when IG is restarted.

This section builds on the example in AM as a single OpenID Connect provider to give an example of discovering and dynamically registering with an identity provider that isn’t known in advance. In this example, the client sends a signed JWT to the Authorization Server.

To facilitate the example, a WebFinger service is embedded in the sample application. In a normal deployment, the WebFinger server is likely to be a service on the issuer’s domain.

  1. Set up a key

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

      $ cd /path/to/secrets
    2. Create a key:

      $ keytool -genkey \
        -alias myprivatekeyalias \
        -keyalg RSA \
        -keysize 2048 \
        -keystore keystore.p12 \
        -storepass keystore \
        -storetype PKCS12 \
        -keypass keystore \
        -validity 360 \
        -dname "CN=ig.example.com, OU=example, O=com, L=fr, ST=fr, C=fr"
  2. Set up AM:

    1. Set up AM as described in AM as a single OpenID Connect provider.

    2. Select the user demo, and change the last name to Ch4ng31t. For this example, the last name must be the same as the password.

    3. Configure the OAuth 2.0 Authorization Server for dynamic registration:

      1. Select Services > OAuth2 Provider.

      2. On the Advanced tab, add the following scopes to Client Registration Scope Allowlist: openid, profile, email.

      3. On the Client Dynamic Registration tab, select these settings:

        • Allow Open Dynamic Client Registration: Enabled

        • Generate Registration Access Tokens: Disabled

    4. Configure the authentication method for the OAuth 2.0 Client:

      1. Select Applications > OAuth 2.0 > Clients.

      2. Select oidc_client, and on the Advanced tab, select Token Endpoint Authentication Method: private_key_jwt.

  3. Set up IG:

    1. In the IG configuration, set an environment variable for the keystore password, and then restart IG:

      $ export KEYSTORE_SECRET_ID='a2V5c3RvcmU='

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

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

      • Linux

      • Windows

      $HOME/.openig/config/routes/00-static-resources.json
      %appdata%\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"
      }
    3. Add the following script to IG:

      • Linux

      • Windows

      $HOME/.openig/scripts/groovy/discovery.groovy
      %appdata%\OpenIG\scripts\groovy\discovery.groovy
      /*
       * OIDC discovery with the sample application
       */
      response = new Response(Status.OK)
      response.getHeaders().put(ContentTypeHeader.NAME, "text/html");
      response.entity = """
      <!doctype html>
      <html>
        <head>
          <title>OpenID Connect Discovery</title>
          <meta charset='UTF-8'>
        </head>
        <body>
          <form id='form' action='/discovery/login?'>
            Enter your user ID or email address:
              <input type='text' id='discovery' name='discovery'
                placeholder='demo or demo@example.com' />
              <input type='hidden' name='goto'
                value='${contexts.idpSelectionLogin.originalUri}' />
          </form>
          <script>
            // Make sure sampleAppUrl is correct for your sample app.
            window.onload = function() {
            document.getElementById('form').onsubmit = function() {
            // Fix the URL if not using the default settings.
            var sampleAppUrl = 'http://app.example.com:8081/';
            var discovery = document.getElementById('discovery');
            discovery.value = sampleAppUrl + discovery.value.split('@', 1)[0];
            };
          };
          </script>
        </body>
      </html>""" as String
      return response

      The script transforms the input into a discovery value for IG. This is not a requirement for deployment, only a convenience for the purposes of this example. Alternatives are described in the discovery protocol specification.

    4. Add the following route to IG, replacing /path/to/secrets/keystore.p12 with your path:

      • Linux

      • Windows

      $HOME/.openig/config/routes/07-discovery.json
      %appdata%\OpenIG\config\routes\07-discovery.json
      {
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "SecretsProvider-1",
            "type": "SecretsProvider",
            "config": {
              "stores": [
                {
                  "type": "KeyStoreSecretStore",
                  "config": {
                    "file": "/path/to/secrets/keystore.p12",
                    "mappings": [
                      {
                        "aliases": [ "myprivatekeyalias" ],
                        "secretId": "private.key.jwt.signing.key"
                      }
                    ],
                    "storePasswordSecretId": "keystore.secret.id",
                    "storeType": "PKCS12",
                    "secretsProvider": "SystemAndEnvSecretStore-1"
                  }
                }
              ]
            }
          },
          {
            "name": "DiscoveryPage",
            "type": "ScriptableHandler",
            "config": {
              "type": "application/x-groovy",
              "file": "discovery.groovy"
            }
          }
        ],
        "name": "07-discovery",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/discovery')}",
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "DynamicallyRegisteredClient",
                "type": "AuthorizationCodeOAuth2ClientFilter",
                "config": {
                  "clientEndpoint": "/discovery",
                  "requireHttps": false,
                  "requireLogin": true,
                  "target": "${attributes.openid}",
                  "failureHandler": {
                    "type": "StaticResponseHandler",
                    "config": {
                      "comment": "Trivial failure handler for debugging only",
                      "status": 500,
                      "headers": {
                        "Content-Type": [ "text/plain; charset=UTF-8" ]
                      },
                      "entity": "${contexts.oauth2Failure.error}: ${contexts.oauth2Failure.description}"
                    }
                  },
                  "loginHandler": "DiscoveryPage",
                  "discoverySecretId": "private.key.jwt.signing.key",
                  "tokenEndpointAuthMethod": "private_key_jwt",
                  "secretsProvider": "SecretsProvider-1",
                  "metadata": {
                    "client_name": "My Dynamically Registered Client",
                    "redirect_uris": [ "http://ig.example.com:8080/discovery/callback" ],
                    "scopes": [ "openid", "profile", "email" ]
                  }
                }
              },
              {
                "type": "StaticRequestFilter",
                "config": {
                  "method": "POST",
                  "uri": "http://app.example.com:8081/login",
                  "form": {
                    "username": [
                      "${attributes.openid.user_info.name}"
                    ],
                    "password": [
                      "${attributes.openid.user_info.family_name}"
                    ]
                  }
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      Consider the differences with 07-openid.json:

      • The route matches requests to /discovery.

      • The AuthorizationCodeOAuth2ClientFilter uses DiscoveryPage as the login handler, and specifies metadata to prepare the dynamic registration request.

      • DiscoveryPage uses a ScriptableHandler and script to provide the discovery parameter and goto parameter.

        If there is a match, then it can use the issuer’s registration endpoint and avoid an additional request to look up the user’s issuer using the WebFinger protocol.

        If there is no match, IG uses the discovery value as the resource for a WebFinger request using OpenID Connect Discovery protocol.

      • IG uses the discovery parameter to find an identity provider. IG extracts the domain host and port from the value, and attempts to find a match in the supportedDomains lists for issuers configured for the route.

      • When discoverySecretId is set, the tokenEndpointAuthMethod is always private_key_jwt. Clients send a signed JWT to the Authorization Server.

        Redirects IG to the end user’s browser, using the goto parameter, after the process is complete and IG has injected the OpenID Connect user information into the context.

  4. Test the setup:

    1. Log out of AM, and clear any cookies.

    2. Go to http://ig.example.com:8080/discovery.

    3. Enter the following email address: demo@example.com. The AM login page is displayed.

    4. Log in as user demo, password Ch4ng31t, and then allow the application to access user information. The sample application returns the user’s page.