Authentication modules and chains
AM uses authentication modules to handle different ways of authenticating. Basically, each authentication module handles one way of obtaining and verifying credentials. You can chain different authentication modules together. In AM, this is called authentication chaining. Each authentication module can be configured to specify the continuation and failure semantics with one of the following four criteria: requisite, sufficient, required, or optional.
Authentication modules in a chain can assign a pass or fail flag to the authorization request. To successfully complete an authentication chain at least one pass flag must have been achieved, and there must be no fail flags.
Flags are assigned when completing a module as shown in the table below:
Criteria | Fail | Pass | Example |
---|---|---|---|
Requisite |
Assigns fail flag. Exits chain. |
Assigns pass flag. Continues chain. |
Active Directory, Data Store, and LDAP authentication modules are often set as requisite because of a subsequent requirement in the chain to identify the user. For example, the Device ID (Match) authentication module needs a user’s ID before it can retrieve information about the user’s devices. |
Sufficient |
Assigns no flag. Continues chain. |
Assigns pass flag. Exits chain. |
You could set Windows Desktop SSO as sufficient, so authenticated Windows users are let through, whereas web users must traverse another authentication module, such as one requiring a username and password. One exception is that if you pass a sufficient module after having failed a required module, you will continue through the chain and will not exit at that point. Consider using a requisite module instead of a required module in this situation. |
Required |
Assigns fail flag. Continues chain. |
Assigns pass flag. Continues chain. |
You could use a required module for login with email and password, so that it can fail through to another module to handle new users who have not yet signed up. |
Optional |
Assigns no flag. Continues chain. |
Assigns pass flag. Continues chain. |
You could use an optional module to assign a higher authentication level if it passes. Consider a chain with a requisite Data Store module and an optional Certificate module. Users who only passed the Data Store module could be assigned a lower authentication level than users who passed both the Data Store and Certificate modules. The users with the higher authentication level could be granted access to more highly-secured resources. |
In authentication chains with a single module, |
The AM authentication chain editor displays the flags that could be assigned by each module in the chain, and whether execution of the chain continues downwards through the chain or exits out, as shown below:
With AM, you can further set authentication levels per module, with higher levels being used typically to allow access to more restricted resources. The AM SPIs also let you develop your own authentication modules, and post-authentication plugins. Client applications can specify the authentication level, module, user, and authentication service to use among those you have configured. As described later in this guide, you can use realms to organize which authentication process applies for different applications or different domains, perhaps managed by different people.
Authentication levels for chains
When a user successfully authenticates, AM creates a session, which allows AM to manage the user’s access to resources. The session is assigned an authentication level, which is calculated to be the highest authentication level of any authentication module that passed. If the user’s session does not have the appropriate authentication level, then the user may need to reauthenticate again at a higher authentication level to access the requested resource.
The authentication level sets the level of security associated with a module. Typically, the strongest form of authentication is assigned the highest authentication level.
If an authentication chain contains requisite
or required
modules
that were not executed due to the presence of a passing sufficient
module in front of them,
the session’s authentication level is calculated to be whichever is greater:
the highest authentication level of any authentication module that passed,
or the highest authentication level of requisite
or required
modules that were not executed.
You can modify AM’s default behavior, so that a session’s authentication level
is always the highest authentication level of any authentication module that passed,
even if there are requisite
or required
modules in the authentication chain that were not executed.
To modify the default behavior,
set the org.forgerock.openam.authLevel.excludeRequiredOrRequisite
property to true
under Deployment > Servers > Server Name > Advanced and restart the AM server.
Authorization policies may also require a particular authentication level to access protected resources. When an authenticated user tries to access a protected resource without satisfying the authentication level requirement, AM denies access to the resource and returns an advice indicating that the user needs to reauthenticate at the required authentication level to access the resource.
The web or Java agent or policy enforcement point can then send the user back to AM for Session upgrade.
Configure authentication chains
The following table summarizes the high-level tasks required to configure authentication chains:
Task | Resources | ||
---|---|---|---|
Design the authentication journey of your users The best way to tackle the design decision is to write down a list of required steps users would need to take to log in to your environment, and then check the list of modules available in AM. |
|
||
Decide if you need custom authentication modules, server-side Scripts, or post-authentication plugins If the default authentication modules and plugins do not suit your needs, consider coding your own. |
|||
Configure authentication modules Before setting up your chains, configure the authentication modules you need. |
|||
Configure authentication chains Use the chain designer to put together your chains quickly. |
|||
Configure post-authentication plugins Post-authentication plugins allow you to manage session properties and run scripts after authentication, or after the user has logged out. |
Configure authentication modules
The AM admin UI provides two places where you can configure authentication modules:
-
Under Configure > Authentication, you configure default properties for global authentication modules.
-
Under Realms > Realm Name > Authentication v Modules, you configure modules for your realm.
To configure the authentication modules required by your environment, refer to the following resources:
For module reference information, see Authentication modules configuration reference.
Create an authentication chain
After you have configured authentication modules and added the modules to the list of module instances, you can configure authentication chains. Authentication chains let you handle cases where alternate modules or credentials are needed. If you need modules in the chain to share user credentials, then set options for the module.
-
In the AM admin UI, go to Realms > Realm Name > Authentication > Chains.
-
On the Authentication Chains page, click Add Chain. Enter a chain name, and click Create.
In the Edit Chain dialog, click Add a Module.
-
Choose the authentication module in the drop-down list, and then assign appropriate criteria (Optional, Required, Requisite, Sufficient).
Add as many modules as required.
-
If you need modules in the chain to share user credentials, consider the following available options:
Options to share credentials among modules
iplanet-am-auth-store-shared-state-enabled
-
Set
iplanet-am-auth-store-shared-state-enabled=true
to store the credentials captured by this module in shared state. This enables subsequent modules in the chain to access the credentials captured by this module. The shared state is cleared when the user successfully authenticates, quits the chain, or logs out.Default:
true
OATH and OTP codes are never added to the shared state, and cannot be shared between other modules in the chain. iplanet-am-auth-shared-state-enabled
-
Set
iplanet-am-auth-shared-state-enabled=true
to allow this module to access the credentials, such as user name and password, that have been stored in shared state by previous modules in the authentication chain.Default:
false
iplanet-am-auth-shared-state-behavior-pattern
-
Set
iplanet-am-auth-shared-state-behavior-pattern=tryFirstPass
to try authenticating with the username and password stored in shared state. If authentication fails, AM displays the login screen of this module for the user to re-enter their credentials.Set
iplanet-am-auth-shared-state-behavior-pattern=useFirstPass
to prevent the user from entering the username and password twice during authentication. Typically, you set the property touseFirstPass
for all modules in the chain except the first module. If authentication fails, then the module fails.Default:
tryFirstPass
Enter the key and its value, and click Plus (+). When you finish entering the options, click OK.
Examples
For example, consider a chain with two modules sharing credentials according to the following settings: the first module in the chain has the option
iplanet-am-auth-store-shared-state-enabled=true
, and criteriaREQUIRED
.Figure 2. Authentication chain first moduleThe second module in the chain has options
iplanet-am-auth-shared-state-enabled=true
,iplanet-am-auth-shared-state-behavior-pattern=useFirstPass
with criteriaREQUIRED
.Figure 3. Authentication chain second module -
On the Settings tab, configure where AM redirects the user upon successful and failed authentication, and plug in your post-authentication processing classes as necessary.
If you configure absolute URLs that are not in the same scheme, FQDN, and port as AM, you must also configure the Success and failure redirection URLs.
-
Save your work.
The following authentication sequence would occur: the user enters their credentials for the first module and successfully authenticates. The first module shares the credentials with the second module, successfully authenticating the user without prompting again for their credentials, unless the credentials for the first module do not successfully authenticate the user to the second module.
Login session timeouts for chains
Login pages have a session timeout that specifies the number of minutes before the session times out, if the user has not logged in. The login session timeout has two components:
-
The timeout of the specific authentication module.
The default session timeout for an authentication module is two minutes.
-
The overall session timeout, set in Configure > Server Defaults > Session > Session Limits > Invalidate Session Max Time.
The default overall session timeout is three minutes.
You must set the overall session timeout to a value greater than the complete authentication process (including any multi-page authentication processes). If you have chained authentication modules, with different timeouts, you must set the overall session timeout to a value greater than the sum of these timeouts.
Learn more in How do I configure login page session timeouts in PingAM when using authentication modules? in the Knowledge Base.
Post-authentication plugins
Post-authentication plugins (PAP) let you include custom processing at the end of the authentication process and when users log out of AM.
In the AM admin UI, you add post-authentication plugins to an authentication chain. Go to Realms > Realm Name > Authentication > Chains > Auth Chain Name > Settings > Post Authentication Processing Class > Class Name.
See Create post-authentication plugins for chains for more information about post-authentication plugins.
AM provides some post-authentication plugins as part of the standard product delivery.
- Class name:
org.forgerock.openam.authentication.modules.adaptive.AdaptivePostAuthenticationPlugin
-
The adaptive authentication plugin serves to save cookies and profile attributes after successful authentication.
Add it to your authentication chains that use the adaptive authentication module configured to save cookies and profile attributes.
- Class name:
org.forgerock.openam.authentication.modules.common.JaspiAuthLoginModulePostAuthenticationPlugin
-
The Java Authentication Service Provider Interface (JASPI) post-authentication plugin initializes the underlying JASPI
ServerAuth
module.JASPI defines a standard service provider interface (SPI) where developers can write message level authentication agents for Java containers on either the client side or the server side.
- Class name:
org.forgerock.openam.authentication.modules.oauth2.OAuth2PostAuthnPlugin
-
The OAuth 2.0 post-authentication plugin builds a global logout URL used by
/oauth2c/OAuthLogout.jsp
after successful OAuth 2.0 client authentication. This logs the resource owner out with the OAuth 2.0 provider when logging out of AM.Before using this plugin, configure the OAuth 2.0 authentication module with the correct OAuth 2.0 Provider logout service URL, and set the Logout options to Log out or Prompt. This plugin cannot succeed unless those parameters are correctly set.
Sometimes OAuth 2.0 providers change their endpoints, including their logout URLs. When using a provider like Facebook, Google, or MSN, make sure you are aware when they change their endpoint locations so that you can change your client configuration accordingly.
- Class name:
org.forgerock.openam.authentication.modules.saml2.SAML2PostAuthenticationPlugin
-
The SAML v2.0 post-authentication plugin that gets activated for single logout. Supports HTTP-Redirect for logout-sending messages only.
Set the post-authentication processing class for the authentication chain that contains the SAML v2.0 authentication module.
- Class name:
org.forgerock.openam.authentication.modules.persistentcookie.PersistentCookieAuthModule
-
The Persistent Cookie Authentication Module provides logic for persistent cookie authentication in AM. It makes use of the JASPI
JwtSession
module to create and verify the persistent cookie. - Class name:
com.sun.identity.authentication.spi.ReplayPasswd
-
Password replay post-authentication plugin class that uses a DES/ECB/NoPadding encryption algorithm. This class is deprecated in favor of the
com.sun.identity.authentication.spi.JwtReplayPassword
class. (Only one password replay post-authentication plugin class can be active for a given AM deployment.)The plugin encrypts the password captured by AM during the authentication process and stores it in a session property. PingGateway or a web agent looks up the property, decrypts it, and replays the password into legacy applications.
To configure password replay for AM and PingGateway, refer to Password replay from AM in the PingGateway documentation.
- Class name:
com.sun.identity.authentication.spi.JwtReplayPassword
-
Password replay post-authentication plugin class that uses a JWT-based AES A128CBC-HS256 encryption algorithm. (Only one password replay post-authentication plugin class can be active for a given AM deployment.)
The plugin encrypts the password captured by AM during the authentication process and stores it in a session property. PingGateway looks up the property, decrypts it, and replays the password into legacy applications.
Only version 6 or later is supported.
To configure password replay for AM and PingGateway, refer to Password replay from AM in the PingGateway documentation.
If necessary, you can also write your own custom post-authentication plugin as described in Create post-authentication plugins for chains.
Customize authentication chains
Your deployment might require customizing standard authentication chain features.
Create a custom authentication module
This section shows how to customize authentication with a sample custom authentication module. For deployments with particular requirements not met by existing AM authentication modules, determine whether you can adapt one of the built-in or extension modules for your needs. If not, build the functionality into a custom authentication module.
The sample custom authentication module
The sample custom authentication module prompts for a user name and password to authenticate the user, and handles error conditions. The sample shows how you integrate an authentication module into AM such that you can configure the module through the AM admin UI, and also localize the user interface.
Learn about downloading and building PingAM sample source code in the following Knowledge Base article: How do I access and build the sample code provided for PingAM?.
Get a local clone so that you can try the sample on your system.
In the sources, you find the following files
under the /path/to/openam-samples-external/custom-authentication-module
directory:
pom.xml
-
Apache Maven project file for the module
This file specifies how to build the sample authentication module, and also specifies its dependencies on AM components and on the Java Servlet API.
src/main/java/org/forgerock/openam/examples/SampleAuth.java
-
Core class for the sample authentication module
This class is called by AM to initialize the module and to process authentication. See Sample authentication logic for details.
src/main/java/org/forgerock/openam/examples/SampleAuthPrincipal.java
-
Class implementing
java.security.Principal
interface that defines how to map credentials to identitiesThis class is used to process authentication. See The Sample Auth Principal for details.
src/main/resources/amAuthSampleAuth.properties
-
Properties file mapping UI strings to property values
This file makes it easier to localize the UI. See Sample Auth properties for details.
src/main/resources/amAuthSampleAuth.xml
-
Configuration file for the sample authentication service
This file is used when registering the authentication module with AM. See The Sample Auth Service Configuration for details.
src/main/resources/config/auth/default/SampleAuth.xml
-
Callback file for deprecated AM classic UI authentication pages
The sample authentication module does not include localized versions of this file. See Sample Auth callbacks for details.
src/main/java/org/forgerock/openam/examples/SampleAuthPlugin.java
-
These files serve to register the plugin with AM.
The Java class,
SampleAuthPlugin
, implements theorg.forgerock.openam.plugins.AmPlugin
interface. In the sample, this class registers theSampleAuth
implementation, and theamAuthSampleAuth
service schema for configuration.The services file,
org.forgerock.openam.plugins.AmPlugin
, holds the fully qualified class name of theAmPlugin
that registers the custom implementations. In this case,org.forgerock.openam.examples.SampleAuthPlugin
.For an explanation of service loading, see the ServiceLoader Javadoc.
Sample Auth properties
AM uses a Java properties file per locale to retrieve the appropriate, localized strings for the authentication module.
The following is the Sample Authentication Module properties file, amAuthSampleAuth.properties
.
#
# The contents of this file are subject to the terms of the Common Development and
# Distribution License (the License). You may not use this file except in compliance with the
# License.
#
# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
# specific language governing permission and limitations under the License.
#
# When distributing Covered Software, include this CDDL Header Notice in each file and include
# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
# Header, with the fields enclosed by brackets [] replaced by your own identifying
# information: "Portions copyright [year] [name of copyright owner]".
#
# Copyright 2011-2017 ForgeRock AS. All Rights Reserved
#
sampleauth-service-description=Sample Authentication Module
a500=Authentication Level
a501=Service Specific Attribute
sampleauth-ui-login-header=Login
sampleauth-ui-username-prompt=User Name:
sampleauth-ui-password-prompt=Password:
sampleauth-error-1=Error 1 occurred during the authentication
sampleauth-error-2=Error 2 occurred during the authentication
Sample Auth callbacks
AM callbacks XML files are used to build the deprecated classic UI
to prompt the user for identity information needed to process the authentication.
The document type for a callback XML file
is described in WEB-INF/Auth_Module_Properties.dtd
where AM is deployed.
The value of the moduleName
property in the callbacks file must match your custom authentication module’s class name.
Observe that the module name SampleAuth
, shown in the example below,
matches the class name in SampleAuth.java
.
<?xml version="1.0" encoding="UTF-8"?>
<!--
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2011-2017 ForgeRock AS. All Rights Reserved
*
-->
<!DOCTYPE ModuleProperties PUBLIC
"=//iPlanet//Authentication Module Properties XML Interface 1.0 DTD//EN"
"jar://com/sun/identity/authentication/Auth_Module_Properties.dtd">
<ModuleProperties moduleName="SampleAuth" version="1.0" >
<Callbacks length="0" order="1" timeout="600" header="#NOT SHOWN#" />
<Callbacks length="2" order="2" timeout="600" header="#TO BE SUBSTITUTED#">
<NameCallback isRequired="true">
<Prompt>#USERNAME#</Prompt>
</NameCallback>
<PasswordCallback echoPassword="false" >
<Prompt>#PASSWORD#</Prompt>
</PasswordCallback>
</Callbacks>
<Callbacks length="1" order="3" timeout="600" header="#TO BE SUBSTITUTED#"
error="true" >
<NameCallback>
<Prompt>#THE DUMMY WILL NEVER BE SHOWN#</Prompt>
</NameCallback>
</Callbacks>
</ModuleProperties>
This file specifies three states.
-
The initial state (order="1") is used dynamically to replace the dummy strings shown between hashes (for example,
#USERNAME#
) by thesubstituteUIStrings()
method inSampleAuth.java
. -
The next state (order="2") handles prompting the user for authentication information.
-
The last state (order="3") has the attribute
error="true"
. If the authentication module state machine reaches this order then the authentication has failed. TheNameCallback
is not used and not displayed to user. AM requires that the callbacks array have at least one element. Otherwise AM does not permit header substitution.
Sample authentication logic
An AM authentication module must extend the com.sun.identity.authentication.spi.AMLoginModule
abstract class,
and must implement the methods shown below.
The account lockout functionality in AM is triggered by counting invalid password exceptions, rather than invalid login exceptions. To trigger account lockouts after repeated failed attempts,
ensure your modules throw |
See the PingAM Java SDK API Specification for reference.
public void init(Subject subject, Map sharedState, Map options)
// OpenAM calls the process() method when the user submits authentication
// information. The process() method determines what happens next:
// success, failure, or the next state specified by the order
// attribute in the callbacks XML file.
public int process(Callback[] callbacks, int state) throws LoginException
// OpenAM expects the getPrincipal() method to return an implementation of
// the java.security.Principal interface.
public Principal getPrincipal()
AM does not reuse authentication module instances. This means that you can store information specific to the authentication process in the instance.
The implementation, SampleAuth.java
, is shown below:
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2011-2023 ForgeRock AS. All Rights Reserved
*/
package org.forgerock.openam.examples;
import java.security.Principal;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.identity.authentication.spi.AMLoginModule;
import com.sun.identity.authentication.spi.AuthLoginException;
import com.sun.identity.authentication.spi.InvalidPasswordException;
import com.sun.identity.authentication.util.ISAuthConstants;
import com.sun.identity.shared.datastruct.CollectionHelper;
/**
* SampleAuth authentication module example.
*
* If you create your own module based on this example, you must modify all
* occurrences of "SampleAuth" in addition to changing the name of the class.
*
* Please refer to OpenAM documentation for further information.
*
* Feel free to look at the code for authentication modules delivered with
* OpenAM, as they implement this same API.
*/
public class SampleAuth extends AMLoginModule {
// Name for the debug-log
private final static String DEBUG_NAME = "SampleAuth";
private final static Logger debug = LoggerFactory.getLogger(SampleAuth.class);
// Name of the resource bundle
private final static String amAuthSampleAuth = "amAuthSampleAuth";
// User names for authentication logic
private final static String USERNAME = "demo";
private final static String PASSWORD = "Ch4ng31t";
private final static String ERROR_1_USERNAME = "test1";
private final static String ERROR_2_USERNAME = "test2";
// Orders defined in the callbacks file
private final static int STATE_BEGIN = 1;
private final static int STATE_AUTH = 2;
private final static int STATE_ERROR = 3;
// Errors properties
private final static String SAMPLE_AUTH_ERROR_1 = "sampleauth-error-1";
private final static String SAMPLE_AUTH_ERROR_2 = "sampleauth-error-2";
private Map<String, Set<String>> options;
private ResourceBundle bundle;
private Map<String, String> sharedState;
public SampleAuth() {
super();
}
/**
* This method stores service attributes and localized properties for later
* use.
* @param subject
* @param sharedState
* @param options
*/
@Override
public void init(Subject subject, Map sharedState, Map options) {
debug.debug("SampleAuth::init");
this.options = options;
this.sharedState = sharedState;
this.bundle = amCache.getResBundle(amAuthSampleAuth, getLoginLocale());
}
@Override
public int process(Callback[] callbacks, int state) throws LoginException {
debug.debug("SampleAuth::process state: {}", state);
switch (state) {
case STATE_BEGIN:
// Intialize Callback list if used in chain with
// iplanet-am-auth-shared-state-enabled=true
setForceCallbacksRead(true);
forceCallbacksInit();
// No time wasted here - simply modify the UI and
// proceed to next state
substituteUIStrings();
return STATE_AUTH;
case STATE_AUTH:
// Get data from callbacks. Refer to callbacks XML file.
NameCallback nc = (NameCallback) callbacks[0];
PasswordCallback pc = (PasswordCallback) callbacks[1];
String username = nc.getName();
String password = String.valueOf(pc.getPassword());
//First errorstring is stored in "sampleauth-error-1" property.
if (ERROR_1_USERNAME.equals(username)) {
setErrorText(SAMPLE_AUTH_ERROR_1);
return STATE_ERROR;
}
//Second errorstring is stored in "sampleauth-error-2" property.
if (ERROR_2_USERNAME.equals(username)) {
setErrorText(SAMPLE_AUTH_ERROR_2);
return STATE_ERROR;
}
if (USERNAME.equals(username) && PASSWORD.equals(password)) {
debug.debug("SampleAuth::process User '{}' " +
"authenticated with success.", username);
return ISAuthConstants.LOGIN_SUCCEED;
}
throw new InvalidPasswordException("password is wrong",
USERNAME, isReturningPrincipalAsDn());
case STATE_ERROR:
return STATE_ERROR;
default:
throw new AuthLoginException("invalid state");
}
}
@Override
public Principal getPrincipal() {
return new SampleAuthPrincipal(USERNAME);
}
private void setErrorText(String err) throws AuthLoginException {
// Receive correct string from properties and substitute the
// header in callbacks order 3.
substituteHeader(STATE_ERROR, bundle.getString(err));
}
private void substituteUIStrings() throws AuthLoginException {
// Get service specific attribute configured in OpenAM
String ssa = CollectionHelper.getMapAttr(options, "specificAttribute");
// Get property from bundle
String new_hdr = ssa + " " +
bundle.getString("sampleauth-ui-login-header");
substituteHeader(STATE_AUTH, new_hdr);
replaceCallback(STATE_AUTH, 0, new NameCallback(
bundle.getString("sampleauth-ui-username-prompt")));
replaceCallback(STATE_AUTH, 1, new PasswordCallback(
bundle.getString("sampleauth-ui-password-prompt"), false));
}
}
The Sample Auth Principal
The implementation, SampleAuthPrincipal.java
, is shown below:
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2011-2017 ForgeRock AS. All Rights Reserved
*/
package org.forgerock.openam.examples;
import java.io.Serializable;
import java.security.Principal;
/**
* SampleAuthPrincipal represents the user entity.
*/
public class SampleAuthPrincipal implements Principal, Serializable {
private final static String COLON = " : ";
private final String name;
public SampleAuthPrincipal(String name) {
if (name == null) {
throw new NullPointerException("illegal null input");
}
this.name = name;
}
/**
* Return the LDAP username for this <code>SampleAuthPrincipal</code>.
*
* @return the LDAP username for this <code>SampleAuthPrincipal</code>
*/
@Override
public String getName() {
return name;
}
/**
* Return a string representation of this <code>SampleAuthPrincipal</code>.
*
* @return a string representation of this
* <code>TestAuthModulePrincipal</code>.
*/
@Override
public String toString() {
return new StringBuilder().append(this.getClass().getName())
.append(COLON).append(name).toString();
}
/**
* Compares the specified Object with this <code>SampleAuthPrincipal</code>
* for equality. Returns true if the given object is also a
* <code> SampleAuthPrincipal </code> and the two SampleAuthPrincipal have
* the same username.
*
* @param o Object to be compared for equality with this
* <code>SampleAuthPrincipal</code>.
* @return true if the specified Object is equal equal to this
* <code>SampleAuthPrincipal</code>.
*/
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (!(o instanceof SampleAuthPrincipal)) {
return false;
}
SampleAuthPrincipal that = (SampleAuthPrincipal) o;
if (this.getName().equals(that.getName())) {
return true;
}
return false;
}
/**
* Return a hash code for this <code>SampleAuthPrincipal</code>.
*
* @return a hash code for this <code>SampleAuthPrincipal</code>.
*/
@Override
public int hashCode() {
return name.hashCode();
}
}
The Sample Auth Service Configuration
AM requires that all authentication modules be configured by means of an AM service.
At minimum, the service must include an authentication level attribute.
Your module can access these configuration attributes in the options
parameter passed to the init()
method.
Some observations about the service configuration file follow in the list below.
-
The document type for a service configuration file is described in
WEB-INF/sms.dtd
where AM is deployed. -
The service name is derived from the module name. The service name must have the following format:
-
It must start with either
iPlanetAMAuth
orsunAMAuth
. -
The module name must follow. The case of the module name must match the case of the class that implements the custom authentication module.
-
It must end with
Service
.
In the Sample Auth service configuration, the module name is
SampleAuth
and the service name isiPlanetAMAuthSampleAuthService
. -
-
The service must have a localized description, retrieved from a properties file.
-
The
i18nFileName
attribute in the service configuration holds the default (non-localized) base name of the Java properties file. Thei18nKey
attributes indicate properties keys to string values in the Java properties file. -
The authentication level attribute name must have the following format:
-
It must start with
iplanet-am-auth-
,sun-am-auth-
, orforgerock-am-auth-
. -
The module name must follow, and must appear in lower case if the attribute name starts with
iplanet-am-auth-
orforgerock-am-auth-
. If the attribute name starts withsun-am-auth-
, it must exactly match the case of the module name as it appears in the service name. -
It must end with
-auth-level
.
In the Sample Auth service configuration, the authentication level attribute name is
iplanet-am-auth-sampleauth-auth-level
. -
-
The Sample Auth service configuration includes an example
sampleauth-service-specific-attribute
, which can be configured through the AM admin UI.
The service configuration file, amAuthSampleAuth.xml
, is shown below.
Save a local copy of this file, which you use when registering the module.
<?xml version="1.0" encoding="UTF-8"?>
<!--
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2011-2020 ForgeRock AS. All Rights Reserved
*
-->
<!DOCTYPE ServicesConfiguration
PUBLIC "=//iPlanet//Service Management Services (SMS) 1.0 DTD//EN"
"jar://com/sun/identity/sm/sms.dtd">
<ServicesConfiguration>
<Service name="iPlanetAMAuthSampleAuthService" version="1.0">
<Schema
serviceHierarchy="/DSAMEConfig/authentication/iPlanetAMAuthSampleAuthService"
i18nFileName="amAuthSampleAuth" revisionNumber="10"
i18nKey="sampleauth-service-description" resourceName="sample">
<Organization>
<!-- Specify resourceName for a JSON-friendly property in the REST SMS -->
<AttributeSchema name="iplanet-am-auth-sampleauth-auth-level" resourceName="authLevel"
type="single" syntax="number_range" rangeStart="0" rangeEnd="2147483647"
i18nKey="a500">
<DefaultValues>
<Value>1</Value>
</DefaultValues>
</AttributeSchema>
<!-- No need for resourceName when the name is JSON-compatible -->
<AttributeSchema name="specificAttribute"
type="single" syntax="string" validator="no" i18nKey="a501" />
<!--
For Auth Modules, the parent Schema element specifies the REST SMS resourceName,
and the nested SubSchema must have resourceName="USE-PARENT"
-->
<SubSchema name="serverconfig" inheritance="multiple" resourceName="USE-PARENT">
<AttributeSchema name="iplanet-am-auth-sampleauth-auth-level" resourceName="authLevel"
type="single" syntax="number_range" rangeStart="0" rangeEnd="2147483647"
i18nKey="a500">
<DefaultValues>
<Value>1</Value>
</DefaultValues>
</AttributeSchema>
<!-- No need for a DefaultValues element when the default is blank -->
<AttributeSchema name="specificAttribute"
type="single" syntax="string" validator="no" i18nKey="a501" />
</SubSchema>
</Organization>
</Schema>
</Service>
</ServicesConfiguration>
Building and Installing the Sample Custom Auth Module
Build the module with Apache Maven, and install the module in AM.
Learn about downloading and building PingAM sample source code in the following Knowledge Base article: How do I access and build the sample code provided for PingAM?.
Installing the Module
Installing the sample authentication module consists of copying the .jar
file
to AM’s WEB-INF/lib/
directory, registering the module with AM,
and then restarting AM or the web application container where it runs.
-
Copy the sample authentication module
.jar
file toWEB-INF/lib/
where AM is deployed.$ cp target/custom*.jar /path/to/tomcat/webapps/openam/WEB-INF/lib/
-
Restart AM or the container in which it runs.
For example if you deployed AM in Apache Tomcat, then you shut down Tomcat and start it again.
$ /path/to/tomcat/bin/shutdown.sh $ /path/to/tomcat/bin/startup.sh $ tail -1 /path/to/tomcat/logs/catalina.out INFO: Server startup in 14736 ms
Configure and test the Sample Auth module
Authentication modules are registered as services with AM globally, and then set up for use in a particular realm. In this example, you set up the sample authentication module for use in the realm / (Top Level Realm).
In the AM admin UI, go to Realms > Top Level Realm > Authentication > Modules.
Click Add Module to create an instance of the Sample Authentication Module.
Name the module Sample
.
Click Create, and then configure the authentication module as appropriate.
Now that the module is configured, log out of the AM admin UI.
Finally, try the module by specifying the Sample
module.
Browse to the login URL such as https://openam.example.com:8443/openam/XUI/?realm=/&module=Sample#login
,
and then authenticate with user name demo
and password Ch4ng31t
.
After authentication you are redirected to the end user page for the demo user.
You can logout of the AM admin UI,
and then try to authenticate as the non-existent user test123
to see what the error handling looks like to the user.
Server-side authentication scripts in authentication modules
This section demonstrates how to use the default server-side authentication script. An authentication script can be called from a Scripted authentication module.
The default server-side authentication script only authenticates a subject
when the current time on the AM server is between 09:00 and 17:00.
The script also uses the logger
and httpClient
functionality provided in the scripting API.
To examine the contents of the default server-side authentication script in the AM admin UI, go to Realms > Top Level Realm > Scripts, and click Scripted Module - Server Side.
For general information about scripting in AM, see Scripting.
Prepare AM to use server-side authentication scripts
AM requires a small amount of configuration before trying the example server-side authentication script.
You must create an authentication module of the Scripted type, and then include it in an authentication chain,
which can then be used when logging in to AM.
You must also ensure the demo
user has an associated postal address.
Create a scripted authentication module that uses the default server-side authentication acript
In this procedure, create a Scripted Authentication module, and link it to the default server-side authentication script.
-
Log in as an AM administrator, for example
amAdmin
. -
Go to Realms > Top Level Realm > Authentication > Modules.
-
On the Authentication Modules page, click Add Module.
-
On the New Module page, enter a module name, such as
myScriptedAuthModule
, from the Type drop-down list, selectScripted Module
, and click Create. -
On the module configuration page:
-
Uncheck the Client-side Script Enabled checkbox.
-
From the Server-side Script drop-down list, select
Scripted Module - Server Side
. -
Click Save Changes.
-
Create an authentication chain that uses a Scripted Authentication module
In this procedure, create an authentication chain that uses a Data Store authentication module and the Scripted authentication module created in the previous procedure.
-
Log in as an AM administrator, for example
amAdmin
. -
Go to Realms > Top Level Realm > Authentication > Chains.
-
On the Authentication Chains page, click Add Chain.
-
On the Add Chain page, enter a name, such as
myScriptedChain
, and click Create. -
On the Edit Chain tab, click Add a Module.
-
In the New Module dialog box:
-
From the Select Module drop-down list, select
DataStore
. -
From the Select Criteria drop-down list, select
Required
. -
Click OK.
The Data Store authentication module checks the user credentials, whereas the Scripted authentication module does not check credentials, but instead only checks that the authentication request is processed during working hours. Without the Data Store module, the username in the Scripted authentication module cannot be determined. Therefore, do not configure the Scripted authentication module (server-side script) as the first module in an authentication chain, because it needs a username.
-
-
On the Edit Chain tab, click Add Module.
-
In the New Module dialog box:
-
From the Select Module drop-down list, select the Scripted Module from the previous procedure, for example
myScriptedAuthModule
. -
From the Select Criteria drop-down list, select
Required
. -
Click OK.
The resulting chain resembles the following:
-
-
On the Edit Chain tab, click Save Changes.
Add a postal address to the demo user
-
Log in as an AM administrator, for example
amAdmin
. -
Go to Realms > Top Level Realm > Identities.
-
On the Identities tab, click the
demo
user. -
In the Home Address field, enter a valid postal address, with lines separated by commas.
For example:
ForgeRock Inc., 201 Mission St #2900, San Francisco, CA 94105, USA
-
Save your changes.
Trying the default server-side authentication script
This section shows how to log in using an authentication chain that contains a Scripted authentication module, which in turn uses the default server-side authentication script.
The default server-side authentication script gets the postal address of a user after they authenticate using a Data Store authentication module, and then makes an HTTP call to an external web service to determine the longitude and latitude of the address. Using these details, a second HTTP call is performed to get the local time at those coordinates. If that time is between two preset limits, authentication is allowed, and the user is given a session and redirected to the profile page.
Log in using a chain containing a Scripted Authentication module
-
Log out of AM.
-
In a browser, go to the AM login URL, and specify the authentication chain created in the previous procedure as the value of the
service
parameter.For example:
https://openam.example.com:8443/openam/XUI/?service=myScriptedChain#login
-
Log in as user
demo
with passwordCh4ng31t
.If login is successful, the user profile page is displayed. The script will also output messages, such as the following in the
debug/Authentication
log file:Starting scripted authentication amScript:02/27/2017 03:22:42:881 PM GMT: Thread[ScriptEvaluator-5,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] User: demo amScript:02/27/2017 03:22:42:882 PM GMT: Thread[ScriptEvaluator-5,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] User address: ForgeRock Inc., 201 Mission St #2900, San Francisco, CA 94105, USA amScript:02/27/2017 03:22:42:929 PM GMT: Thread[ScriptEvaluator-5,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] User REST Call. Status: [Status: 200 OK] amScript:02/27/2017 03:27:31:646 PM GMT: Thread[ScriptEvaluator-7,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] latitude:37.7914374 longitude:-122.3950694 amScript:02/27/2017 03:27:31:676 PM GMT: Thread[ScriptEvaluator-7,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] User REST Call. Status: [Status: 200 OK] amScript:02/27/2017 03:27:31:676 PM GMT: Thread[ScriptEvaluator-7,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] Current time at the users location: 10 amScript:02/27/2017 03:27:31:676 PM GMT: Thread[ScriptEvaluator-7,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] Authentication allowed! amLoginModule:02/27/2017 03:27:31:676 PM GMT: Thread[http-nio-8080-exec-4,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] Login NEXT State : -1 amLoginModule:02/27/2017 03:27:31:676 PM GMT: Thread[http-nio-8080-exec-4,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] SETTING Module name.... :myScriptedAuthModule amAuth:02/27/2017 03:27:31:676 PM GMT: Thread[http-nio-8080-exec-4,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] Module name is .. myScriptedAuthModule amAuth:02/27/2017 03:27:31:676 PM GMT: Thread[http-nio-8080-exec-4,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] successModuleSet is : [DataStore, myScriptedAuthModule] amJAAS:02/27/2017 03:27:31:676 PM GMT: Thread[http-nio-8080-exec-4,5,main]: TransactionId[7635cd7c-ea97-4be6-8694-9e2be8642d56-8581] login success
The default server-side authentication script outputs log messages at the
message
anderror
level.AM does not log debug messages from scripts by default. You can configure AM to log such messages by setting the debug log level for the
amScript
service. For details, see Debug logging. -
To test that the script is being used as part of the login process, edit the script to alter the times when authentication is allowed:
-
Log out the
demo
user. -
Log in as an AM administrator, for example
amAdmin
. -
Go to Realms > Top Level Realm > Scripts > Scripted Module - Server Side.
-
In the script, swap the values for
START_TIME
andEND_TIME
, for example:var START_TIME = 17; var END_TIME = 9;
-
Click Save.
-
Repeat steps 1, 2, and 3 above, logging into the module as the
demo
user as before. The authentication result will be the opposite of the previous result, as the allowed times have inverted.
-
Create post-authentication plugins for chains
Post-authentication plugins (PAP) let you include custom processing at the following places in the authentication cycle:
-
At the end of the authentication process, immediately before a user is authenticated.
-
When a user logs out of an AM session.
A common use of post-authentication plugins is to set state information in the session object in conjunction with web or Java agents. The post-authentication plugin sets custom session properties, and then the web or Java agent injects the custom properties into the header sent to the protected application.
Two issues should be considered when writing a post-authentication plugin for an AM deployment that uses client-side sessions:
- Cookie size
-
You can set an unlimited number of session properties in a post-authentication plugin. When AM creates a client-side session, it writes the session properties into the session cookie, increasing the size of the cookie. Very large session cookies can exceed browser limitations. Therefore, when implementing a post-authentication plugin in a deployment with client-side sessions, be sure to monitor the session cookie size and verify that you have not exceeded browser cookie size limits.
For more information about client-side session cookies, see Session cookies and session security.
- Cookie security
-
The AM administrator secures custom session properties in sessions residing in the CTS token store by using firewalls and other typical security techniques.
However, when using client-side sessions, custom session properties are written in cookies and reside on end users' systems. Cookies can be long-lasting and might represent a security issue if any session properties are of a sensitive nature. When developing a post-authentication plugin for a deployment that uses client-side sessions, be sure that you are aware of the measures securing the session contained within the cookie.
For more information about client-side session cookie security, see Configure client-side session security.
Design your post-authentication plugin
Your post-authentication plugin class implements the AMPostAuthProcessInterface
interface,
and in particular the following three methods.
public void onLoginSuccess(
Map requestParamsMap,
HttpServletRequest request,
HttpServletResponse response,
SSOToken token
) throws AuthenticationException
public void onLoginFailure(
Map requestParamsMap,
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException
public void onLogout(
HttpServletRequest request,
HttpServletResponse response,
SSOToken token
) throws AuthenticationException
AM calls the onLoginSuccess()
and onLoginFailure()
methods
immediately before informing the user of login success or failure, respectively.
AM calls the onLogout()
method only when the user actively logs out, not when a user’s session times out.
See the PingAM Java SDK API Specification for reference.
These methods can perform whatever processing you require. Yet, know that AM calls your methods synchronously as part of the authentication process. Therefore, if your methods take a long time to complete, you will keep users waiting. Minimize the processing done in your post-authentication methods.
Implementing a post-authentication plugin in the top level realm can have unexpected effects. At the top level realm, AM invokes the post-authentication plugin for all types of authentication during startup, including user logins and internal administrative logins. The best practice is to let end users into subrealms only, and administrators into the top level realm. If you must execute the post-authentication plugin for administrative logins, make sure that the plugin can also handle internal authentications. An alternate solution is to configure the post-authentication plugin on a per authentication chain basis, which can be configured separately for user logins or internal administrative logins. Realm-level post-authentication plugins are only called when no post-authentication plugin is configured for the authentication chain. |
Post-authentication plugins must be stateless: they do not maintain state between login and logout. Store any information that you want to save between login and logout in a session property. AM stores session properties in the CTS token store after login, and retrieves them from the token store as part of the logout process.
Build your sample post-authentication plugin
The following example post-authentication plugin sets a session property during successful login, writing to its debug log if the operation fails.
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2011-2019 ForgeRock AS. All Rights Reserved
*/
package com.forgerock.openam.examples;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.iplanet.sso.SSOException;
import com.iplanet.sso.SSOToken;
import com.sun.identity.authentication.spi.AMPostAuthProcessInterface;
import com.sun.identity.authentication.spi.AuthenticationException;
/**
* Set a session property on successful authentication.
* If authentication fails, log a debug message.
*/
public class SamplePAP implements AMPostAuthProcessInterface {
private final static String PROP_NAME = "MyProperty";
private final static String PROP_VALUE = "MyValue";
private final static String DEBUG_FILE = "SamplePAP";
private Logger debug = LoggerFactory.getLogger(SamplePAP.class);
public void onLoginSuccess(
Map requestParamsMap,
HttpServletRequest request,
HttpServletResponse response,
SSOToken token
) throws AuthenticationException {
try {
token.setProperty(PROP_NAME, PROP_VALUE);
} catch (SSOException e) {
debug.error("Unable to set property");
}
}
public void onLoginFailure(
Map requestParamsMap,
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException {
// Not used
}
public void onLogout(
HttpServletRequest request,
HttpServletResponse response,
SSOToken token
) throws AuthenticationException {
// Not used
}
}
If you have not already done so, download and build the sample code.
Learn about downloading and building PingAM sample source code in the following Knowledge Base article: How do I access and build the sample code provided for PingAM?.
In the sources, you find the following files:
pom.xml
-
Apache Maven project file for the module
This file specifies how to build the sample post-authentication plugin, and also specifies its dependencies on AM components and on the Servlet API.
src/main/java/com/forgerock/openam/examples/SamplePAP.java
-
Core class for the sample post-authentication plugin
Once built, copy the .jar to the WEB-INF/lib
directory where you deployed AM.
$ cp target/*.jar /path/to/tomcat/webapps/openam/WEB-INF/lib/
Restart AM or the container in which it runs.
Configure your post-authentication plugin
You can associate post-authentication plugins with realms or services (authentication chains). Where you configure the plugin depends on the scope to which the plugin should apply:
-
Plugins configured at the realm level are executed when authenticating to any authentication chain in the realm, provided the authentication chain does not have an associated plugin.
-
Plugins configured at the service level are executed if that authentication chain is used for authentication. Any plugins configured at the realm level will not execute.
In the AM admin UI, go to Realms > Realm Name > Authentication > Settings > Post Authentication Processing.
In the Authentication Post Processing Classes list,
add the sample plugin class, com.forgerock.openam.examples.SamplePAP
, and click Save.
Alternatively, you can configure sample plugin for the realm by using the ssoadm
command.
$ ssoadm set-svc-attrs \
--adminid uid=amAdmin,ou=People,dc=openam,dc=forgerock,dc=org \
--password-file /tmp/pwd.txt \
--servicename iPlanetAMAuthService \
--realm /myRealm \
--attributevalues iplanet-am-auth-post-login-process-class=
com.forgerock.openam.examples.SamplePAP
iPlanetAMAuthService under /myRealm was
modified.
Test your post-authentication plugin
To test the sample post-authentication plugin,
login successfully to AM in the scope where the plugin is configured.
For example, if you configured your plugin for the realm, /myRealm
, specify the realm in the login URL.
https://openam.example.com:8443/openam/XUI/?realm=/myRealm#login
Although you will not notice anywhere in the user interface that AM calls your plugin, a web or Java agent or custom client code could retrieve the session property that your plugin added to the user session.