Identity Gateway 2024.3

Authentication

Single sign-on (SSO)

The following sections describe how to set up SSO for requests in the same domain:

To require users to authenticate in the correct realm for security reasons, configure SSO or CDSSO with a PolicyEnforcementFilter, that refers to an AM policy where the realm is enforced. For an example, refer to Require users to authenticate to a specific realm.

In SSO using the SingleSignOnFilter, IG processes a request using authentication provided by AM. IG and the authentication provider must run on the same domain.

The following sequence diagram shows the flow of information during SSO between IG and AM as the authentication provider.

sso
  • The browser sends an unauthenticated request to access the sample app.

  • IG intercepts the request, and redirects the browser to AM for authentication.

  • AM authenticates the user, creates an SSO token.

  • AM redirects the request back to the original URI with the token in a cookie, and the browser follows the redirect to IG.

  • IG validates the token it gets from the cookie. It then adds the AM session info to the request, and stores the SSO token in the context for use by downstream filters and handlers.

  • IG forwards the request to the sample app, and the sample app returns the requested resource to the browser.

SSO through the default AM authentication tree

This section gives an example of how to authenticate by using SSO and the default authentication service provided in AM.

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

  1. Set up AM:

    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. Register an IG agent with the following values, as described in Register an IG agent in AM:

      • Agent ID: ig_agent

      • Password: password

        Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
    3. (Optional) Authenticate the agent to AM as described in Authenticate an IG agent to AM.

      IG agents are automatically authenticated to AM by a deprecated authentication module in AM. This step is currently optional, but will be required when authentication chains and modules are removed in a future release of AM.
    4. Select Configure > Global Services > Platform, and add example.com as an AM cookie domain.

      By default, AM sets host-based cookies. After authentication with AM, requests can be redirected to AM instead of to the resource.

  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 the IG agent password, and then restart IG:

      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='

      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:

      • 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/sso.json
      %appdata%\OpenIG\config\routes\sso.json
      {
        "name": "sso",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/home/sso$')}",
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "url": "http://am.example.com:8088/openam/"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "SingleSignOnFilter-1",
                "type": "SingleSignOnFilter",
                "config": {
                  "amService": "AmService-1"
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      For information about how to set up the IG route in Studio, refer to Policy enforcement in Structured Editor or Protecting a web app with Freeform Designer.

  3. Test the setup:

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

    2. Log in to AM as user demo, password Ch4ng31t.

      The SingleSignOnFilter passes the request to sample application, which returns the sample application home page.

SSO through a specified AM authentication tree

This section gives an example of how to authenticate by using SSO and the example authentication tree provided in AM, instead of the default authentication tree.

  1. Set up the example in Authenticate with SSO through the default authentication service.

  2. Add the following route to IG:

    • Linux

    • Windows

    $HOME/.openig/config/routes/sso-authservice.json
    %appdata%\OpenIG\config\routes\sso-authservice.json
    {
      "name": "sso-authservice",
      "baseURI": "http://app.example.com:8081",
      "condition": "${find(request.uri.path, '^/home/sso-authservice')}",
      "heap": [
        {
          "name": "SystemAndEnvSecretStore-1",
          "type": "SystemAndEnvSecretStore"
        },
        {
          "name": "AmService-1",
          "type": "AmService",
          "config": {
            "agent": {
              "username": "ig_agent",
              "passwordSecretId": "agent.secret.id"
            },
            "secretsProvider": "SystemAndEnvSecretStore-1",
            "url": "http://am.example.com:8088/openam/"
          }
        }
      ],
      "handler": {
        "type": "Chain",
        "config": {
          "filters": [
            {
              "name": "SingleSignOnFilter-1",
              "type": "SingleSignOnFilter",
              "config": {
                "amService": "AmService-1",
                "authenticationService": "Example"
              }
            }
          ],
          "handler": "ReverseProxyHandler"
        }
      }
    }

    Notice the features of the route compared to sso.json:

    • The route matches requests to /home/sso-authservice.

    • The authenticationService property of SingleSignOnFilter refers to Example, the name of the example authentication tree in AM. This authentication tree is used for authentication instead of the AM admin UI.

  3. Test the setup:

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

    2. If you see warnings that the site isn’t secure, respond to the warnings to access the site.

    3. Note that the login page is different to that returned in Authenticate with SSO through the default authentication service.

Cross-domain single sign-on (CDSSO)

The following sections describe how to set up CDSSO for requests in a different domain:

To require users to authenticate in the correct realm for security reasons, configure SSO or CDSSO with a PolicyEnforcementFilter, that refers to an AM policy where the realm is enforced. For an example, refer to Require users to authenticate to a specific realm.

The SSO mechanism described in Authenticating with SSO can be used when IG and AM are running in the same domain. When IG and AM are running in different domains, AM cookies are not visible to IG because of the same-origin policy.

CDSSO using the CrossDomainSingleSignOnFilter provides a mechanism to push tokens issued by AM to IG running in a different domain.

The following sequence diagram shows the flow of information between IG, AM, and the sample application during CDSSO. In this example, AM is running on am.example.com, and IG is running on ig.ext.com.

cdsso

1. The browser sends an unauthenticated request to access the sample app.

2-3. IG intercepts the request, and redirects the browser to AM for authentication.

4. AM authenticates the user and creates a CDSSO token.

5. AM responds to a successful authentication with an HTML autosubmit form containing the issued token.

6. The browser loads the HTML and autosubmit form parameters to the IG callback URL for the redirect endpoint.

7. When verificationSecretId in CrossDomainSingleSignOnFilter is configured, IG uses it to verify signature of AM session tokens.

When verificationSecretId isn’t configured, IG discovers and uses the AM JWK set to verify the signature of AM session tokens.

If that fails, the CrossDomainSingleSignOnFilter fails to load.

8. IG checks the nonce found inside the CDSSO token to confirm that the callback comes from an authentication initiated by IG.

9. IG constructs a cookie, and fulfills it with a cookie name, path, and domain, using the CrossDomainSingleSignOnFilter property authCookie. The domain must match that set in the AM IG agent.

10-11. IG redirects the request back to the original URI, with the cookie, and the browser follows the redirect back to IG.

12. IG validates the SSO token inside of the CDSSO token

13-15. IG adds the AM session info to the request, and stores the SSO token and CDSSO token in the contexts for use by downstream filters and handlers.

16-18. IG forwards the request to the sample application, and the sample application returns the requested resource to the browser.

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

  1. Set up AM:

    1. Register an IG agent with the following values, as described in Register an IG agent in AM:

      • Agent ID: ig_agent_cdsso

      • Password: password

      • Redirect URL for CDSSO: https://ig.ext.com:8443/home/cdsso/redirect

        Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
    2. (Optional) Authenticate the agent to AM as described in Authenticate an IG agent to AM.

      IG agents are automatically authenticated to AM by a deprecated authentication module in AM. This step is currently optional, but will be required when authentication chains and modules are removed in a future release of AM.
    3. Select Services > Add a Service, and add a Validation Service with the following Valid goto URL Resources:

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

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

    4. Select Configure > Global Services > Platform, and add example.com as an AM cookie domain.

      By default, AM sets host-based cookies. After authentication with AM, requests can be redirected to AM instead of to the resource.

  2. Set up IG:

    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='

      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:

      • 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"
      }
    5. Add the following route to IG:

      • Linux

      • Windows

      $HOME/.openig/config/routes/cdsso.json
      %appdata%\OpenIG\config\routes\cdsso.json
      {
        "name": "cdsso",
        "baseURI": "http://app.example.com:8081",
        "condition": "${find(request.uri.path, '^/home/cdsso')}",
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "url": "http://am.example.com:8088/openam",
              "realm": "/",
              "agent": {
                "username": "ig_agent_cdsso",
                "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"
          }
        }
      }

      Notice the following features of the route:

      • The route matches requests to /home/cdsso.

      • The agent password for AmService is provided by a SystemAndEnvSecretStore in the heap.

      • Because the CrossDomainSingleSignOnFilter’s verificationSecretId isn’t configured, IG discovers and uses the AM JWK set to verify the signature of AM session tokens. If that fails, the CrossDomainSingleSignOnFilter fails to load.

  3. Test the setup:

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

      The CrossDomainSingleSignOnFilter redirects the request to AM for authentication.

    2. Log in to AM as user demo, password Ch4ng31t.

      When you have authenticated, AM calls /home/cdsso/redirect, and includes the CDSSO token. The CrossDomainSingleSignOnFilter passes the request to sample app, which returns the home page.

Password replay from AM

Use IG with AM’s password capture and replay to bring SSO to legacy web applications, without the need to edit, upgrade, or recode. This feature helps you to integrate legacy web applications with other applications using the same user identity.

The following figure illustrates the flow of requests when an unauthenticated user accesses a protected application. After authenticating with AM, the user is logged into the application with the username and password from the AM login session.

Data flow to log in to a protect appl[[figure-password-capture-replay-agentless]]
Figure 1. Data flow to log in to a protect appl
  • IG intercepts the browser’s HTTP GET request.

  • Because the user is not authenticated, the SingleSignOnFilter redirects the user to AM for authentication.

  • AM authenticates the user, capturing the login credentials, and storing the encrypted password in the user’s AM session.

  • AM redirects the browser back to the protected application.

  • IG intercepts the browser’s HTTP GET request again:

    • The user is now authenticated, so IG’s SingleSignOnFilter passes the request to the CapturedUserPasswordFilter.

    • The CapturedUserPasswordFilter checks that the SessionInfoContext ${contexts.amSession.properties.sunIdentityUserPassword} is available and not null. It then decrypts the password and stores it in the CapturedUserPasswordContext, at ${contexts.capturedPassword}.

  • The PasswordReplayFilter uses the username and decrypted password in the context to replace the request with an HTTP POST of the login form.

  • The sample application validates the credentials.

  • The sample application responds with the user’s profile page.

  • IG then passes the response from the sample application to the browser.

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

In Identity Cloud and from AM 7.5, the password capture and replay feature can optionally manage the replay password through AM’s secret service. The secret label for the replay password must be am.authentication.replaypassword.key.

For backward compatibility, if a secret isn’t defined, is empty, or can’t be resolved, AM manages the replay password through the AM system property am.authentication.replaypassword.key.

  1. Generate an AES 256-bit key:

    $ openssl rand -base64 32
    loH...UFQ=
  2. Set up AM:

    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. Register an IG agent with the following values, as described in Register an IG agent in AM:

      • Agent ID: ig_agent

      • Password: password

        Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
    3. (Optional) Authenticate the agent to AM as described in Authenticate an IG agent to AM.

      IG agents are automatically authenticated to AM by a deprecated authentication module in AM. This step is currently optional, but will be required when authentication chains and modules are removed in a future release of AM.
    4. Update the Authentication Post Processing Classes for password replay:

      1. Select Authentication > Settings > Post Authentication Processing.

      2. In Authentication Post Processing Classes, add com.sun.identity.authentication.spi.JwtReplayPassword.

    5. Add the AES 256-bit key to AM:

      1. Select DEPLOYMENT > Servers, and then select the AM server name, http://am.example.com:8088/openam.

        In earlier version of AM, select Configuration > Servers and Sites.

      2. Select Advanced, and add the following property:

        • PROPERTY NAME : com.sun.am.replaypasswd.key

        • PROPERTY VALUE : The value of the AES 256-bit key from step 1.

    6. Select Configure > Global Services > Platform, and add example.com as an AM cookie domain.

      By default, AM sets host-based cookies. After authentication with AM, requests can be redirected to AM instead of to the resource.

  3. Set up IG:

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

    2. Set environment variables for the value of the AES 256-bit key in step 1, and the IG agent password, and then restart IG:

      $ export AES_KEY='AES 256-bit key'
      $ export AGENT_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/04-replay.json
      %appdata%\OpenIG\config\routes\04-replay.json
      {
        "name": "04-replay",
        "condition": "${find(request.uri.path, '^/replay')}",
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore",
            "config": {
              "mappings": [
                {
                  "secretId": "aes.key",
                  "format": {
                    "type": "SecretKeyPropertyFormat",
                    "config": {
                      "format": "BASE64",
                      "algorithm": "AES"
                    }
                  }
                }
              ]
            }
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "url": "http://am.example.com:8088/openam/"
            }
          },
          {
            "name": "CapturedUserPasswordFilter",
            "type": "CapturedUserPasswordFilter",
            "config": {
              "ssoToken": "${contexts.ssoToken.value}",
              "keySecretId": "aes.key",
              "keyType": "AES",
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "amService": "AmService-1"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "type": "SingleSignOnFilter",
                "config": {
                  "amService": "AmService-1"
                }
              },
              {
                "type": "PasswordReplayFilter",
                "config": {
                  "loginPage": "${true}",
                  "credentials": "CapturedUserPasswordFilter",
                  "request": {
                    "method": "POST",
                    "uri": "http://app.example.com:8081/login",
                    "form": {
                      "username": [
                        "${contexts.ssoToken.info.uid}"
                      ],
                      "password": [
                        "${contexts.capturedPassword.value}"
                      ]
                    }
                  }
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      Notice the following features of the route:

      • The route matches requests to /replay.

      • The agent password for AmService is provided by a SystemAndEnvSecretStore in the heap.

      • If the request does not have a valid AM session cookie, the SingleSignOnFilter redirects the request to AM for authentication.

        After authentication, the SingleSignOnFilter passes the request to the next filter, storing the cookie value in an SsoTokenContext.

      • The PasswordReplayFilter uses the CapturedUserPasswordFilter declared in the heap to retrieve the AM password from AM session properties. The CapturedUserPasswordFilter uses the AES 256-bit key to decrypt the password, and then makes it available in a CapturedUserPasswordContext.

        The value of the AES 256-bit key is provided by the SystemAndEnvSecretStore.

        The PasswordReplayFilter retrieves the username and password from the context. It replaces the browser’s original HTTP GET request with an HTTP POST login request containing the credentials to authenticate to the sample application.

  4. Test the setup:

    1. In your browser’s privacy or incognito mode, go to https://ig.example.com:8443/replay. The SingleSignOnFilter redirects the request to AM for authentication.

    2. If you see warnings that the site isn’t secure, respond to the warnings to access the site.

    3. Log in to AM as user demo, password Ch4ng31t. The request is redirected to the sample application.

Password replay from a database

This section describes how to configure IG to get credentials from a database. This example is tested with H2 1.4.197.

The following figure illustrates the flow of requests when IG uses credentials from a database to log a user in to the sample application:

login-from-sql
  • IG intercepts the browser’s HTTP GET request.

  • The PasswordReplayFilter confirms that a login page is required, and passes the request to the SqlAttributesFilter.

  • The SqlAttributesFilter uses the email address to look up credentials in H2, and stores them in the request context attributes map.

  • The PasswordReplayFilter retrieves the credentials from the attributes map, builds the login form, and performs the HTTP POST request to the sample app.

  • The sample application validates the credentials, and responds with a profile page.

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

  1. Set up the database:

    1. On your system, add the following data in a comma-separated value file:

      • Linux

      • Windows

      /tmp/userfile.txt
      C:\Temp\userfile.txt
      username,password,fullname,email
      george,C0stanza,George Costanza,george@example.com
      kramer,N3wman12,Kramer,kramer@example.com
      bjensen,H1falutin,Babs Jensen,bjensen@example.com
      demo,Ch4ng31t,Demo User,demo@example.com
      kvaughan,B5ibery12,Kirsten Vaughan,kvaughan@example.com
      scarter,S9rain12,Sam Carter,scarter@example.com
    2. Download and unpack the H2 database, and then start H2:

      $ sh /path/to/h2/bin/h2.sh

      H2 starts, listening on port 8082, and opens the H2 Console in a browser.

    3. In the H2 Console, select the following options, and then select Connect to access the console:

      • Saved Settings : Generic H2 (Server)

      • Setting Name : Generic H2 (Server)

      • Driver Class: org.h2.Driver

      • JDBC URL: jdbc:h2:~/ig-credentials

      • User Name: sa

      • Password : password

        If you have run this example before but can’t access the console now, try deleting your local ~/ig-credentials files and starting H2 again.
    4. In the console, add the following text, and then run it to create the user table:

      DROP TABLE IF EXISTS USERS;
      CREATE TABLE USERS AS SELECT * FROM CSVREAD('/tmp/userfile.txt');
    5. In the console, add the following text, and then run it to verify that the table contains the same users as the file:

      SELECT * FROM users;
    6. Add the .jar file /path/to/h2/bin/h2-*.jar to the IG configuration:

      • Create the directory $HOME/.openig/extra, where $HOME/.openig is the instance directory, and add .jar files to the directory.

  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 the database password, and then restart IG:

      $ export DATABASE_PASSWORD='cGFzc3dvcmQ='

      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:

      • 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/03-sql.json
      %appdata%\OpenIG\config\routes\03-sql.json
      {
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "JdbcDataSource-1",
            "type": "JdbcDataSource",
            "config": {
              "driverClassName": "org.h2.Driver",
              "jdbcUrl": "jdbc:h2:tcp://localhost/~/ig-credentials",
              "username": "sa",
              "passwordSecretId": "database.password",
              "secretsProvider": "SystemAndEnvSecretStore-1"
            }
          }
        ],
        "name": "sql",
        "condition": "${find(request.uri.path, '^/profile')}",
        "handler": {
          "type": "Chain",
          "baseURI": "http://app.example.com:8081",
          "config": {
            "filters": [
              {
                "type": "PasswordReplayFilter",
                "config": {
                  "loginPage": "${find(request.uri.path, '^/profile/george') and (request.method == 'GET')}",
                  "credentials": {
                    "type": "SqlAttributesFilter",
                    "config": {
                      "dataSource": "JdbcDataSource-1",
                      "preparedStatement":
                      "SELECT username, password FROM users WHERE email = ?;",
                      "parameters": [
                        "george@example.com"
                      ],
                      "target": "${attributes.sql}"
                    }
                  },
                  "request": {
                    "method": "POST",
                    "uri": "http://app.example.com:8081/login",
                    "form": {
                      "username": [
                        "${attributes.sql.USERNAME}"
                      ],
                      "password": [
                        "${attributes.sql.PASSWORD}"
                      ]
                    }
                  }
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      Notice the following features of the route:

      • The route matches requests to /profile.

      • The PasswordReplayFilter specifies a loginPage page property:

      • When a request is an HTTP GET, and the request URI path is /profile/george, the expression resolves to true. The request is directed to a login page.

        The SqlAttributesFilter specifies the data source to access, a prepared statement to look up the user’s record, a parameter to pass into the statement, and where to store the search results in the request context attributes map.

        The request object retrieves the username and password from the context, and replaces the browser’s original HTTP GET request with an HTTP POST login request, containing the credentials to authenticate.

        The request is for username, password, but H2 returns the fields as USERNAME and PASSWORD. The configuration reflects this difference.

      • For other requests, the expression resolves to false. The request passes to the ReverseProxyHandler, which directs it to the profile page of the sample app.

  3. Test the setup:

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

      If you see warnings that the site isn’t secure, respond to the warnings to access the site.

      Because the property loginPage resolves to false, the PasswordReplayFilter passes the request directly to the ReverseProxyHandler. The sample app returns the login page.

    2. Go to https://ig.example.com:8443/profile/george.

      Because the property loginPage resolves to true, the PasswordReplayFilter processes the request to obtain the login credentials. The sample app returns the profile page for George.

Password replay from a file

The following figure illustrates the flow of requests when IG uses credentials in a file to log a user in to the sample application:

login-from-file
  • IG intercepts the browser’s HTTP GET request, which matches the route condition.

  • The PasswordReplayFilter confirms that a login page is required, and

  • The FileAttributesFilter uses the email address to look up the user credentials in a file, and stores the credentials in the request context attributes map.

  • The PasswordReplayFilter retrieves the credentials from the attributes map, builds the login form, and performs the HTTP POST request to the sample app.

  • The sample application validates the credentials, and responds with a profile page.

  • The ReverseProxyHandler passes the response to the browser.

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

  1. On your system, add the following data in a comma-separated value file:

    • Linux

    • Windows

    /tmp/userfile.txt
    C:\Temp\userfile.txt
    username,password,fullname,email
    george,C0stanza,George Costanza,george@example.com
    kramer,N3wman12,Kramer,kramer@example.com
    bjensen,H1falutin,Babs Jensen,bjensen@example.com
    demo,Ch4ng31t,Demo User,demo@example.com
    kvaughan,B5ibery12,Kirsten Vaughan,kvaughan@example.com
    scarter,S9rain12,Sam Carter,scarter@example.com
  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 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 route to IG:

      • Linux

      • Windows

      $HOME/.openig/config/routes/02-file.json
      %appdata%\OpenIG\config\routes\02-file.json
      {
        "name": "02-file",
        "condition": "${find(request.uri.path, '^/profile')}",
        "capture": "all",
        "handler": {
          "type": "Chain",
          "baseURI": "http://app.example.com:8081",
          "config": {
            "filters": [
              {
                "type": "PasswordReplayFilter",
                "config": {
                  "loginPage": "${find(request.uri.path, '^/profile/george') and (request.method == 'GET')}",
                  "credentials": {
                    "type": "FileAttributesFilter",
                    "config": {
                      "file": "/tmp/userfile.txt",
                      "key": "email",
                      "value": "george@example.com",
                      "target": "${attributes.credentials}"
                    }
                  },
                  "request": {
                    "method": "POST",
                    "uri": "http://app.example.com:8081/login",
                    "form": {
                      "username": [
                        "${attributes.credentials.username}"
                      ],
                      "password": [
                        "${attributes.credentials.password}"
                      ]
                    }
                  }
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      Notice the following features of the route:

      • The route matches requests to /profile.

      • The PasswordReplayFilter specifies a loginPage page property:

        • When a request is an HTTP GET, and the request URI path is /profile/george, the expression resolves to true. The request is directed to a login page.

          The FileAttributesFilter looks up the key and value in /tmp/userfile.txt, and stores them in the context.

          The request object retrieves the username and password from the context, and replaces the browser’s original HTTP GET request with an HTTP POST login request, containing the credentials to authenticate.

        • For other requests, the expression resolves to false. The request passes to the ReverseProxyHandler, which directs it to the profile page of the sample app.

  3. Test the setup:

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

    2. If you see warnings that the site isn’t secure, respond to the warnings to access the site.

      Because the property loginPage resolves to true, the PasswordReplayFilter processes the request to obtain the login credentials. The sample app returns the profile page for George.

    3. Go to https://ig.example.com:8443/profile/bob, or to any other URI starting with https://ig.example.com:8443/profile.

      Because the property loginPage resolves to false, the PasswordReplayFilter passes the request directly to the ReverseProxyHandler. The sample app returns the login page.

Session cache eviction

When WebSocket notifications are enabled in IG, IG receives notifications when the following events occur:

  • A user logs out of AM

  • An AM session is modified, closed, or times out

  • An AM admin forces logout of user sessions (from AM 7.3)

The following procedure gives an example of how to change the configurations in Single sign-on and Cross-domain single sign-on to receive WebSocket notifications for session logout, and to evict entries related to the session from the cache. For information about WebSocket notifications, refer to WebSocket notifications.

Before you start, set up and test the example in Single sign-on (SSO).

  1. Websocket notifications are enabled by default. If they are disabled, enable them by adding the following configuration to the AmService in your route:

    "notifications": {
      "enabled": true
    }
  2. Enable the session cache by adding the following configuration to the AmService in your route:

    "sessionCache": {
      "enabled": true
    }
  3. In logback.xml add the following logger for WebSocket notifications, and then restart IG:

    <logger name="org.forgerock.openig.tools.notifications.ws" level="TRACE" />
  4. On the AM console, log the demo user out of AM to end the AM session.

  5. Note that the IG system logs are updated with Websocket notifications about the logout:

    ... | TRACE | vert.x-eventloop-thread-4 | o.f.o.t.n.w.l.DirectAmLink | @system | Received a message: { "topic": ... "eventType": "LOGOUT" } }
    ... | TRACE | vert.x-eventloop-thread-4 | o.f.o.t.n.w.SubscriptionService | @system | Notification received... "eventType": "LOGOUT" }}
    ... | TRACE | vert.x-eventloop-thread-4 | o.f.o.t.n.w.SubscriptionService | @system | Notification sent to a [/agent/session.v2] listener