OpenIG As an OAuth 2.0 Client or OpenID Connect Relying Party

OpenIG helps integrate applications into OAuth 2.0 and OpenID Connect deployments. In this chapter, you will learn to:

  • Configure OpenIG as an OAuth 2.0 client

  • Configure OpenIG as an OpenID Connect 1.0 relying party

  • Configure OpenIG to use OpenID Connect discovery and dynamic client registration

About OpenIG As an OAuth 2.0 Client

As described in OpenIG As an OAuth 2.0 Resource Server, an OAuth 2.0 client is the third-party application that needs access to a user’s protected resources. The client application therefore has the user (the OAuth 2.0 resource owner) delegate authorization by authenticating with an identity provider (the OAuth 2.0 authorization server) using an existing account, and then consenting to authorize access to protected resources (on an OAuth 2.0 resource server).

OpenIG can act as an OAuth 2.0 client when you configure an OAuth2ClientFilter as described in OAuth2ClientFilter(5) in the Configuration Reference. The OAuth2ClientFilter handles the process of allowing the user to select a provider, and redirecting the user through the authentication and authorization steps of an OAuth 2.0 authorization code grant, which results in the authorization server returning an access token to the filter.

When an authorization grant succeeds, the OAuth2ClientFilter injects the access token data into a configurable target in the context so that subsequent filters and handlers have access to the access token. Subsequent requests can use the access token without reauthentication. If an authorization grant fails, the failureHandler is invoked.

If the protected application is an OAuth 2.0 resource server, then OpenIG can send the access token with the resource request.

About OpenIG As an OpenID Connect 1.0 Relying Party

The specifications available through the OpenID Connect site describe an authentication layer built on OAuth 2.0, which is OpenID Connect 1.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 aims to access. This resource is the UserInfo, information about the authenticated end-user expressed in a standard format. In OpenID Connect 1.0, the key entities are the following:

  • The end user (OAuth 2.0 resource owner) whose user information the application needs to access.

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

  • The Relying Party (RP) (OAuth 2.0 client) 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.

  • The OpenID Provider (OP) (OAuth 2.0 authorization server and also resource server) that holds the user information and grants access.

    The OP effectively has the end user consent to providing the RP with access to some of its user information. As OpenID Connect 1.0 defines unique identification for an account (subject identifier + issuer identifier), the RP can use this as a key to its own user profile.

    In the case of the online mail application, this key could be used to access the mailboxes and related account information. In the case of 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.

When OpenIG acts therefore as an OpenID Connect 1.0 relying party, its ultimate 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.

In the tutorial that follows, you configure OpenIG as a relying party, and use OpenAM as the OpenID Provider.

Preparing the Tutorial

Getting Started describes how to configure OpenIG to proxy traffic and capture request and response data. You also learned how to configure OpenIG to use a static request to log in with hard-coded credentials.

This tutorial shows you how OpenIG can act as an OpenID Connect 1.0 relying party.

This tutorial relies on OpenAM as an OpenID Provider. As a relying party, OpenIG takes the end user to OpenAM for authorization and an access token. It then uses the access token to get end user information from OpenAM.

Before you start this tutorial, prepare OpenIG and the minimal HTTP server as described in Getting Started.

OpenIG should be running in Jetty, configured to access the minimal HTTP server as described in that chapter.

Setting Up OpenAM As an OpenID Provider

  1. Install and configure OpenAM on http://openam.example.com:8088/openam with the default configuration.

    If you use a different configuration, make sure you substitute in the tutorial accordingly. Although this tutorial does not use HTTPS, you must use HTTPS to protect access tokens and user information in production environments.

  2. Login to the OpenAM console as administrator.

    In the console for OpenAM 12 and earlier, use the common task to configure OAuth 2.0/OpenID Connect in the top-level realm. In the console for OpenAM 13 and later, use the wizard under Dashboard > Configure OAuth Provider > Configure OpenID Connect for the top-level realm. This configures OpenAM as an OAuth 2.0 authorization server and OpenID Provider.

  3. Create an OAuth 2.0 Client profile in the top-level realm.

    This allows OpenIG to communicate with OpenAM as an OAuth 2.0 client.

    In the console for OpenAM 12 and earlier, browse to Access Control > / (Top Level Realm) > Agents > OAuth 2.0 Client. In the console for OpenAM 13 and later, select the top-level realm and browse to Agents > OAuth 2.0/OpenID Connect Client Then click New in the Agent table.

    Give the OAuth 2.0 client profile the name OpenIG and password password.

    The name is the clientId value, and the password is the clientSecret value that you use in the provider configuration in OpenIG.

  4. Edit the OpenIG client profile to add the Redirection URI http://openig.example.com:8080/openid/callback.

    Add openid and profile scopes to the Scope(s) list, and then save your work.

  5. Overload the profile settings to pass credentials to OpenIG.

    This tutorial uses Full Name and Last Name for the sake of simplicity. Both of those attributes are part of a user’s profile out of the box with the default OpenAM configuration. Neither of the attributes are needed for anything else in this tutorial.

    So, this tutorial uses Last Name to hold the username, and Full Name to hold the password. In a real deployment, you would no doubt use other attributes, depending upon the user profiles and on your requirements.

    To overload the profile, create a user whose additional credentials you set in the Full Name and Last Name fields, or edit the existing user george if you have already created the profile for another tutorial:

    1. In the console for OpenAM 12 and earlier, browse to Access Control > / (Top Level Realm) > Subjects > User. In the console for OpenAM 13 and later, browse to Subjects > User for the top-level realm. Click New and create the user profile.

      If the profile already exists in the table, then click the link to open the profile for editing.

    2. Set the ID to george, the password to costanza, the Last Name to george, and the Full Name to costanza before saving your work.

    3. When finished, log out of OpenAM console by clicking the log out button. It is not enough simply to close the browser tab, as the OpenAM session remains active until you log out or quit the browser.

Configuring OpenIG As a Relying Party

To configure OpenIG as an OpenID Connect 1.0 relying party, add a new route to the OpenIG configuration, by including the following route configuration file as $HOME/.openig/config/routes/07-openid.json:

{
  "heap": [
    {
      "comment": "To reuse issuers, configure them in the parent route",
      "name": "openam",
      "type": "Issuer",
      "config": {
        "wellKnownEndpoint":
          "http://openam.example.com:8088/openam/oauth2/.well-known/openid-configuration"
      }
    },
    {
      "comment": "To reuse client registrations, configure them in the parent route",
      "name": "OidcRelyingParty",
      "type": "ClientRegistration",
      "config": {
        "clientId": "OpenIG",
        "clientSecret": "password",
        "issuer": "openam",
        "scopes": [
          "openid",
          "profile"
        ]
      }
    }
  ],
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "OAuth2ClientFilter",
          "config": {
            "clientEndpoint": "/openid",
            "requireHttps": false,
            "requireLogin": true,
            "target": "${attributes.openid}",
            "failureHandler": {
              "type": "StaticResponseHandler",
              "config": {
                "comment": "Trivial failure handler for debugging only",
                "status": 500,
                "reason": "Error",
                "entity": "${attributes.openid}"
              }
            },
            "registrations": "OidcRelyingParty"
          }
        }
      ],
      "handler": {
        "type": "Chain",
        "config": {
          "filters": [
            {
              "type": "StaticRequestFilter",
              "config": {
                "method": "POST",
                "uri": "http://app.example.com:8081",
                "form": {
                  "username": [
                    "${attributes.openid.user_info.family_name}"
                  ],
                  "password": [
                    "${attributes.openid.user_info.name}"
                  ]
                }
              }
            }
          ],
          "handler": "ClientHandler"
        }
      }
    }
  },
  "condition": "${matches(request.uri.path, '^/openid')}",
  "baseURI": "http://openig.example.com:8080"
}

On Windows, the file name should be %appdata%\OpenIG\config\routes\07-openid.json. Notice the following features of the new route:

  • The heap defines an issuer, in this case, an OpenID Provider, and a client registration with the issuer. To reuse the definitions in multiple routes, define them in the heap of the parent route.

    An issuer describes an OAuth 2.0 authorization server or OpenID Provider. A client registration holds the information provided when the OAuth 2.0 client was manually registered with the issuer. Multiple client registrations can exist with the same issuer. As an OAuth 2.0 client or OpenID Connect relying party, OpenIG uses these configurations to connect with the OAuth 2.0 authorization server or OpenID Provider. For details, see Issuer(5) in the Configuration Reference and ClientRegistration(5) in the Configuration Reference.

    If the issuer is an OpenID Provider that supports dynamic registration, it is possible to avoid explicitly configuring the client registration. For details, see the example in Using OpenID Connect Discovery and Dynamic Client Registration.

  • At the global level the route changes the base URI for requests to ensure that the initial interaction happens between OpenIG and OpenAM, which is the OpenID Provider. This route sends only the final request to the protected application.

  • The first filter in the outermost chain has the OAuth2ClientFilter type, which is described in OAuth2ClientFilter(5) in the Configuration Reference. This is the filter that enables OpenIG to act as a relying party.

    The filter is configured to work only with a single client registration, the OpenAM server you configured in Setting Up OpenAM As an OpenID Provider. If you have zero or multiple client registrations, you must use a loginHandler to manage the selection of an identity provider.

    The OAuth2ClientFilter has a base client endpoint of /openid. Incoming requests to /openid/login start the delegated authorization process. Incoming requests to /openid/callback are expected as redirects from the OP (as authorization server), so this is why you set the redirect URI in the client profile in OpenAM to http://openig.example.com:8080/openid/callback.

    The OAuth2ClientFilter has "requireHttps": false as a convenience for testing. In production environments, require HTTPS.

    The filter has "requireLogin": true to ensure you see the delegated authorization process when you make your request.

    In the OAuth2ClientFilter, the target for storing authorization state information is ${attributes.openid}, so this is where subsequent filters and handlers can find access token and user information.

    If the request fails, the errors are managed by the failureHandler, which is in this case a StaticResponseHandler. The current information in the context is dumped into a web page response to the end user. While this is helpful to you for debugging purposes, it is not helpful to an end user. In production environments, return a more user-friendly failure page.

  • After the filter injects the access token and user information into attributes.openid, OpenIG invokes a chain. The chain uses the credentials to log the user in to the minimal HTTP server.

    With this configuration, all successful requests result in login attempts against the minimal HTTP server.

  • The StaticRequestFilter retrieves the username and password from the context and replaces the original HTTP GET request with an HTTP POST login request that contains the credentials to authenticate.

  • The route matches requests to /openid.

Test the Configuration

To try your configuration, browse to OpenIG at http://openig.example.com:8080/openid.

When redirected to the OpenAM login page, login as user george, password costanza, and then allow the application access to user information.

If successful, OpenIG logs you into the minimal HTTP server as George Costanza, and the minimal HTTP server returns George’s page.

What is happening behind the scenes?

After OpenIG gets the browser request, the OAuth2ClientFilter redirects you to authenticate with OpenAM and consent to authorize access to user information. After you authorize access, OpenAM returns an access token to the filter.

The filter then uses that access token to get the user information. The filter injects the authorization state information into attributes.openid. The outermost chain then calls its handler, which as another Chain.

This inner chain uses the credentials to log the user in to the minimal HTTP server, which responds with its user information page.

Using OpenID Connect Discovery and Dynamic Client Registration

OpenID Connect defines mechanisms for discovering and dynamically registering with an identity provider that is not known in advance. These mechanisms are specified in OpenID Connect Discovery and OpenID Connect Dynamic Client Registration. OpenIG supports discovery and dynamic registration. In this section you will learn how to configure OpenIG to try these features with OpenAM.

Although this tutorial focuses on OpenID Connect dynamic registration, OpenIG also supports dynamic registration as described in RFC 7591, OAuth 2.0 Dynamic Client Registration Protocol.

Preparing to Try Discovery and Dynamic Client Registration

This short tutorial builds on the previous tutorial in this chapter. If you have not already done so, start by performing the steps described in Preparing the Tutorial. This tutorial requires a recent minimal HTTP server, as the newer versions include a small WebFinger service that is used here. When ready, complete preparations for OpenID Connect discovery and dynamic client registration:

Preparing OpenAM for OpenID Connect Dynamic Registration

By default, OpenAM does not allow dynamic registration without an access token.

After carrying out the steps described in Setting Up OpenAM As an OpenID Provider, also perform these steps:

  1. Log in to OpenAM console as administrator.

  2. In the top-level realm, browse to the Services configuration and display the OAuth2 Provider configuration.

  3. Select Allow Open Dynamic Client Registration.

  4. Save your work, and log out of OpenAM console.

Preparing OpenIG for Discovery and Dynamic Registration

Follow these steps to add a route demonstrating OpenID Connect discovery and dynamic client registration:

  1. Add a new route to the OpenIG configuration, by including the following route configuration file as $HOME/.openig/config/routes/07-discovery.json:

    {
      "heap": [
        {
          "name": "DiscoveryPage",
          "type": "StaticResponseHandler",
          "config": {
            "status": 200,
            "reason": "OK",
            "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='george or george@example.com' />
                <input type='hidden' name='goto'
                       value='${contexts.router.originalUri}' />
                </form>
                <script>
                // The sample application handles the WebFinger request,
                // so make sure the request is sent to the 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>"
          }
        }
      ],
      "handler": {
        "type": "Chain",
        "config": {
          "filters": [
            {
              "name": "DynamicallyRegisteredClient",
              "type": "OAuth2ClientFilter",
              "config": {
                "clientEndpoint": "/discovery",
                "requireHttps": false,
                "requireLogin": true,
                "target": "${attributes.openid}",
                "failureHandler": {
                  "type": "StaticResponseHandler",
                  "config": {
                    "comment": "Trivial failure handler for debugging only",
                    "status": 500,
                    "reason": "Error",
                    "entity": "${attributes.openid}"
                  }
                },
                "loginHandler": "DiscoveryPage",
                "metadata": {
                  "client_name": "My Dynamically Registered Client",
                  "redirect_uris": [
                    "http://openig.example.com:8080/discovery/callback"
                  ],
                  "scopes": [
                    "openid",
                    "profile"
                  ]
                }
              }
            }
          ],
          "handler": {
            "type": "Chain",
            "config": {
              "filters": [
                {
                  "type": "StaticRequestFilter",
                  "config": {
                    "method": "POST",
                    "uri": "http://app.example.com:8081",
                    "form": {
                      "username": [
                        "${attributes.openid.user_info.family_name}"
                      ],
                      "password": [
                        "${attributes.openid.user_info.name}"
                      ]
                    }
                  }
                }
              ],
              "handler": "ClientHandler"
            }
          }
        }
      },
      "condition": "${matches(request.uri.path, '^/discovery')}",
      "baseURI": "http://openig.example.com:8080"
    }

    On Windows, the file name should be %appdata%\OpenIG\config\routes\07-discovery.json.

  2. Consider the differences with 07-openid.json:

    • For discovery and dynamic client registration, no issuer or client registration is defined. Instead a StaticResponseHandler is used as a login handler for the client filter.

      The static response handler serves an HTML page that provides important pieces of information to OpenIG:

      • The value of a discovery parameter.

        OpenIG uses the value to perform OpenID Connect discovery. Examples from the specification include acct:joe@example.com, https://example.com:8080/, and https://example.com/joe. First, OpenIG extracts the domain host and port from the value, and attempts to find a match in the supportedDomains lists for any issuers that are already configured for the route. If it finds a match, then it can potentially use the issuer’s registration end point and avoid an additional request to look up the user’s issuer using the WebFinger protocol. If there is no match in the supported domains lists, OpenIG uses the discovery value as the resource for a WebFinger request according to the OpenID Connect Discovery protocol.

        On success, OpenIG has either found an appropriate issuer in the configuration, or found the issuer using the WebFinger protocol. OpenIG can thus proceed to dynamic client registration.

        The small JavaScript function in the HTML page transforms user input into a useful discovery value for OpenIG. This is not a requirement for deployment, only a convenience for the purposes of this example. Alternatives are described in the discovery protocol specification.

      • The value of a goto parameter.

        The goto parameter takes a URI that tells OpenIG where to redirect the end user’s browser once the process is complete and OpenIG has injected the OpenID Connect user information into the context. In this case, the user is redirected back to this route so that the innermost chain of the configuration can log the user in to the protected application.

    • The OAuth 2.0 client filter specifies a login handler, and dynamic client registration metadata, including a client name, redirection URIs, and scopes.

      The login handler points to the login page described above.

      OpenIG uses the metadata to prepare the dynamic registration request.

      As set out in OAuth2 and OpenID RFCs, the redirection URIs are mandatory for dynamic client registration, to represent an array of redirection URIs used by the client. One of the registered redirection URI values *must

    • exactly match the clientEndpoint/callback URI.

      OpenIG also needs the scopes that are required for your application.

    • 07-discovery.json uses the path /discovery, whereas 07-openid.json uses /openid.

      This distinction makes it easy to keep traffic separate on the two routes with a simple condition as in the following:

      "condition": "${matches(request.uri.path, '^/discovery')}"

Trying OpenID Connect Discovery and Dynamic Client Registration

After following the steps described in Preparing to Try Discovery and Dynamic Client Registration, test your configuration by browsing to OpenIG at http://openig.example.com:8080/discovery.

When redirected to the OpenAM login page, log in as user george, password costanza, and then allow the application access to user information.

If successful, OpenIG logs you in to the minimal HTTP server as George Costanza, and the minimal HTTP server returns George’s page.

What is happening behind the scenes?

After OpenIG gets the browser request, it returns the example page for discovery. You provide a user ID or email address, and the page transforms that into a discovery value. The value is tailored to let OpenIG use the minimal HTTP server as a WebFinger server. (In the real world the WebFinger server is more likely a service on the issuer’s domain, not part of the protected application. For the purposes of this tutorial the WebFinger service has been embedded in the minimal HTTP server to avoid leaving you with another server to manage during the tutorial.)

OpenIG learns from the WebFinger service that OpenAM is the issuer for the user. OpenIG retrieves the OpenID Provider configuration from OpenAM, and registers itself dynamically with OpenAM, using the redirection URIs and scopes specified in the OAuth 2.0 client filter configuration.

Once the issuer and client registration are properly configured, the OAuth 2.0 client filter redirects the browser to OpenAM for authentication and authorization to access to the user information. The rest is the same as the previous tutorial in this chapter. For details, see Test the Configuration.

OpenIG reuses issuer and client registration configurations that it builds after discovery and dynamic registration. These dynamically generated configuration objects are held in memory, and do not persist when OpenIG is restarted.