Person Authentication scripts#
The Janssen Server leverages interception scripts of PersonAuthenticationType which when implemented can facilitate complex multi-step, multi-factor authentication workflows.
The authentication flow in the Janssen Server is driven by the
OpenID Connect specification.
The authorization request to the OP (the Janssen Server) contains an
optional query parameter called acr_values
which is used by the OP to pick an
interception script which will be run when /authorize
endpoint
(Authentication flow) is invoked.
Each authentication method, whose name is the acr
value,
is tied to a PersonAuthenticationType
script which offers the authentication
workflow.
Typically, a PersonAuthenticationType
script can be used to:
- Introduce a new 2FA authentication mechanism
- Customize multi-step authentication
- Offer Social logins
- Proactively perform fraud detection and block a fraudulent user
Authentication mechanisms offered by Jans can be confirmed by checking the Janssen Server OP configuration URL:
https://<hostname>/.well-known/openid-configuration
under the claim acr_values_supported
.
Building blocks of an authentication workflow#
Jans-auth server comprises of a number of beans, configuration files and Facelets (JSF) views, packaged as a WAR module. That means custom scripts and custom pages (JSF facelets) can make use of business logic already encapsulated in the Weld managed beans.
The following sections explain how authentication flow can be built using a custom script.
A. Custom script#
The PersonAuthenticationType script is described by a java interface whose methods should be overridden to implement an authentication workflow. The article talks about these methods in detail and the psuedo code for each method.
B. UI pages:#
All web pages are xhtml files. The Command-Action offering by JSF framework is used by the Jans-auth server to implement authentication flows.
a. Server-side actions implemented by custom script:#
The custom script's authenticate
and prepareForStep
implementations are
called by the following java class - Authenticator. These methods are
mapped as command actions and view actions respectively in the web page.
Relevant methods:
Signature | Description |
---|---|
boolean authenticate() | Makes the authentication flow proceed by calling the authenticate method of the custom script |
String prepareAuthenticationForStep() | Makes the authentication flow proceed by calling the prepareForStep method of the custom script |
b. Web page in xhtml:#
- The
f:metadata
andf:viewAction
tags are used to load variables (prepared in theprepareForStep
method of the custom script). These variables are rendered on the UI page.<f:metadata> <f:viewAction action="#{authenticator.prepareAuthenticationForStep}" if="#{not identity.loggedIn}" /> </f:metadata>
- A form submit takes the flow to the
authenticate()
method of the custom script.<h:commandButton id="updateButton" value="Update" styleClass="btn btn-primary col-sm-4" action="#{authenticator.authenticate}" style="margin:5px;" />
c. Page customizations:#
The Jans-auth server comes with a default set of pages for login, logout, errors, authorizations. You can easily override these pages or write new ones. You can easily apply your own stylesheet, images and resouce-bundles to your pages.
This article covers all the details you need to write your own web page.
C. Business logic in Custom script:#
Jans-auth server uses Weld 3.0 (JSR-365 aka CDI 2.0) for managed beans. The most important aspects of business logic are implemented through a set of beans. Details and examples of this can be found in this article
D. Third party libraries for use in the custom script#
Java or Python libraries to be imported and used very easily. Remember, you can import a python library only if it has been written in "pure python". More details of this mentioned here
E. Configuring the acr
parameter in the Jans-auth server:#
The acr
parameter can be configured in the following ways :
Default authentication method:#
default_acr
: This is the default authentication mechanism exposed to all
applications that send users to the Janssen Server for sign-in. Unless an app
specifically requests a different form of authentication using the
OpenID Connect acr_values parameter (as specified below), users will receive
the form of authentication specified in this field.
Internal ACR#
If a default ACR is not specified, Janssen will determine it based on enabled
scripts and the internal user/password ACR. This internal ACR,
simple_password_auth
, is set to level -1. This means that it has lower
priority than any scripts, so Janssen server will use it only if no other
authentication method is set.
Use the jans-cli to update / look-up the default authentication method.
Authentication method for a client (RP):#
A client may also specify default_acr_values
during registration
(and omit the parameter acr_values
while making an authentication request).
Multiple Authentication Mechanisms#
The Jans Server can concurrently support multiple authentication mechanisms,
enabling Web and mobile apps (clients) to request a specific type of
authentication using the standard OpenID Connect request parameter: acr_values
.
Learn more about acr_values in the OpenID Connect core spec.
Enabling an authentication mechanism#
An Authentication method is offered by the AS if its ACR value i.e. its
corresponding custom script is enabled
.
By default, users will get the default authentication mechanism as specified above. However, using the OpenID Connect acr_values parameter, web and mobile clients can request any enabled authentication mechanism.
-
Obtain the json contents of a custom script by using a jans-cli command like
get-config-scripts-by-type
,get-config-scripts-by-inum
etc. Example :/opt/jans/jans-cli/config-cli.py --operation-id get-config-scripts-by-type --url-suffix type:PERSON_AUTHENTICATION
/opt/jans/jans-cli/config-cli.py --operation-id get-config-scripts-by-inum --url-suffix inum:6122281b-b55d-4dd0-8115-b098eeeee2b7
-
Update the custom script and change the
enabled
attribute totrue
Level (rank) of an Authentication mechanism :#
Each authentication mechanism (script) has a "Level" assigned to it which
describes how secure and reliable it is. The higher the "Level", higher is
the reliability represented by the script. Though several mechanisms can be
enabled at the same Janssen server instance at the same time, for any specific
user's session only one of them can be set as the current one (and will be
returned as acr
claim of id_token for them). If after initial session is
created a new authorization request from a RP comes in specifying another
authentication method, its "Level" will be compared to that of the method
currently associated with this session. If requested method's "Level" is
lower or equal to it, nothing is changed and the usual SSO behavior is observed.
If it's higher (i.e. a more secure method is requested), it's not possible to
serve such request using the existing session's context, and user must
re-authenticate themselves to continue. If they succeed, a new session
becomes associated with that requested mechanism instead.
F. Using person authenticator jansAuthenticator
attribute :#
The jansAuthenticator
attribute can be used in cases when person authenticator
need to persist data between person logins. For example script can have
enrollment and authentication flows. And after enrollment it needs to store
data into user entry for later use in authentication flow.
In order to search by enrolled authenticator there is another multi-valued
attribute jansExtUid
. This attribute has default DB index to quick search.
Both attributes is recommended to use In Jans since 1.1.1 version for better
compatibility in future.
1. Format of attributes#
jansExtUid: [otp:xyz1, cert:xyz2, duo:xyz3, ...]
jansAuthenticator: {
"xyz1": {"id": "xyz1", "type": "otp", "custom": {...}},
"xyz2": {"id": "xyz2", "type": "cert", "custom": {...}},
"xyz3": {"id": "xyz3", "type": "duo", "custom": {...}},
...
}
2. Sample data:#
jansExtUid: [hotp:S9dO_qKQoOcpPk9GuStlNO9seoA=, cert:totp, duo:Nv7Dg7aP0wRPJd6NHjx1ai9bN9Y=]
jansAuthenticator:
{
"S9dO_qKQoOcpPk9GuStlNO9seoA=": {
"id": "S9dO_qKQoOcpPk9GuStlNO9seoA=",
"type": "hotp",
"custom": {
"movingFactor": 3
}
},
"Nv7Dg7aP0wRPJd6NHjx1ai9bN9Y=": {
"id": "Nv7Dg7aP0wRPJd6NHjx1ai9bN9Y=",
"type": "totp"
}
}
2. Access jansAuthenticator
and jansExtUid
from generic bean#
For both attributes there are get/set methods in User
entry. After loading
user object script can call these methods to get required data.
...
@AttributeName(name = "jansExtUid")
private String[] externalUid;
@JsonObject
@AttributeName(name = "jansAuthenticator")
private UserAuthenticatorList authenticator
...
public String[] getExternalUid() {
return externalUid;
}
public void setExternalUid(String[] externalUid) {
this.externalUid = externalUid;
}
public UserAuthenticatorList getAuthenticator() {
return authenticator;
}
public void setAuthenticator(UserAuthenticatorList authenticator) {
this.authenticator = authenticator;
}
...
UserAuthenticatorList
is defined here. It contains list
of UserAuthenticator
objects. UserAuthenticator
is defined here
For convenience in 1.1.1 there is new service UserAuthenticator
. See it's
code here
It provides next API methods:
public UserAuthenticatorList getUserAuthenticatorList(SimpleUser user);
public List<UserAuthenticator> getUserAuthenticatorsByType(SimpleUser user, String type);
public UserAuthenticator getUserAuthenticatorById(SimpleUser user, String id);
public void addUserAuthenticator(SimpleUser user, UserAuthenticator userAuthenticator);
public void removeUserAuthenticator(SimpleUser user, UserAuthenticator userAuthenticator);
public void removeUserAuthenticator(SimpleUser user, String type);
public UserAuthenticator createUserAuthenticator(String id, String type);
public UserAuthenticator createUserAuthenticator(String id, String type, Map<String, Object> custom);
public String formatExternalUid(String id, String type);
public boolean checkAndMigrateToAuthenticatorList(SimpleUser user);
All these methods update both jansAuthenticator
and jansExtUid
attributes
without persisting changes to DB. After calling these methods script should
persist user entry. It's expected behaviour because script might update other
fields too. Hence it's better to persist all changes in one update request to DB.
1. Default authentication method:#
default_acr
: This is the default authentication mechanism exposed to all
applications that send users to the Janssen Server for sign-in. Unless an app
specifically requests a different form of authentication using the
OpenID Connect acr_values parameter (as specified below), users will receive
the form of authentication specified in this field.
Usage scenarios#
A. Implementing 2FA authentication mechanisms#
-
FIDO2 : Authentications using platform authenticators embedded into a person's device or physical USB, NFC or Bluetooth security keys that are inserted into a USB slot of a computer
-
OTP authentication : Authentication mechanism using an app like Google authenticator, FreeOTP or Authy that implements the open standards HOTP and TOTP
-
SMS OTP :
-
Email OTP:
B. Implementing Multistep authentication#
- Redirect to previous step: The script here an example of how the number of steps can be varied depending on the context or business requirement.
C. Implementing Social logins#
You can use a PersonAuthenticationType
script to allow users to sign using
credentials from popular Social Identity providers or
Inbound Identity Providers like Facebook, Google and Apple. After users
authenticate, thier Social Identity Provider credentials are provisioned into
the Jans-auth server. More on this topic in this article
D. Proactively perform fraud detection#
- Impossible travel
Testing an authentication flow#
An example of a complete URL looks like this - ```
https://<your.jans.server>/jans-auth/authorize.htm? \
response_type=code&redirect_uri=https://<your.jans.server>/admin \
&client_id=17b8b82e-b3ec-42a2-bd90-097028a37f3 \
&scope=openid+profile+email+user_name \
&state=faad2cdjfdddjfkdf&nonce=dajdffdfsdcfff
```
To test , enter the complete URL for authorization in a browser or create a simple webmapage with a link that simulates the user sign-in attempt. If the server is configured properly, the first page for the selected authentication method will be displayed to the user.
FAQs#
1. How can error messages be displayed on a web page?#
- FacesMessage bean is used for this purpose.
from org.jans.jsf2.message import FacesMessages from org.jans.service.cdi.util import CdiUtil from javax.faces.application import FacesMessage ... facesMessages = CdiUtil.bean(FacesMessages) facesMessages.setKeepMessages() facesMessages.add(FacesMessage.SEVERITY_ERROR, "Please enter a valid username")
- The error will appear in the associated template using the following markup:
See an example here
... <h:messages /> ...
2. How is redirection to a third party application for authentication handled in a script?#
For user authentication or consent gathering, there might be a need to redirect to a third party application to perform some operation and return the control back to authentication steps of the custom script. Please apply these steps to a person authentication script in such a scenario:
- Return from def getPageForStep(self, step, context), a page /auth/method_name/redirect.html ; with content similar to the code snippet below -
def getPageForStep(self, step, context): return "/auth/method_name/redirect.html"
- Contents of redirect.xhtml should take the flow to prepareForStep method
... <f:metadata> <f:viewAction action="#{authenticator.prepareForStep}" if="#{not identity.loggedIn}" /> </f:metadata>
- In method prepareForStep prepare data needed for redirect and perform the redirection to the external service.
def prepareForStep(self, step, context): ..... facesService = CdiUtil.bean(FacesService) facesService.redirectToExternalURL(third_party_URL ) return True
- In order to resume flow after the redirection, invoke a similar URL to https://my.gluu.server/postlogin.htm?param=123 from the third party app which takes the flow back to the authenticate method of the custom script.
So create an xhtml page postlogin.xhtml which will look like this :
The
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://xmlns.jcp.org/jsf/core"> <f:view transient="true" contentType="text/html"> <f:metadata> <f:viewAction action="#{authenticator.authenticateWithOutcome}" /> </f:metadata> </f:view> </html>
<f:viewAction action="#{authenticator.authenticateWithOutcome}" />
in step 4 takes us to the authenticate method inside the custom scriptdef authenticate(self, configurationAttributes, requestParameters, step):
. Here you can - use parameters from request
param = ServerUtil.getFirstValue(requestParameters, "param-name"))
-
perform the
state
check (state : Opaque value used to maintain state between the request and the callback.) -
finally, return true or false from this method.
Created: 2022-05-18