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, see 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.
-
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.
-
Set up AM:
-
(From AM 6.5.3) Select Services > Add a Service, and add a Validation Service with the following Valid goto URL Resources:
-
http://ig.example.com:8080/*
-
http://ig.example.com:8080/*?*
-
-
Select Applications > Agents > Identity Gateway, and add an agent with the following values:
-
Agent ID:
ig_agent
-
Password:
password
For AM 6.5.x and earlier versions, set up an agent as described in Set up an IG agent in AM 6.5 and earlier.
Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
-
-
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.
-
-
Set up IG:
-
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.
-
Add the following route to IG, to serve .css and other static resources for the sample application:
$HOME/.openig/config/routes/static-resources.json
appdata\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
Add the following route to IG:
{ "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, see Policy enforcement in Structured Editor or Protecting a web app with Freeform Designer.
-
-
Test the setup:
-
If you are logged in to AM, log out and clear any cookies.
-
Go to http://ig.example.com:8080/home/sso, and log in to AM as user
demo
, passwordCh4ng31t
.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.
-
Set up the example in Authenticate with SSO through the default authentication service.
-
Add the following route to IG:
$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 toExample
, the name of the example authentication tree in AM. This authentication tree is used for authentication instead of the AM admin UI.
-
-
Test the setup:
-
If you are logged in to AM, log out and clear any cookies.
-
Go to http://ig.example.com:8080/home/sso-authservice, and 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, see 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 app during CDSSO. In this
example, AM is running on am.example.com
, and IG is
running on ig.ext.com
.
-
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 and creates a CDSSO token.
-
AM responds to a successful authentication with an HTML autosubmit form containing the issued token.
-
The browser loads the HTML and autosubmit form parameters to the IG callback URL for the redirect endpoint.
-
IG checks the nonce found inside the CDSSO token to confirm that the callback comes from an authentication initiated by IG. IG then 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 J2EE agent. -
IG redirects the request back to the original URI, with the cookie, and the browser follows the redirect back to IG.
-
IG validates the token it gets from the cookie. It 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.
-
IG forwards the request to the sample app, and the sample app returns the requested resource to the browser.
Before you start, prepare AM, IG, and the sample application, as described in Download and start IG.
-
Set up AM:
-
Select Applications > Agents > Identity Gateway, add an agent with the following values:
-
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. For AM 6.5.x and earlier versions, set up an agent as described in Set up an IG agent in AM 6.5 and earlier.
-
-
(From AM 6.5.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/*?*
-
-
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.
-
-
Set up IG:
-
Set up IG for HTTPS, as described in Configure IG for HTTPS (server-side).
-
Add the following
session
configuration toadmin.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
isstrict
orlax
, 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
isfalse
, the browser is likely to reject the session cookie.For more information, refer to admin.json.
-
-
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.
-
Add the following route to IG, to serve .css and other static resources for the sample application:
$HOME/.openig/config/routes/static-resources.json
appdata\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
Add the following route to IG:
{ "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", "verificationSecretId": "verify", "secretsProvider": { "type": "JwkSetSecretStore", "config": { "jwkUrl": "http://am.example.com:8088/openam/oauth2/connect/jwk_uri" } } } } ], "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.
-
The property
verificationSecretId
is configured with a value. If this property is not configured, the filter does not verify the signature of signed access tokens. -
The JwkSetSecretStore specifies the URL to a JWK set on AM, that contains signing keys identified by a
kid
.The JwkSetSecretStore verifies the signature of the token when the value of a
kid
in the JWK set matches akid
in the the signed access token.If the JWT doesn’t have a
kid
, or if the JWK set doesn’t contain a key with the same value, the JwkSetSecretStore looks for valid secrets with the same purpose as the value ofverificationSecretId
.
-
-
-
Test the setup:
-
If you are logged in to AM, log out and clear any cookies.
-
Go to https://ig.ext.com:8443/home/cdsso.
If you see warnings that the site is not secure, respond to the warnings to access the site.
The CrossDomainSingleSignOnFilter redirects the request to AM for authentication.
-
Log in to AM as user
demo
, passwordCh4ng31t
.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.
For an alternative configuration using an AM policy agent instead of IG’s CapturedUserPasswordFilter, refer to the documentation for earlier versions of IG.
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.
-
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 notnull
. 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.
-
Generate an AES 256-bit key:
$ openssl rand -base64 32 loH...UFQ=
-
Set up AM:
-
(From AM 6.5.3) Select Services > Add a Service, and add a Validation Service with the following Valid goto URL Resources:
-
http://ig.example.com:8080/*
-
http://ig.example.com:8080/*?*
-
-
Select Applications > Agents > Identity Gateway, and add an agent with the following values:
-
Agent ID:
ig_agent
-
Password:
password
For AM 6.5.x and earlier versions, set up an agent as described in Set up an IG agent in AM 6.5 and earlier.
Use secure passwords in a production environment. Consider using a password manager to generate secure passwords.
-
-
Update the Authentication Post Processing Classes for password replay:
-
Select Authentication > Settings > Post Authentication Processing.
-
In Authentication Post Processing Classes, add
com.sun.identity.authentication.spi.JwtReplayPassword
.
-
-
Add the AES 256-bit key to AM:
-
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.
-
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.
-
-
-
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.
-
-
Set up IG:
-
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='
-
Add the following route to IG, to serve .css and other static resources for the sample application:
$HOME/.openig/config/routes/static-resources.json
appdata\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
Add the following route to IG:
$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.
-
-
-
Test the setup:
-
If you are logged in to AM, log out and clear any cookies.
-
Go to http://ig.example.com:8080/replay. The SingleSignOnFilter redirects the request to AM for authentication.
-
Log in to AM as user
demo
, passwordCh4ng31t
. 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 Jetty and 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:
-
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 Getting started.
-
Set up the database:
-
On your system, add the following data in a comma-separated value file:
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
-
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.
-
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.
-
-
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');
-
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;
-
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.
-
-
-
Set up IG:
-
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.
-
Add the following route to IG, to serve .css and other static resources for the sample application:
$HOME/.openig/config/routes/static-resources.json
appdata\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
Add the following route to IG:
{ "heap": [ { "name": "SystemAndEnvSecretStore-1", "type": "SystemAndEnvSecretStore" }, { "name": "JdbcDataSource-1", "type": "JdbcDataSource", "config": { "driverClassName": "org.h2.Driver", "jdbcUrl": "jdbc:h2:tcp://localhost/~/test", "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 totrue
. 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 asUSERNAME
andPASSWORD
. 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.
-
-
-
Test the setup:
-
Go to http://ig.example.com:8080/profile.
Because the property
loginPage
resolves tofalse
, the PasswordReplayFilter passes the request directly to the ReverseProxyHandler. The sample app returns the login page. -
Go to http://ig.example.com:8080/profile/george.
Because the property
loginPage
resolves totrue
, 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:
-
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 Getting started.
-
On your system, add the following data in a comma-separated value file:
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
-
Set up IG:
-
Add the following route to IG, to serve .css and other static resources for the sample application:
$HOME/.openig/config/routes/static-resources.json
appdata\OpenIG\config\routes\static-resources.json
{ "name" : "sampleapp-resources", "baseURI" : "http://app.example.com:8081", "condition": "${find(request.uri.path,'^/css')}", "handler": "ReverseProxyHandler" }
-
Add the following route to IG:
{ "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 aloginPage
page property:-
When a request is an HTTP GET, and the request URI path is
/profile/george
, the expression resolves totrue
. 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.
-
-
-
-
Test the setup:
-
Go to http://ig.example.com:8080/profile/george.
Because the property
loginPage
resolves totrue
, the PasswordReplayFilter processes the request to obtain the login credentials. The sample app returns the profile page for George. -
Go to http://ig.example.com:8080/profile/bob, or to any other URI starting with
http://ig.example.com:8080/profile
.Because the property
loginPage
resolves tofalse
, 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 Authenticating with SSO.
-
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 }
-
Enable the session cache by adding the following configuration to the AmService in your route:
"sessionCache": { "enabled": true }
-
In
logback.xml
add the following logger for WebSocket notifications, and then restart IG:<logger name="org.forgerock.openig.tools.notifications.ws" level="TRACE" />
For information, refer to Changing the log level for different object types.
-
Go to http://ig.example.com:8080/home/sso, and log in to AM as user
demo
, passwordCh4ng31t
. -
On the AM console, log the demo user out of AM to end the AM session.
-
Note that the IG system logs are updated with Websocket notifications about the logout. The following example uses AM 7.3:
... | TRACE | vert.x-eventloop-thread-2 | o.f.o.t.n.w.SubscriptionService | @system | Received a message: { "topic": "/agent/session.v2", "timestamp": "...", "body": { "sessionuid": "58c...573", "eventType": "LOGOUT" } } ... | TRACE | vert.x-eventloop-thread-2 | o.f.o.t.n.w.SubscriptionService | @system | Received a notification: { "topic": "/agent/session.v2", "timestamp": "...", "body": { "sessionuid": "58c...573", "eventType": "LOGOUT" } }