3.2.11.2. Login
CUBA Platform provides built-in extensible authentication mechanisms. They include different authentication schemes such as login/password, remember me, trusted and anonymous login.
This section primarily describes authentication mechanisms of the middle tier. For web client specifics, see Web Login.
The platform includes the following authentication mechanisms on middleware:
-
AuthenticationManagerimplemented byAuthenticationManagerBean -
AuthenticationProviderimplementations -
AuthenticationServiceimplemented byAuthenticationServiceBean -
UserSessionLog- see user session logging.
Also, it employs the following additional components:
-
TrustedClientServiceimplemented byTrustedClientServiceBean- provides anonymous/system sessions to trusted clients. -
AnonymousSessionHolder- creates and holds anonymous session instance for trusted clients. -
UserCredentialsChecker- checks if user credentials can be used, for instance, protect against brute-force attack. -
UserAccessChecker- checks if user can access system from the given context, for instance, from REST or using provided IP address.
The main interface for authentication is AuthenticationManager which contains four methods:
public interface AuthenticationManager {
AuthenticationDetails authenticate(Credentials credentials) throws LoginException;
AuthenticationDetails login(Credentials credentials) throws LoginException;
UserSession substituteUser(User substitutedUser);
void logout();
}
There are two methods with similar responsibility: authenticate() and login(). Both methods check if provided credentials are valid and corresponds to a valid user, then return AuthenticationDetails object. The main difference between them is that login method additionally activates user session, thus it can be used for calling service methods later.
Credentials represent a set of credentials for authentication subsystem. The platform has several types of credentials that are supported by AuthenticationManager:
Available for all tiers:
-
LoginPasswordCredentials -
RememberMeCredentials -
TrustedClientCredentials
Available only on middle tier:
-
SystemUserCredentials -
AnonymousUserCredentials
AuthenticationManager login / authenticate methods return AuthenticationDetails instance which contains UserSession object. This object can be used to check additional permissions, read User properties and session attributes. There is only one built-in implementation of AuthenticationDetails interface - SimpleAuthenticationDetails that stores only user session object, but application can provide its own AuthenticationDetails implementation with additional information for clients.
AuthenticationManager can do one of three things in its authenticate() method:
-
return
AuthenticationDetailsif it can verify that the input represents a valid user. -
throw
LoginExceptionif it cannot authenticate user with the passed credentials object. -
throw
UnsupportedCredentialsExceptionif it does not support the passed credentials object.
The default implementation of AuthenticationManager is AuthenticationManagerBean, which delegates authentication to a chain of AuthenticationProvider instances. An AuthenticationProvider is an authentication module that can process a specific Credentials implementation, also it has a special method supports() to allow the caller to query if it supports a given Credentials type.
Standard user login process:
-
The user enters their username and password.
-
Application client invokes
Connection.login()method passing the user login and password. -
ConnectioncreatesCredentialsobject and invokeslogin()method ofAuthenticationService. -
AuthenticationServicedelegates execution to theAuthenticationManagerbean, which uses chain ofAuthenticationProviderobjects. There isLoginPasswordAuthenticationProviderthat can work withLoginPasswordCredentialsobjects. It loadsUserobject by the entered login, hashes the obtained password hash again using user identifier as salt and compares the obtained hash to the password hash stored in the DB. In case of mismatch,LoginExceptionis thrown. -
If the authentication is successful, all the access parameters of the user (roles list, rights, restrictions and session attributes) are loaded to the created UserSession instance.
-
If the user session logging is enabled, the record with the user session information is saved to the database.
See also Web Login Procedure.
Password hashing algorithm is implemented by the EncryptionModule type bean and is specified in cuba.passwordEncryptionModule application property. BCrypt is used by default.
- Built-in authentication providers
-
The platform contains the following implementations of
AuthenticationProviderinterface:-
LoginPasswordAuthenticationProvider -
RememberMeAuthenticationProvider -
TrustedClientAuthenticationProvider -
SystemAuthenticationProvider -
AnonymousAuthenticationProvider
All the implementations load user from the database, verify the passed credentials object and create a non-active user session using
UserSessionManager. That session instance can become active later in case ofAuthenticationManager.login()is called.LoginPasswordAuthenticationProvider,RememberMeAuthenticationProviderandTrustedClientAuthenticationProvideruse additional pluggable checks: beans that implementUserAccessCheckerinterface. If at least one of theUserAccessCheckerinstances throwLoginExceptionthen authentication is considered failed andLoginExceptionis thrown.Besides,
LoginPasswordAuthenticationProviderandRememberMeAuthenticationProvidercheck credentials instance using UserCredentialsChecker beans. There is only one built-in implementation of UserCredentialsChecker interface - BruteForceUserCredentialsChecker that checks if a user uses brute-force attack to find out valid credentials. -
- Exceptions
-
AuthenticationManagerandAuthenticationProvidercan throw LoginException or one of its descendants fromauthenticate()andlogin()methods. Also, UnsupportedCredentialsException is thrown if passed credentials object cannot be processed by availableAuthenticationProviderbeans.See the following exception classes:
-
UnsupportedCredentialsException -
LoginException -
AccountLockedException -
UserIpRestrictedException -
RestApiAccessDeniedException
-
- Events
-
Standard implementation of
AuthenticationManager-AuthenticationManagerBeanfires the following application events during login / authentication procedure:-
BeforeAuthenticationEvent/AfterAuthenticationEvent -
BeforeLoginEvent/AfterLoginEvent -
AuthenticationSuccessEvent/AuthenticationFailureEvent -
UserLoggedInEvent/UserLoggedOutEvent -
UserSubstitutedEvent
Spring beans of the middle tier can handle these events using Spring
@EventListenersubscription:@Component public class LoginEventListener { @Inject private Logger log; @EventListener protected void onUserLoggedIn(UserLoggedInEvent event) { User user = event.getSource().getUser(); log.info("Logged in user {}", user.getInstanceName()); } }Event handlers of all events mentioned above (excluding
AfterLoginEvent,UserSubstitutedEventandUserLoggedInEvent) can throwLoginExceptionto interrupt authentication / login process.For instance, we can implement maintenance mode valve for our application that will block login attempts if maintenance mode is active.
@Component public class MaintenanceModeValve { private volatile boolean maintenance = true; public boolean isMaintenance() { return maintenance; } public void setMaintenance(boolean maintenance) { this.maintenance = maintenance; } @EventListener protected void onBeforeLogin(BeforeLoginEvent event) throws LoginException { if (maintenance && event.getCredentials() instanceof AbstractClientCredentials) { throw new LoginException("Sorry, system is unavailable"); } } } -
- Extension points
-
You can extend authentication mechanisms using the following types of extension points:
-
AuthenticationService- replace existingAuthenticationServiceBean. -
AuthenticationManager- replace existingAuthenticationManagerBean. -
AuthenticationProviderimplementations - implement additional or replace existingAuthenticationProvider. -
Events - implement event handler.
You can replace existing beans using Spring Framework mechanisms, for instance by registering a new bean in Spring XML config of the core module.
<bean id="cuba_LoginPasswordAuthenticationProvider" class="com.company.authext.core.CustomLoginPasswordAuthenticationProvider"/>public class CustomLoginPasswordAuthenticationProvider extends LoginPasswordAuthenticationProvider { @Inject public CustomLoginPasswordAuthenticationProvider(Persistence persistence, Messages messages) { super(persistence, messages); } @Override public AuthenticationDetails authenticate(Credentials credentials) throws LoginException { LoginPasswordCredentials loginPassword = (LoginPasswordCredentials) credentials; // for instance, add new check before login if ("demo".equals(loginPassword.getLogin())) { throw new LoginException("Demo account is disabled"); } return super.authenticate(credentials); } }Event handlers can be ordered using the
@Orderannotation. All the platform beans and event handlers useordervalue between 100 and 1000, thus you can add your custom handling before or after the platform code. If you want to add your bean or event handler before platform beans - use a value lower than 100.Ordering for an event handler:
@Component public class DemoEventListener { @Inject private Logger log; @Order(10) @EventListener protected void onUserLoggedIn(UserLoggedInEvent event) { log.info("Demo"); } }AuthenticationProviders can use Ordered interface and implement
getOrder()method.@Component public class DemoAuthenticationProvider extends AbstractAuthenticationProvider implements AuthenticationProvider, Ordered { @Inject private UserSessionManager userSessionManager; @Inject public DemoAuthenticationProvider(Persistence persistence, Messages messages) { super(persistence, messages); } @Nullable @Override public AuthenticationDetails authenticate(Credentials credentials) throws LoginException { // ... } @Override public boolean supports(Class<?> credentialsClass) { return LoginPasswordCredentials.class.isAssignableFrom(credentialsClass); } @Override public int getOrder() { return 10; } } -
- Additional Features
-
-
The platform has a mechanism for the protection against password brute force cracking. The protection is enabled by the cuba.bruteForceProtection.enabled application property on Middleware. If the protection is enabled then the combination of user login and IP address is blocked for a time interval in case of multiple unsuccessful login attempts. A maximum number of login attempts for the combination of user login and IP address is defined by the cuba.bruteForceProtection.maxLoginAttemptsNumber application property (default value is 5). Blocking interval in seconds is defined by the cuba.bruteForceProtection.blockIntervalSec application property (default value is 60).
-
It is possible that the user password (actually, password hash) is not stored in the database, but is verified by external means, for example, by means of integration with LDAP. In this case the authentication is in fact performed by the client block, while the Middleware "trusts" the client by creating the session based on user login only, without the password, using
AuthenticationService.login()method withTrustedClientCredentials. This method requires satisfying the following conditions:-
The client block has to pass the so-called trusted password, specified in the cuba.trustedClientPassword Middleware and client block application property.
-
IP address of the client block has to be in the list specified in the cuba.trustedClientPermittedIpList application property.
-
-
Login to the system is also required for scheduled automatic processes as well as for connecting to the Middleware beans using JMX interface. Formally, these actions are considered administrative and they do not require authentication as long as no entities are changed in the database. When an entity is persisted to the database, the process requires login of the user who is making the change so that the login of the user responsible for the changes is stored.
An additional benefit from login to the system for an automatic process or for JMX call is that the server log output is displayed with the current user login if the user session is set to the execution thread. This simplifies searching messages created by specific process during log parsing.
System access for the processes within Middleware is done using
AuthenticationManager.login()withSystemUserCredentialscontaining the login (without password) of the user on whose behalf the process will be executed. As result, UserSession object will be created and cached in the corresponding Middleware block but it will not be replicated in the cluster.
See more about processes authentication inside Middleware in System Authentication.
-