Authentication
Basic view
From the simple viewpoint, authentication mechanism in CzechIdM can be divided into three areas:
- First access: Unauthenticated users come to CzechIdM for the first time. They fill in their login and password and log in.
- Following requests: Users make requests for various pages. Every request needs to be authenticated again, for the backend to know who makes the request. There is no "session" held for the user on the server side. But of course users don't fill the credentials every time they make a request.
- Single-sign-on (SSO): Unauthenticated users come to CzechIdM for the first time. Without the need to fill any login or password, they are authenticated to CzechIdM and come to the front page.
The First access part is simply a request to the /authentication
endpoint, which is handled by LoginController. The authentication is then processed by configured authenticators (for more details see Authenticators). The result of the successful authentication is a JWT token, which is returned to the frontend client through HTTP headers.
Following requests need to contain the information about the user who makes the request. So the client appends the JWT token to the request HTTP headers. The AuthenticationFilter intercepts every request and lets all configured IdmAuthenticationFilters process the request. One of them is JwtIdMAuthenticationFilter, which verifies the JWT token, retrieves the user name from it and authenticates that user.
Of course, following requests can't use the same JWT token forever, because it contains also the expiration date of the token. Therefore, the ExtendExpirationFilter intercepts every requests. This filter verifies current JWT token and creates a new one with extended expiration date. The frontend saves this new JWT token and uses it for following requests.
SSO is an additional ability of the filter mechanism, which can be implemented if desired. Since the filters intercept every request to a secured endpoint, they are also called when the endpoint /authentication/remote-auth
is requested the first time the user comes to CzechIdM (it is requested, because the frontend is so implemented). So there can be some instance of IdmAuthenticationFilter
which will e.g. check some cookie or HTTP header, send its value to some external authority and obtain corresponding user name. The filter then authenticates this user, creates the JWT token a returns it. In the end, the user is authenticated to CzechIdM, even if no credentials of the local CzechIdM user were given to the authentication endpoint.
Authenticators
Components implementing Authenticator are used as an authentication chain. Every such component checks authentication against some authority, see the actual list of authenticators.
Every authenticator defines how the result of its authentication should be processed. For possible types of results see (AuthenticationResponseEnum):
- requisite - when authentication fails, the authentication chain is immediately broken and throws an exception, the user won't be authenticated.
- sufficient - this authenticator is authoritative, i.e. when its authentication succeeds, the authentication chain doesn't continue and is successful.
When the authenticator throws an exception, the authentication chain may continue or be broken depending on its defined result type (see results' type). If the authenticator doesn't return any valid object of type LoginDto (i.e. return NULL), the authentication chain continues.
The authenticators are ordered. They are processed in defined order in the implementation of the class AuthenticationManager. The authentication manager also checks the module to which the authenticator belongs. Authenticators from inactive modules are removed from the authentication chain.
Actual list of authenticators
DefaultCoreAuthenticator
DefaultCoreAuthenticator is the standard authenticator of IdM. It checks LoginDto against user name and password in the local IdM repository.
The service LoginService is called in this authenticator.
The result type of this authenticator is SUFFICIENT, it's order is 0.
DefaultAccAuthenticator
DefaultAccAuthenticator checks the LoginDto against an end system. Users use their internal IdM login and their password from the end system.
The end system is defined in the configuration attribute (the UUID of the end system):
# ID system against which to authenticate idm.sec.security.auth.systemId=
First, the authenticator tries to find an end system with given UUUID. If such system doesn't exist, the authentication returns NULL. Otherwise, the authenticator finds the mapped attribute of the system which is marked as Authentication attr.
.
Authentication attr.
in the system mapping, the identifier attribute is used by default.
Next, the authenticator finds the identity for given login. All accounts of this identity for the given system are processed and the method Authenticate is called for them one-by-one. The processing is stopped after the first successful authentication.
The result type of this authenticator is SUFFICIENT. It's order is 10, which means that this authenticator would be processed after DefaultCoreAuthenticator
Password change & old password
When users want to change their password in IdM, they will be required to fill their old password (unless the configuration attribute requireOldPassword is set differently, see below). It's possible that their local password in CzechIdM is distinct from their password in end systems. In such situation, users must use the password which satisfies the authentication chain (the same authentication chain that is used during authentication - same rules, same order of processing). If the (old) password is validated successfully, users can change their password.
The configuration attribute idm.pub.core.identity.passwordChange.requireOldPassword= determines whether the users are required to fill in the old password when changing their password. The possible values are:
- true - the old password is required,
- false - the old password is not required.