Authentication
JAAS Authentication and Login Modules
General Concepts
In order to understand how login modules work and how Oak can help providing extension points we need to look at how JAAS authentication works in general and discuss where the actual credential-verification is performed.
Brief recap of the JAAS authentication
The following section is copied and adapted from the javadoc of javax.security.auth.spi.LoginModule.
The authentication process within the LoginModule
proceeds in two distinct phases,
login and commit phase:
Phase 1: Login
- In the first phase, the
LoginModule
'slogin
method gets invoked by theLoginContext
'slogin
method. - The
login
method for theLoginModule
then performs the actual authentication (prompt for and verify a password for example) and saves its authentication status as private state information. - Once finished, the
LoginModule
's login method either returnstrue
(if it succeeded) orfalse
(if it should be ignored), or throws aLoginException
to specify a failure. In the failure case, theLoginModule
must not retry the authentication or introduce delays. The responsibility of such tasks belongs to the application. If the application attempts to retry the authentication, theLoginModule
'slogin
method will be called again.
Phase 2: Commit
- In the second phase, if the
LoginContext
's overall authentication succeeded (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules succeeded), then thecommit
method for theLoginModule
gets invoked. - The
commit
method for aLoginModule
checks its privately saved state to see if its own authentication succeeded. - If the overall
LoginContext
authentication succeeded and theLoginModule
's own authentication succeeded, then thecommit
method associates the relevant Principals (authenticated identities) and Credentials (authentication data such as cryptographic keys) with the Subject located within theLoginModule
. - If the
LoginContext
's overall authentication failed (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed), then theabort
method for eachLoginModule
gets invoked. In this case, theLoginModule
removes/destroys any authentication state originally saved.
Login module execution order
Very simply put, all the login modules that participate in JAAS authentication are configured in a list and can have
flags indicating how to treat their behaviors on the login()
calls.
JAAS defines the following module flags:
(The following section is copied and adapted from the javadoc of javax.security.auth.login.Configuration)
- Required: The LoginModule is required to succeed. If it succeeds or fails, authentication still continues to proceed down the LoginModule list.
- Requisite: The LoginModule is required to succeed. If it succeeds, authentication continues down the LoginModule list. If it fails, control immediately returns to the application (authentication does not proceed down the LoginModule list).
- Sufficient: The LoginModule is not required to succeed. If it does succeed, control immediately returns to the application (authentication does not proceed down the LoginModule list). If it fails, authentication continues down the LoginModule list.
- Optional: The LoginModule is not required to succeed. If it succeeds or fails, authentication still continues to proceed down the LoginModule list.
The overall authentication succeeds only if all Required and Requisite LoginModules succeed. If a Sufficient LoginModule is configured and succeeds, then only the Required and Requisite LoginModules prior to that Sufficient LoginModule need to have succeeded for the overall authentication to succeed. If no Required or Requisite LoginModules are configured for an application, then at least one Sufficient or Optional LoginModule must succeed.
JCR API
Within the scope of JCR Repository.login
is used to authenticate a given user.
This method either takes a Credentials
argument if the validation is performed
by the repository itself or null
in case the user has be pre-authenticated by
an external system.
Furthermore JCR defines two types of Credentials
implementations:
- javax.jcr.GuestCredentials: used to obtain a “guest”, “public” or “anonymous” session.
- javax.jcr.SimpleCredentials: used to login a user with a userId and password.
The following variants exist for the repository login itself:
Repository.login()
: equivalent to passingnull
credentials and the default workspace name.Repository.login(Credentials credentials)
: login with credentials to the default workspace.Repository.login(String workspace)
: login withnull
credentials to the workspace with the specified name.Repository.login(Credentials credentials, String workspaceName)
JackrabbitRepository.login(Credentials credentials, String workspaceName, Map<String, Object> attributes)
: in addition allows to pass implementation specific session attributes.
See javax.jcr.Repository and org.apache.jackrabbit.api.JackrabbitRepository for further details.
In addition JCR defines Session.impersonate(Credentials)
to impersonate another
user or - as of JSR 333 - clone an existing session.
Oak API
The Oak API contains the following authentication related methods and interfaces
- AuthInfo: Immutable object created upon successful login providing information about the authenticated
Subject.
ContentRepository.login(Credentials, String)
: The Oak counterpart of the JCR login.ContentSession.getAuthInfo()
: exposes theAuthInfo
associated with theContentSession
.
API Extension
Oak Authentication
In the the package org.apache.jackrabbit.oak.spi.security.authentication
Oak 1.0
defines some extensions points that allow for further customization of the authentication.
LoginContextProvider
: Configurable provider of theLoginContext
(see below)LoginContext
: Interface version of the JAAS LoginContext aimed to ease integration with non-JAAS componentsAuthentication
: Aimed to validate credentials during the first phase of the (JAAS) login process.
In addition this package contains various utilities and base implementations. Most notably an abstract login module implementation (AbstractLoginModule) as described below and a default implementation of the AuthInfo interface (AuthInfoImpl).
Abstract Login Module
This package also contains a abstract LoginModule
implementation (AbstractLoginModule)
providing common functionality. In particular it contains Oak specific methods that allow
subclasses to retrieve the SecurityProvider
, a Root
and accesss to various
security related interfaces (e.g. PrincipalManager
).
Subclasses are required to implement the following methods:
getSupportedCredentials()
: return a set of supported credential classes. See also section Supported Credentialslogin()
: The login method defined byLoginModule
commit()
: The commit method defined byLoginModule
Example: Extending AbstractLoginModule
public class TestLoginModule extends AbstractLoginModule {
private Credentials credentials;
private String userId;
private Set<? extends Principal> principals;
@Nonnull
@Override
protected Set<Class> getSupportedCredentials() {
return Set.of(TestCredentials.class);
}
@Override
public boolean login() throws LoginException {
credentials = getCredentials();
if (validCredentials(credentials)) {
this.credentials = credentials;
this.userId = getUserId(credentials);
this.principals = getPrincipals(userId);
return true;
}
return false;
}
@Override
public boolean commit() throws LoginException {
if (credentials != null) {
if (!subject.isReadOnly()) {
subject.getPublicCredentials().add(credentials);
if (principals != null) {
subject.getPrincipals().addAll(principals);
}
AuthInfo authInfo = new AuthInfoImpl(userId, Collections.EMPTY_MAP, principals);
setAuthInfo(authInfo, subject);
}
return true;
}
return false;
}
}
Supported Credentials
Since Oak 1.5.1 the extensions additionally contain a dedicated interface that
eases the support for different Credentials
in the package space
org.apache.jackrabbit.oak.spi.security.authentication.credentials
:
- CredentialsSupport: Interface definition exposing the set of supported
Credentials
classes and some common utility methods. - SimpleCredentialsSupport: Default implementation for the widely used
SimpleCredentials
Oak Authentication Implementation
A description of the various requirements covered by Oak by default as well as the characteristics of the corresponding implementations can be found in section Authentication: Implementation Details.
See section differences for comprehensive list of differences wrt authentication between Jackrabbit 2.x and Oak.
Configuration
The configuration of the authentication setup is defined by the AuthenticationConfiguration. This interface provides the following method:
getLoginContextProvider()
: provides the login contexts for the desired authentication mechanism.
JAAS Configuration Utilities
There also exists a utility class that allows to obtain different
javax.security.auth.login.Configuration
for the most common setup [11]:
ConfigurationUtil#getDefaultConfiguration
: default OAK configuration supporting uid/pw login configuresLoginModuleImpl
onlyConfigurationUtil#getJackrabbit2Configuration
: backwards compatible configuration that provides the functionality covered by jackrabbit-core DefaultLoginModule, namely:GuestLoginModule
: null login falls back to anonymousTokenLoginModule
: covers token based authenticationLoginModuleImpl
: covering regular uid/pw login
Pluggability
The default security setup as present with Oak 1.0 is able to provide custom implementation on various levels:
- The complete authentication setup can be changed by plugging a different
AuthenticationConfiguration
implementations. In OSGi-base setup this is achieved by making the configuration a service. In a non-OSGi-base setup the custom configuration must be exposed by theSecurityProvider
implementation. - Within the default authentication setup you replace or extend the set of login modules and their individual settings. In an OSGi-base setup is achieved by making the modules accessible to the framework and setting their execution order accordingly. In a non-OSGi setup this is specified in the JAAS config.
Examples
Custom LoginModule in non-OSGi setup
import javax.security.auth.login.AppConfigurationEntry
import javax.security.auth.login.Configuration;
AppConfigurationEntry[] entries = new AppConfigurationEntry[]{new DefaultEntry(options)};
Configuration c = new Configuration() {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String applicationName) {
Map<String, ?> options = [....];
// choose control flag for custom login module (example here: REQUIRED)
AppConfigurationEntry.LoginModuleControlFlag flag = LoginModuleControlFlag.SUFFICIENT;
// create an entry for your custom login module
AppConfigurationEntry customEntry = new AppConfigurationEntry("your.org.LoginModuleClassName", flag, options)
// additionally use the oak default login module
AppConfigurationEntry defaultEntry = new AppConfigurationEntry(("org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl", LoginModuleControlFlag.REQUIRED, options)
// define array of all entries in the correct order according to your needs
return new AppConfigurationEntry[]{customEntry, defaultEntry};
}
};
Configuration.setConfiguration(c);