5.2.11.2. Login
CUBA Platform provides built-in authentication mechanisms that are designed to be extensible. They include different authentication schemes such as login/password, remember me, trusted and anonymous login.
The platform includes the following authentication mechanisms on middleware:
-
AuthenticationManager
implemented byAuthenticationManagerBean
-
AuthenticationProvider
implementations -
AuthenticationService
implemented byAuthenticationServiceBean
-
UserSessionLog
- see user session logging.
Also, it employs the following additional components:
-
TrustedClientService
implemented 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
AuthenticationDetails
if it can verify that the input represents a valid user. -
throw
LoginException
if it cannot authenticate user with the passed credentials object. -
throw
UnsupportedCredentialsException
if 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. 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 his username and password.
-
Application client block hashes the password using
getPlainHash()
method ofPasswordEncryption
bean and invokesConnection.login()
middleware method passing the user login and password hash to it. -
Connection
createsCredentials
object and invokeslogin()
method ofAuthenticationService
. -
AuthenticationService
delegates execution to theAuthenticationManager
bean, which uses chain ofAuthenticationProvider
objects. There isLoginPasswordAuthenticationProvider
that can work withLoginPasswordCredentials
objects. It loadsUser
object 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,LoginException
is 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.
Password hashing algorithm is implemented by the EncryptionModule
type bean and is specified in cuba.passwordEncryptionModule application property. SHA-1 is used by default.
- Built-in authentication providers
-
The platform contains the following implementations of
AuthenticationProvider
interface:-
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
,RememberMeAuthenticationProvider
andTrustedClientAuthenticationProvider
use additional pluggable checks: beans that implementUserAccessChecker
interface. If at least one of theUserAccessChecker
instances throwLoginException
then authentication is considered failed andLoginException
is thrown.Besides,
LoginPasswordAuthenticationProvider
andRememberMeAuthenticationProvider
check 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
-
AuthenticationManager
andAuthenticationProvider
can throw LoginException or one of its descendants fromauthenticate()
andlogin()
methods. Also, UnsupportedCredentialsException is thrown if passed credentials object cannot be processed by availableAuthenticationProvider
beans.See the following exception classes:
-
UnsupportedCredentialsException
-
LoginException
-
AccountLockedException
-
UserIpRestrictedException
-
RestApiAccessDeniedException
-
- Events
-
Standard implementation of
AuthenticationManager
-AuthenticationManagerBean
fires 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
subscription:@EventListener
@Component public class LoginEventListener { @Inject private Logger log; @EventListener private 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
,UserSubstitutedEvent
andUserLoggedInEvent
) can throwLoginException
to 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 private 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
. -
AuthenticationProvider
implementations - 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
@Order
annotation. All the platform beans and event handlers useorder
value 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 private 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()
withSystemUserCredentials
containing 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.
-
- Obsolete/Deprecated
-
The following components now are considered deprecated:
-
LoginService
delegates login methods execution toAuthenticationService
-
LoginWorker
delegates login methods execution toAuthenticationManager
Do not use these components in your code. They will be removed in the next version of the platform.
-