As authentication is pretty much the core functionality of OpenAM, I believe it is helpful to have a good understanding of how it works really. For starters let’s have a look at the different concepts around authentication.
Authentication modules are just a simple piece of functionality that is meant to identify the user by some means. Depending on your requirements an authentication module could verify user credentials, or perform some kind of two factor verification process. For more complex use-cases you could use the auth module to collect informations about the end-user and with the help of a fraud-detection system determine if the current login attempt is “risky”.
In any case, the authentication modules are performing some (customizable) logic, and at the end of the module processing you can either ignore the current authentication module (neither a success nor a failure), OR succeed with a logged in user, OR just simply fail (invalid credentials, etc).
The authentication module implementations are JAAS based (with some abstraction on top of plain JAAS), so it is all based on callbacks (think of callbacks as input fields) that needs to be “handled” and submitted. When the callbacks are submitted, the AMLoginModule’s #process gets invoked with the callbacks. This is the time when the authentication module can start to process the submitted data and determine if the authentication attempt was successful. Since an authentication process can involve multiple steps (more than one set of callbacks to submit: for example requesting username, and than some verification code), the #process method needs to return a number that represents the next state (there are special numbers like ISAuthConstants.LOGIN_SUCCEED that represent successful authentication result, i.e. no further need to present callbacks), which then will be used to determine the next set of callbacks to display on the UI. Assuming that the authentication finished successfully, we need to return the magic LOGIN_SUCCEED state.
So how does OpenAM really know who the user is?
Once the authentication is successful, the auth framework will call AMLoginModule#getPrincipal which needs to return the authenticated user’s principal. #getPrincipal has a key role in the authentication process, so make sure its implemented correctly (or when using built-in modules, make sure they are configured correctly).
The next sensible building blocks are the authentication chains. The auth chains can be considered as combinations of various authentication modules to present a single authentication procedure for the end-users. Following the previous example, one could think that checking some verification code after providing a username and password may not be actually the job of a single authentication module, and probably should be implemented separately. In that case one could implement one module for username/password login, and then implement an another module for code verification. To make sure the user logs in using both auth modules, one could create an authentication chain that includes both of them, and then the user will just need to authenticate against that chain.
Since the modules are JAAS-based, it makes sense to set up the chain configurations similarly to JAAS as well, but I’m not going to go into too much details on that front, instead you should just read about JAAS a bit more (especially about the “flags”).
Once the user has successfully authenticated, there is a thing called “profile lookup”. This bit is all about trying to find the logged in user in one of the configured data stores, and then ensure that the user-specific settings (things like custom idle/max timeout values, or session quota even) are all applied for the just-to-be-created session. There are other additional checks as well, like ensuring that the logged in user actually has an active status in the system (e.g. doesn’t have a locked account).
To make things a bit more clearer let’s talk about User Profile Mode now (Access control – realm – Authentication – All Core Settings). The user profile mode tells what should happen at the profile lookup stage of the authentication, and these are the possible modes:
- Required – this is the default mode, which just means that the user profile MUST exist in the configured data store.
- Ignored – the user profile does not have to exist, the user profile will not be looked up as part of the authentication process.
- Dynamic – the profile will be looked up, but if it doesn’t already exist, it will be dynamically created in the data store.
- Dynamic with User Alias – this is similar to Dynamic, but it also appears to store user alias attributes in the newly created entries (I must admit I don’t fully understand this mode yet).
I believe it is important to stop here a bit and emphasize the following:
The authentication module may interact with arbitrary external components during the authentication phase, however when it comes to profile lookup, that will be always performed against the configured data stores. If you are running into the infamous “User has no profile in this organization” error message, then that means, that the authentication was successful, but the profile lookup failed, since OpenAM was unable to find the user in the configured data stores.
The profile lookup itself is performed based on the return value of #getPrincipal, so this is why it is really important to ensure that the module works correctly. The returned principal can be simply a username like “helloworld”, but it can also have a DN format like “uid=helloworld,ou=people,dc=example,dc=com” (see LDAP module’s Return User DN to DataStore setting). When the returned value is a DN, then the RDN value will be used for the data store search (so helloworld), hence it is important to ensure that the data store has been configured to search users based on the attribute that is expected to have the value of helloworld.
The idea behind all of this is that in #getPrincipal the username returned should be unique across the user base, so even if let’s say you allow someone authenticating with “John Smith”, you should still return a bit more meaningful username (like jsmith123) to the backend. That way you can ensure that when you ask for “John Smith”‘s user details, you will get the right set of values.
Post authentication actions
After a successful profile lookup there are various additional things that OpenAM does, but I’m not really going to go into the nifty details for those. Here’s a small list of things that normally happens:
- Check if user account is active.
- Check if the account is locked (using OpenAM’s built-in Account Lockout feature).
- Check if there are user-specific session settings configured for the user, and apply those values for the newly created session.
- When the user session is created, check if the session quota has been exhausted and run the corresponding quota exhaustion action if yes.
- Execute the Post Authentication Processing plugins.
- Determine the user’s success login URL and also ensure that the goto URL is validated.
Whilst we only discussed portions of the actual authentication process, I think the main concepts for authentication are laid out, so hopefully when you need to configure OpenAM the next time around, you can reuse the things learned here. 🙂