KeyMaster
Signon Overview
This document provides reference information about the KeyMaster SignOn framework and examples of how to use it.
You use the SignOn framework to implement a web-based sign-on system. The implementation uses Java Servlets and Filters to protect the generation of Caplin KeyMaster credentials tokens. It can also protect other web resources as required. The framework is supplied as a class library that you extend to implement single-factor user authentication (1FA) and, if required, two-factor authentication (2FA). The extensions you implement would typically make use of the user authentication and 2FA services provided by your existing security system.
The framework consists of three main components:
- A SignOn servlet
- An Authentication Filter
- A KeyMaster servlet
User authentication is handled by the SignOn servlet. This calls the security system to check user credentials,
such as username and password. When a user has been successfully authenticated, their username and authentication
level are stored in the servlet session for later use by the Authentication Filter.
The servlet receives URL requests from the client; any request parameters must be supplied in JSON format,
and it also returns the responses in JSON format. To implement your SignOn servlet, you extend
the AbstractSignOnServlet
class to implement the abstract
authenticate
and
sendToken
methods.
The Authentication Filter sits above resources, such as the KeyMaster servlet, and protects each resource by checking that the user�s session is authenticated to the required level before passing any URL request on to the resource.
The KeyMaster servlet, which is protected by the Authentication Filter, generates credentials tokens that are used to log authenticated users on to Liberator. At the sign-on stage, you can add additional data about the session; the KeyMaster servlet can then extract this data and include it in the generated credentials token.
For a diagram showing the SignOn framework components and how they interact, see KeyMaster SignOn framework
On the rest of this page:
- Message flows
- Implementing the SignOn servlet
- Configuring the Authentication Filter
- Configuring the KeyMaster servlet
- Implementing a Signon System using the Framework
Message flows
Here are some examples of the typical sign-on message flows between a client and the web server that hosts the SignOn framework. The first example shows the message flows for 1FA authentication (authentication of username and password), and the second shows a typical, more complicated sequence for 2FA authentication.
One Factor Authentication (1FA) Message Flow
Two Factor Authentication (2FA) Message Flow
Implementing the SignOn servlet
The KeyMaster kit contains an AbstractSignOnServlet
. To implement the SignOn servlet,
subclass AbstractSignOnServlet and implement the abstract methods: authenticate
and sendToken
. The reference documentation for these methods shows example
implementations of them.
There are also two examples of the SignOn servlet supplied in the KeyMaster kit:
- The SimpleSignonServlet demonstrates how to perform two-factor authentication on a user by using SMS.
- The EncryptedSignonServlet shows how a password and SMS token can be encrypted by using a public/private key pair and a JavaScript encryption library.
Request handling
The AbstractSignOnServlet
provided in the KeyMaster kit handles the following URL requests.
For details of the JSON format parameters that a client must send with URL requests to the SignOn servlet,
and the JSON format parameters that the servlet sends in its responses back to the client,
see SignOn servlet JSON Specifications.
URL Request | Description |
---|---|
/parameters | Requests any signon parameters from the server, the default implementation returns a list of authentication schemes (the default being ['USER'], meaning signon using user and password). Additional schemes (e.g "SMS") and parameters can be added by the signon servlet using You can also add additional parameters to the response, by calling the AbstractSignOnServlet�s
|
/authenticate |
Requests authentication with the supplied authentication scheme, (optional) username, (optional) password and (optional) 2FA token. The AbstractSignOnServlet parses these fields from the request JSON body and then calls your
implementation of the Authentication levels are strings but the common values are available as constants with names of the form SessionData.LEVEL_<LEVEL_NAME> See the table in Authentication levels below. If authentication requires a further step (for example, 2FA using SMS) then you can optionally supply a next step parameter to sendAuthenticationOK(). The client is informed of this via the next_step field of the response (see the SignOn servlet JSON Specifications below). |
/sendtoken |
Where 2FA authentication is required through sending a token using SMS, or a similar method, use the
/sendtoken request to ask the server for the 2FA token. The AbstractSignOnServlet parses the passed-in
username and requested scheme (for example, SMS) and calls your implementation of the
sendToken() should:
|
/logout |
Requests the user be logged out and their session data discarded. |
any other request |
Any other requests are directed to the AbstractSignOnServlet�s |
-----------------------------------------------------------------------------------------------
Authentication schemes
The following table lists some commonly used authentication schemes. The values are returned by the
default implementation of AbstractSignOnServlet in response to the /parameter URL request.
(Strings are used so that your implementation of authenticate
can return additional authentication schemes.)
String Value | Constant | Description |
---|---|---|
USER | SessionData.SCHEME_USER | 1FA signon using username and password |
SMS | SessionData.SCHEME_SMS | 2FA signon with SMS |
TOKEN | SessionData.SCHEME_TOKEN | 2FA signon with hardware token |
Authentication levels
This table lists commonly used authentication levels (Strings are used so that your implementation of
authenticate
can return a different authentication level to one of the standard ones.)
String Value | Constant | Description |
---|---|---|
1FA | SessionData.LEVEL_1FA | User is signed on to the 1FA level |
2FA | SessionData.LEVEL_2FA | User is signed on to the 2FA level |
SignOn servlet JSON Specifications
Here are the specifications of the JSON format parameters that a client must send with URL requests to the SignOn servlet, and the JSON format parameters that the servlet sends in its responses back to the client.
/parameters request
Requests sign-on parameters
Request JSON parameters
None.
Response JSON parameters
Field | Description |
---|---|
result | The result of request SUCCESS or FAILURE |
schemes | Array of authentication schemes. A scheme can be any string value, but the default implementation of the SignOn servlet returns the array USER,SMS,TOKEN |
You can add additional authentication schemes (such as �SMS�) by calling the AbstractSignOnServlet�s
addAuthScheme
method. Scheme names are strings, but the common values are available as
constants with names of the form SessionData.SCHEME_<SCHEME_NAME>.
See the table in Authenication Schemes below.
You can also add additional parameters to the response, by calling the AbstractSignOnServlet�s
addExtraParameter
method.
/authenticate request
Requests authentication with the supplied authentication scheme, (optional) username, (optional) password and (optional) 2FA token.
Request JSON parameters
Field | Description |
---|---|
scheme | The authentication scheme to be used. This can be any string value recognised by your implementation of the SignOn servlet, but values accepted by the default implementation are USER, SMS and TOKEN |
username | The username to be authenticated [optional, as you can instead obtain the username
from the Session object passed into authenticate ] |
password | The user�s password [optional] |
token | The 2FA token [optional] |
Response JSON parameters
Field | Description |
---|---|
result | The result of the request: SUCCESS or FAILURE |
code | A code indicating the reason for a FAILURE result. This can be any string value generated by your implementation of the SignOn servlet, but the values that can be returned by the default implementation are INVALID_CREDENTIALS and ERROR_SERVER |
reason | A text string giving more information about why the authentication request failed (only present if result is FAILURE). |
level | The level authenticated to (only present if result is SUCCESS) See Authenication Levels for a list of commonly used authentication levels. |
next_step | The next step required to authenticate the user [optional]. This can be any string value, but a typical value is 2FA, meaning �proceed to the second step of two-factor authentication�. |
/sendtoken request
Asks the SignOn servlet to send a 2FA token applicable to a specified authentication scheme.
Request JSON parameters
Field | Description |
---|---|
scheme | The authentication scheme to which the requested token applies. This can be any string value but schemes that are supported by the default implementation of the SignOn servlet are listed in Authenication Schemes for example, SMS |
username | The username for whom the token is required. |
Response JSON parameters
Field | Description |
---|---|
result | The result of the request: SUCCESS or FAILURE |
code | A code indicating the reason for a FAILURE result. This can be any string value generated by your implementation of the SignOn servlet, but the values that can be returned by the |
reason | A text string giving more information about why the token request failed (only present if result is FAILURE). |
message | A message sent only if result is SUCCESS |
/logout request
Logs the user out of their current session.
Request JSON parameters
None.
Response JSON parameters
Field | Description |
---|---|
result | The result of the request: SUCCESS or FAILURE |
Example URL requests and responses
Here are some examples of URL requests that can be sent to the SignOn servlet, and typical successful responses to them:
Request | POST body | Response body |
---|---|---|
/parameters | none | {'result':'SUCCESS','schemes':['USER','SMS']} |
/authenticate | {'scheme':'USER','username':'user1','password':'invalidpassword'} | {'result':'FAILURE','code':'INVALID_CREDENTIALS','reason':'Invalid signon'} |
/authenticate | {'scheme':'USER','username':'user1','password':'password1'} | {'result':'SUCCESS','level':'1FA','next_step':'2FA'} |
/sendtoken | {'scheme':'SMS','username':'user1'} | {'result':'FAILURE','code':'SERVER_ERROR','reason':'Session does not contain username'} |
/sendtoken | {'scheme':'SMS','username':'user1'} | {'result':'SUCCESS','message':'SMS token has been sent to 1234567890'} |
/logout | none | {'result':'SUCCESS'} |
Configuring the Authentication Filter
The Authentication Filter is a Servlet filter that intercepts requests from a client before they access a resource, such as the KeyMaster servlet, and checks that the user�s session has been authenticated to the required level. The filter uses the authentication level saved by the SignOn servlet to determine if the request should be passed to the underlying resource.
You must configure in the web application�s web.xml file the URLs, the list of servlets to be protected by the Authentication filter, and the authentication levels for which access is granted to those servlets.
The following snippet of web.xml shows configures the authentication filter to allow access the KeyMaster servlet for sessions authenticated to the '2FA' level; that is, only sessions for users who have successfully logged on using two-factor authentication can access the KeyMaster servlet to obtain a credentials token for accessing Liberator.
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>com.caplin.signon.AuthenticationFilter</filter-class>
<init-param>
<param-name>allowed.auth.levels</param-name>
<param-value>2FA</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/keymaster</url-pattern>
</filter-mapping>
Configuring the KeyMaster servlet
The KeyMaster servlet generates KeyMaster credentials tokens that are used to sign authenticated users on to the Liberator server. Since the KeyMaster servlet is protected behind the Authentication Filter it only receives requests from users who have been authenticated to the desired level.
This KeyMaster servlet uses the following data about the user�s session that was previously saved by
the SignOn servlet in the SessionData
object:
SessionData Field | Description |
---|---|
Username | The username of the authenticated user |
KeyMasterExtraDataToSign | A string of extra data to be added to the credentials token and digitally signed [optional] |
KeyMasterMappingData | A map of extra name-value pairs to be added to the credentials token [optional] |
The KeyMaster servlet parameters:
You configure the KeyMaster servlet by specifying the following Servlet parameters:
Option | Required /Optional | Description |
---|---|---|
caplin.keymaster.privatekey.filename | Required | The location of the PEM PKCS#8 formatted private key file relative to the webapp. |
caplin.keymaster.hashing.algorithm | Optional |
Hashing algorithm name, see KeyMasterHashingAlgorithm for the possible values. The default is SHA256 |
caplin.keymaster.security.provider.class.name | Optional | The class name of a security provider to add to those available. |
caplin.keymaster.security.provider.name | Optional | The name of security provider to use for the token generation. |
The KeyMaster servlet web.xml configuration
Here�s a typical example of the web.xml configuration needed for the KeyMaster servlet.
<servlet>
<servlet-name>KeyMaster</servlet-name>
<servlet-class>com.caplin.signon.KeyMasterServlet</servlet-class>
<init-param>
<param-name>caplin.keymaster.privatekey.filename</param-name>
<param-value>WEB-INF/private.pem</param-value>
<description>Name of the private key filename</description>
</init-param>
</servlet>
Implementing a Signon System using the Framework
The KeyMaster kit includes a signon jar containing the AbstractSignOnServlet, AuthenticationFilter and KeyMasterServlet.
To implement a signon system based on this framework you must:
- Extend the AbstractSignOnServlet and implement the authenticate and sendToken methods
- Package your SignonServlet in a war with the Signon and KeyMaster jars included in the lib directory
- Configure the Servlets and authentication filter in the web.xml.
Example web.xml from simple example:
<web-app> <display-name>Simple Signon Example</display-name> <description>Simple Signon Example</description> <servlet> <servlet-name>SimpleSignon</servlet-name> <servlet-class>com.caplin.signon.SimpleSignonServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SimpleSignon</servlet-name> <url-pattern>/parameters</url-pattern> <url-pattern>/authenticate</url-pattern> <url-pattern>/sendtoken</url-pattern> <url-pattern>/logout</url-pattern> </servlet-mapping> <servlet> <servlet-name>KeyMaster</servlet-name> <servlet-class>com.caplin.signon.KeyMasterServlet</servlet-class> <init-param> <param-name>caplin.keymaster.privatekey.filename</param-name> <param-value>WEB-INF/private.pem</param-value> <description>Name of the private key filename</description> </init-param> </servlet> <servlet-mapping> <servlet-name>KeyMaster</servlet-name> <url-pattern>/keymaster</url-pattern> </servlet-mapping> <filter> <filter-name>AuthenticationFilter</filter-name> <filter-class>com.caplin.signon.AuthenticationFilter</filter-class> <init-param> <param-name>allowed.auth.levels</param-name> <param-value>2FA</param-value> </init-param> </filter> <filter-mapping> <filter-name>AuthenticationFilter</filter-name> <url-pattern>/keymaster</url-pattern> </filter-mapping> </web-app>
The abstract signon servlet should be subclassed and the abstract methods: authenticate
and sendToken
implemented as required.
Example authenticate
implementation:
public void authenticate(String scheme, String username, String password, String token, SessionData sessionData, Mapattributes, HttpServletRequest req, HttpServletResponse resp) throws ServletException { // check the user exists User user = users.get(username); if (user == null) { sendAuthenticateError(req, resp, "Invalid user"); return; } String newLevel = null; String nextStep = null; // check 1AF if (scheme.equals(SessionData.SCHEME_USER)) { if (password.equals(user.password)) { newLevel = SessionData.LEVEL_1FA; nextStep = SessionData.LEVEL_2FA; // user must now authenticate using a 2FA scheme } } // check SMS 2FA else if (scheme.equals(SessionData.SCHEME_SMS) && scheme.equals(sessionData.getSentScheme())) { // must be authenticated to level 1FA already if (sessionData.getLevel().equals(SessionData.LEVEL_1FA)) { // check scheme and token are same as sent if (token.equals(sessionData.getSentSchemeToken())) { newLevel = SessionData.LEVEL_2FA; } } } // if the level has been updated if (newLevel != null) { // update the session with the new level and scheme sessionData.setUserName(username); sessionData.setLevel(newLevel); sessionData.setScheme(scheme); sendAuthenticateOK(req, resp, newLevel, nextStep); } else { sendAuthenticateError(req, resp, "Invalid signon"); } }
Example sendToken
implementation:
public void sendToken(String scheme, String username, SessionData sessionData, Mapattributes, HttpServletRequest req, HttpServletResponse resp) throws ServletException { User user = users.get(username); if (user == null) { sendSendTokenError(req, resp, "Invalid user"); return; } if (scheme.equals(SessionData.SCHEME_SMS)) { String token = generateSMSToken(); // try to send the token via SMS boolean sent = sendSMSToken(user.smsPhoneNumber, token); if (sent) { // record scheme and token sent in the session sessionData.setSentScheme(SessionData.SCHEME_SMS); sessionData.setSentSchemeToken(token); sendSendTokenOK(req, resp, "SMS token has been sent to " + user.smsPhoneNumber); } else { sendSendTokenError(req, resp, "Error sending token using SMS"); } } else { sendSendTokenError(req, resp, "Error invalid 2FA scheme"); } } private boolean sendSMSToken(String phoneNumber, String text) { // implement this to send SMS message return true; } private String generateSMSToken() { // implement this to generate the SMS token return "123456"; }
Two examples are provided, the SimpleSignonServlet example demonstrates how to authenticate a user using SMS for the 2FA method, the EncryptedSignonServlet example shows how the password and SMS token could be encrypted by using a public/private key pair and a Javascript encryption library.