Implementing the UserAuthenticationSecurityCheck Class
Overview
This abstract class extends CredentialsValidationSecurityCheck
and builds upon it to fit the most common use-cases of simple user authentication. In addition to validating the credentials, it creates a user identity that is accessible from various parts of the framework, allowing you to identify the current user. Optionally, UserAuthenticationSecurityCheck
also provides Remember Me capabilities.
This tutorial uses the example of a security check that asks for a user name and password, and uses the user name to represent an authenticated user.
Prerequisites: Make sure to read the CredentialsValidationSecurityCheck tutorial.
Jump to:
- Creating the Security Check
- Creating the Challenge
- Validating the user credentials
- Creating the AuthenticatedUser object
- Adding RememberMe functionality
- Configuring the security check
- Sample security check
Creating the Security Check
Create a Java adapter and add a Java class named UserLogin
that extends UserAuthenticationSecurityCheck
.
public class UserLogin extends UserAuthenticationSecurityCheck {
@Override
protected AuthenticatedUser createUser() {
return null;
}
@Override
protected boolean validateCredentials(Map<String, Object> credentials) {
return false;
}
@Override
protected Map<String, Object> createChallenge() {
return null;
}
}
Creating the challenge
The challenge is exactly the same as the one described in Implementing the CredentialsValidationSecurityCheck.
@Override
protected Map<String, Object> createChallenge() {
Map challenge = new HashMap();
challenge.put("errorMsg",errorMsg);
challenge.put("remainingAttempts",getRemainingAttempts());
return challenge;
}
Validating the user credentials
When the client sends the challenge answer, the answer is passed to validateCredentials
as a Map
. Use this method to implement your logic. The method returns true
if the credentials are valid.
In this example, credentials are considered “valid” whenusername
and password
are the same:
@Override
protected boolean validateCredentials(Map<String, Object> credentials) {
if(credentials!=null && credentials.containsKey("username") && credentials.containsKey("password")){
String username = credentials.get("username").toString();
String password = credentials.get("password").toString();
if(!username.isEmpty() && !password.isEmpty() && username.equals(password)) {
return true;
}
else {
errorMsg = "Wrong Credentials";
}
}
else{
errorMsg = "Credentials not set properly";
}
return false;
}
Creating the AuthenticatedUser object
The UserAuthenticationSecurityCheck
class stores a representation of the current client (user, device, application) in persistent data, allowing you to retrieve the current user in various parts of your code, such as the challenge handlers or the adapters.
Users are represented by an instance of the class AuthenticatedUser
. Its constructor takes the id
, displayName
, and securityCheckName
parameters.
This example uses username
for both the id
and displayName
parameters.
-
First, modify the
validateCredentials
method to save theusername
argument:private String userId, displayName; @Override protected boolean validateCredentials(Map<String, Object> credentials) { if(credentials!=null && credentials.containsKey("username") && credentials.containsKey("password")){ String username = credentials.get("username").toString(); String password = credentials.get("password").toString(); if(!username.isEmpty() && !password.isEmpty() && username.equals(password)) { userId = username; displayName = username; return true; } else { errorMsg = "Wrong Credentials"; } } else{ errorMsg = "The credentials are not set properly."; } return false; }
-
Then, override the
createUser
method to return a new instance ofAuthenticatedUser
:@Override protected AuthenticatedUser createUser() { return new AuthenticatedUser(userId, displayName, this.getName()); }
You can use this.getName()
to get the current security check name.
UserAuthenticationSecurityCheck
calls your createUser()
implementation after a successful validateCredentials
.
Storing attributes in the AuthenticatedUser
AuthenticatedUser
has an alternate constructor:
AuthenticatedUser(String id, String displayName, String securityCheckName, Map<String, Object> attributes);
This constructor adds a Map
of custom attributes to be stored with the user representation. The map can be used to store additional information such as a profile picture, a website, etc. This information is accessible to the client side (challenge handler) and the resource (using introspection data).
Note: The attributes
Map
must contain only objects of types/classes bundled in the Java library (such asString
,int
,Map
, etc), and not custom classes.
Adding RememberMe functionality
By default, UserAuthenticationSecurityCheck
uses the successStateExpirationSec
property to determine how long the success state lasts. This property is inherited from CredentialsValidationSecurityCheck
.
If you want to allow users to stay logged-in past the successStateExpirationSec
value, UserAuthenticationSecurityCheck
adds this capability.
UserAuthenticationSecurityCheck
adds a property called rememberMeDurationSec
whose default value is 0
: by default, users are remembered for 0 seconds, which means that by default, the feature is disabled. Change this value to a number that makes sense for your application (a day, a week, a month…).
You can also manage the feature by overriding the rememberCreatedUser()
method, which returns true
by default, meaning that the feature is active by default (provided that you changed the duration property).
In this example, the client decides to enable/disable the RememberMe feature by sending a boolean
value as part of the submitted credentials.
-
First, modify the
validateCredentials
method to save therememberMe
choice:private String userId, displayName; private boolean rememberMe = false; @Override protected boolean validateCredentials(Map<String, Object> credentials) { if(credentials!=null && credentials.containsKey("username") && credentials.containsKey("password")){ String username = credentials.get("username").toString(); String password = credentials.get("password").toString(); if(!username.isEmpty() && !password.isEmpty() && username.equals(password)) { userId = username; displayName = username; //Optional RememberMe if(credentials.containsKey("rememberMe") ){ rememberMe = Boolean.valueOf(credentials.get("rememberMe").toString()); } return true; } else { errorMsg = "Wrong Credentials"; } } else{ errorMsg = "Credentials not set properly"; } return false; }
-
Then, override the
rememberCreatedUser()
method:@Override protected boolean rememberCreatedUser() { return rememberMe; }
Configuring the security check
In the adapter.xml file, add a <securityCheckDefinition>
element:
<securityCheckDefinition name="UserLogin" class="com.sample.UserLogin">
<property name="maxAttempts" defaultValue="3" description="How many attempts are allowed."/>
<property name="blockedStateExpirationSec" defaultValue="10" description="How long before the client can try again (seconds)."/>
<property name="successStateExpirationSec" defaultValue="60" description="How long is a successful state valid for (seconds)."/>
<property name="rememberMeDurationSec" defaultValue="120" description="How long is the user remembered by the RememberMe feature (seconds)."/>
</securityCheckDefinition>
As mentioned previously, UserAuthenticationSecurityCheck
inherits all the CredentialsValidationSecurityCheck
properties, such as blockedStateExpirationSec
, successStateExpirationSec
, etc.
In addition, you can also configure a rememberMeDurationSec
property.
Sample Security Check
Download the Security Checks Maven project.
The Maven project contains an implementation of UserAuthenticationSecurityCheck
.