Authentication and session modules
An authentication module specifies how a user or client is authenticated. You configure authentication and session modules in your project’s conf/authentication.json
file.
IDM evaluates authentication modules in the order in which they appear in that file, and uses the first "successful" authentication module it finds. Subsequent modules are not evaluated. In a production environment, you should remove any unused authentication modules from your authentication.json
file.
To authenticate a user or client, IDM validates the provided credentials against some resource. That resource can be either an IDM resource such as managed/user
or internal/user
, or it can be an external resource such as an LDAP server. You should prioritize the authentication modules that query IDM resources over those that query external resources. Prioritizing modules that query external resources can lead to authentication problems for internal users such as openidm-admin
.
You can also configure authentication modules in the admin UI. Select Configure > Authentication, and select the Session or Module tab. To change the order of authentication modules in the admin UI, simply drag the modules up or down so that they appear in the order in which they should be evaluated.
Modifying an authentication module in the admin UI might affect your current session. In this case, IDM prompts you with the following message: Your current session may be invalid. Click here to logout and re-authenticate. When you select the Click here link, IDM logs you out of any current session and returns you to the login screen. |
IDM supports the following authentication and session modules:
JWT_SESSION
IDM supports one session module, the JSON Web Token (JWT) Session Module. When a client authenticates successfully, the JWT Session Module creates a JWT and sets it as a cookie on the response. On subsequent requests, the module checks for the presence of the JWT as a cookie on the request, validates the signature and decrypts it, and checks the expiration time of the JWT.
JWT sessions are entirely stateless, that is, they are not persisted in the backend. All information pertaining to the session is encrypted in the JWT.
When a request to IDM produces a JWT, that value replaces the previous one used to send that request. In this way the JWT is always updated to the latest copy. The idle timeout in the JWT is therefore continuously updated and active sessions are not abruptly killed mid-session.
By default, the JWT cookie is deleted on logout. Deleting the cookie manually ends the session. You can modify what happens to the session after a browser restart by changing the value of the sessionOnly
property.
The default JWT Session Module configuration, in conf/authentication.json
, is as follows:
"sessionModule" : {
"name" : "JWT_SESSION",
"properties" : {
"maxTokenLifeMinutes" : 120,
"tokenIdleTimeMinutes" : 30,
"sessionOnly" : true,
"isHttpOnly" : true,
"enableDynamicRoles" : false
}
}
|
For more information about this module, refer to the Class JwtSessionModule JavaDoc.
Attempting to access IDM without the appropriate headers or session cookie results in an HTTP 401 Unauthorized, or HTTP 403 Forbidden, depending on the situation. If you authenticate using a session cookie, you must include an additional header that indicates the origin of the request.
The following example shows a successful authentication attempt and the return of a session cookie:
curl \ --dump-header /dev/stdout \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ "https://localhost:8443/openidm/managed/user?_queryFilter=true&_fields=_id" HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Cache-Control: no-cache Set-Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ...;Path=/ Expires: Thu, 01 Jan 1970 00:00:00 GMT Vary: Accept-Encoding, User-Agent Content-Length: 82 Server: Jetty(8.y.z-SNAPSHOT)
The following example uses the cookie returned in the previous response, and includes the X-Requested-With
header to indicate the origin of the request. The value of the header can be any string, but should be informative for logging purposes. If you do not include the X-Requested-With
header, IDM returns HTTP 403 Forbidden:
curl \ --dump-header /dev/stdout \ --header "Cookie: session-jwt=2l0zobpuk6st1b2m7gvhg5zas ..." \ --header "X-Requested-With: OpenIDM Plugin" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ "https://localhost:8443/openidm/managed/user?_queryFilter=true&_fields=_id" Expires: Thu, 01 Jan 1970 00:00:00 GMT Content-Type: application/json; charset=UTF-8 Cache-Control: no-cache Vary: Accept-Encoding, User-Agent Content-Length: 82 Server: Jetty(8.y.z-SNAPSHOT)
The expiration date of the JWT cookie, January 1, 1970, corresponds to the start of UNIX time. Since that time is in the past, browsers will not store that cookie after the browser session is closed.
Authentication requests are logged in the authentication.audit.json
file. A successful authentication request is logged as follows:
{
"_id": "389d15d3-bdd5-4521-ae3c-bf096d334405-915",
"timestamp": "2019-08-02T11:53:31.110Z",
"eventName": "SESSION",
"transactionId": "389d15d3-bdd5-4521-ae3c-bf096d334405-912",
"trackingIds": [
"5f9f4941-bcbd-4cbc-97f7-e763808e4310",
"88973bcf-0d60-41b8-9922-73718ce76e11"
],
"userId": "openidm-admin",
"principal": [
"openidm-admin"
],
"entries": [
{
"moduleId": "JwtSession",
"result": "SUCCESSFUL",
"info": {
"org.forgerock.authentication.principal": "openidm-admin"
}
}
],
"result": "SUCCESSFUL",
"provider": null,
"method": "JwtSession"
}
For information about querying this log, refer to Query the Authentication Audit Log.
Authenticate without a session
Once a client has authenticated, the JWT_SESSION
takes precedence over any other authentication modules, for subsequent requests. In some cases, you might want to force clients to re-authenticate for each request. This is the case, for example, if you authenticate using a client certificate.
To request one-time authentication without a session, use the X-OpenIDM-NoSession
header in the authentication request. For example:
curl \
--dump-header /dev/stdout \
--header "X-OpenIDM-NoSession: true" \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--cacert ca-cert.pem \
--header "Accept-API-Version: resource=1.0" \
"https://localhost:8443/openidm/managed/user?_queryFilter=true&_fields=_id"
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-cache
Vary: Accept-Encoding, User-Agent
Content-Length: 82
Server: Jetty(8.y.z-SNAPSHOT)
Deterministic ECDSA signatures
By default, JWTs are signed with deterministic Elliptic Curve Digital Signature Algorithm (ECDSA). In order to use this more secure signing method, Bouncy Castle, which is included in the default IDM installation, must be installed. If Bouncy Castle is unavailable, or the key is incompatible, IDM falls back to normal ECDSA.
If you need to turn off the use of deterministic ECDSA, set the following property in your
|
STATIC_USER
The STATIC_USER
module provides an authentication mechanism that avoids database lookups by hard coding a static user. IDM includes a default anonymous
static user, but you can create any static user for this module.
The following sample REST call uses STATIC_USER
authentication with the anonymous
user in the self-registration process:
curl \ --header "X-OpenIDM-Password: anonymous" \ --header "X-OpenIDM-Username: anonymous" \ --header "Accept-API-Version: resource=1.0" \ --cacert ca-cert.pem \ --header "Content-Type: application/json" \ --request POST \ --data '{ "userName": "steve", "givenName": "Steve", "sn": "Carter", "telephoneNumber": "0828290289", "mail": "scarter@example.com", "password": "Passw0rd" }' \ "https://localhost:8443/openidm/managed/user/?_action=create"
This is not the same as an anonymous request that is issued without headers. |
Authenticating with the STATIC_USER
module avoids the performance cost of reading the database for self-registration, certain UI requests, and other actions that can be performed anonymously. Authenticating the anonymous user with the STATIC_USER
module is identical to authenticating the anonymous user with the INTERNAL_USER
module, except that the database is not accessed. So, STATIC_USER
authentication provides an authentication mechanism for the anonymous user that avoids the database lookups incurred when using INTERNAL_USER
.
A sample STATIC_USER
authentication configuration follows:
{
"name" : "STATIC_USER",
"enabled" : true,
"properties" : {
"queryOnResource" : "internal/user",
"username" : "anonymous",
"password" : "anonymous",
"defaultUserRoles" : [
"internal/role/openidm-reg"
]
}
}
IDM also uses the STATIC_USER
module to set the password and default roles of the openidm-admin
internal user on startup. The following configuration in the authentication.json
file sets up the openidm-admin
user:
{
"name" : "STATIC_USER",
"properties" : {
"queryOnResource" : "internal/user",
"username" : "openidm-admin",
"password" : "&{openidm.admin.password}",
"defaultUserRoles" : [
"internal/role/openidm-authorized",
"internal/role/openidm-admin"
]
},
"enabled" : true
}
For information on changing the default openidm-admin
password, see
Change the Administrator User Password.
TRUSTED_ATTRIBUTE
The TRUSTED_ATTRIBUTE
authentication module lets you configure IDM to trust a specific HttpServletRequest
attribute. To enable this module, add it to your authentication.json
file as follows:
{
"name" : "TRUSTED_ATTRIBUTE",
"properties" : {
"queryOnResource" : "managed/user",
"propertyMapping" : {
"authenticationId" : "userName",
"userRoles" : "authzRoles"
},
"defaultUserRoles" : [ ],
"authenticationIdAttribute" : "X-ForgeRock-AuthenticationId",
"augmentSecurityContext" : {
"type" : "text/javascript",
"file" : "auth/populateRolesFromRelationship.js"
}
},
"enabled" : true
}
TRUSTED_ATTRIBUTE
authentication queries the managed/user
resource, and allows authentication when credentials match, based on the username
and authzRoles
assigned to that user, specifically the X-ForgeRock-AuthenticationId
attribute.
To use the
|
MANAGED_USER
MANAGED_USER
authentication queries the repository and allows authentication if the credentials match. Despite the module name, the query is not restricted to managed/user objects. The resource that is queried is configurable. The default configuration uses the username
and password
of a managed user to authenticate, as shown in the following sample configuration:
{
"name" : "MANAGED_USER",
"properties" : {
"augmentSecurityContext": {
"type" : "text/javascript",
"source" : "require('auth/customAuthz').setProtectedAttributes(security)"
},
"queryId" : "credential-query",
"queryOnResource" : "managed/user",
"propertyMapping" : {
"authenticationId" : "username",
"userCredential" : "password",
"userRoles" : "authzRoles"
},
"defaultUserRoles" : [
"internal/role/openidm-authorized"
]
},
"enabled" : true
}
Use the augmentSecurityContext
property to add custom properties to the security context of users who authenticate with this module. By default, this property adds a list of protected properties to the user’s security context. These protected properties are defined in the managed object schema. The isProtected
property is described in Create and modify object types.
INTERNAL_USER
INTERNAL_USER
authentication queries the internal/user
objects in the repository and allows authentication if the credentials match. An example configuration that uses the username
and password
of the internal user to authenticate follows:
{
"name" : "INTERNAL_USER",
"enabled" : true,
"properties" : {
"queryId" : "credential-internaluser-query",
"queryOnResource" : "internal/user",
"propertyMapping" : {
"authenticationId" : "username",
"userCredential" : "password",
"userRoles" : "authzRoles"
},
"defaultUserRoles" : [ ]
}
}
CLIENT_CERT
Client certificate authentication (also called mutual SSL authentication) occurs as part of the SSL or TLS handshake, which takes place before any data is transmitted in an SSL or TLS session. This authentication module is typically used when users have secure certificates that they install in their browsers for authentication and authorization.
The client certificate module, CLIENT_CERT
, authenticates by validating a client certificate, transmitted through an HTTP request. IDM compares the subject DN of the request certificate with the subject DN of the truststore.
A sample CLIENT_CERT
authentication configuration follows:
{
"name" : "CLIENT_CERT",
"properties" : {
"augmentSecurityContext" : {
"type" : "text/javascript",
"globals" : { },
"file" : "auth/mapUserFromClientCert.js"
},
"queryOnResource" : "managed/user",
"defaultUserRoles" : [
"internal/role/openidm-authorized"
],
"allowedAuthenticationIdPatterns" : [
".*CN=localhost, O=ForgeRock.*"
]
},
"enabled" : true
}
When a user authenticates with a client certificate, they receive the roles listed in the defaultUserRoles
property of the CLIENT_CERT
module. Privileges are calculated dynamically per request when enabled in the session module.
Client certificate authentication is also used when the client is a password plugin, such as those described in Password Plugins. This process is similar to an administrative request to modify the passwords of regular users. For password plugin clients, you must include |
Test client certificate authentication
This procedure demonstrates client certificate authentication by generating a self-signed certificate, adding that certificate to the truststore, then authenticating with the certificate. At the end of this procedure, you will verify the certificate over port 8444
as defined in your project’s resolver/boot.properties
file:
openidm.auth.clientauthonlyports=8444
The example assumes an existing managed user, bjensen, with email address bjensen@example.com
.
-
Create a self-signed certificate for user bjensen as follows:
openssl req \ -x509 \ -newkey rsa:1024 \ -keyout /path/to/key.pem \ -out /path/to/cert.pem \ -days 3650 \ -nodes Generating a 1024 bit RSA private key ................. .................. writing new private key to 'key.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) []: US State or Province Name (full name) []: Washington Locality Name (eg, city) []: Vancouver Organization Name (eg, company) []: Example.com Organizational Unit Name (eg, section) []: Common Name (eg, fully qualified host name) []: localhost Email Address []: bjensen@example.com
The Email Address
is used by themapUserFromClientCert.js
to map the user against an existing managed user. -
Import the client certificate into the IDM truststore:
keytool \ -importcert \ -keystore /path/to/openidm/security/truststore \ -storetype JKS \ -storepass changeit \ -file /path/to/cert.pem \ -trustcacerts \ -noprompt \ -alias client-cert-example Certificate was added to keystore
By default, users can authenticate only if their certificates have been issued by a Certificate Authority (CA) that is listed in the truststore. The default truststore includes several trusted root CA certificates, and any user certificate issued by those CAs will be trusted. Change the value of this property to restrict certificates to those issued to users in your domain, or use some other regular expression to limit who will be trusted. If you leave this property empty, no certificates will be trusted.
-
Edit your project’s
conf/authentication.json
file. Add theCLIENT_CERT
module, and add at least the email address from the certificate subject DN to theallowedAuthenticationIdPatterns
:{ "name": "CLIENT_CERT", "properties": { "augmentSecurityContext": { "type": "text/javascript", "globals": {}, "file": "auth/mapUserFromClientCert.js" }, "queryOnResource": "managed/user", "defaultUserRoles": [ "internal/role/openidm-cert", "internal/role/openidm-authorized" ], "allowedAuthenticationIdPatterns": [ ".*EMAILADDRESS=bjensen@example.com.*" ] }, "enabled": true }
The allowedAuthenticationIdPatterns
property is unique to this authentication module. This property contains a regular expression that defines which user distinguished names (DNs) are allowed to authenticate with a certificate. -
Send an HTTP request with your certificate file
cert.pem
to the secure port:curl \ --insecure \ --cert-type PEM \ --key /path/to/key.pem \ --key-type PEM \ --cert /path/to/cert.pem \ --header "X-Requested-With: curl" \ --header "X-OpenIDM-NoSession: true" \ --request GET "https://localhost:8444/openidm/info/login" { "_id": "login", "authenticationId": "EMAILADDRESS=bjensen@example.com, CN=localhost, O=Example.com, L=Vancouver, ST=Washington, C=US", "authorization": { "userRolesProperty": "authzRoles", "component": "managed/user", "authLogin": false, "roles": [ "internal/role/openidm-cert", "internal/role/openidm-authorized" ], "ipAddress": "0:0:0:0:0:0:0:1", "id": "aba3e666-c0db-4669-8760-0eb21f310649", "moduleId": "CLIENT_CERT" } }
|
PASSTHROUGH
PASSTHROUGH
authentication queries an external system, such as an LDAP server, and allows authentication if the credentials included in the REST request match those in the external system.
The following excerpt of an authentication.json
shows a pass-through authentication configuration for an LDAP system:
"authModules" : [
{
"name" : "PASSTHROUGH",
"enabled" : true,
"properties" : {
"augmentSecurityContext": {
"type" : "text/javascript",
"file" : "auth/populateAsManagedUser.js"
},
"queryOnResource" : "system/ldap/account",
"propertyMapping" : {
"authenticationId" : "uid",
"groupMembership" : "ldapGroups"
},
"groupRoleMapping" : {
"internal/role/openidm-admin" : ["cn=admins,ou=Groups,dc=example,dc=com"]
},
"defaultUserRoles" : [
"internal/role/openidm-authorized"
]
},
},
...
]
For more information on authentication module properties, refer to Authentication and session module configuration.
Many of the documented samples are configured for pass-through authentication. For example, the sync-with-ldap*
samples use an external LDAP system for authentication.