Oak provides two different mechanisms to create pre-authentication that doesn't involve the repositories internal authentication mechanism for credentials validation.
This first variant allows to support 3rd party login modules that wish to provide the login context with pre authenticated login names, but still want to rely on the rest of the Oak's login module chain. For example an external SSO login module can extract the userid from a servlet request and use it to authenticate against the repository. But instead of re-implementing the user lookup and subject population (and possible external user synchronization) it just informs any subsequent login modules that the credential validation was already successful.
The key to understand this mechanism is the PreAuthenticatedLogin marker class, which is pushed to the shared state of the login context and which indicates to any subsequent LoginModule that the credentials present in the state already have been verified and thus can be trusted.
This setup is particularly recommended in a OSGi setup that includes Apache Sling on top of the Oak repository but still requires user information to be synchronized into the repository.
The basic steps of the pre-authentication in combination with regular JAAS login module chain are outlined as follows:
PreAuthenticatedLogin
and other information required and processed by subsequent login modules (e.g. credentials and user name).PreAuthenticatedLogin
and the additional information and will properly populate the subject and optionally synchronize user information or create login tokens.Example implementation of LoginModule#login
that pushes the PreAuthenticatedLogin
marker to the shared state:
public class PreAuthLoginModule extends AbstractLoginModule { [...] @Overwrite public boolean login() throws LoginException { Credentials credentials = getCredentials(); if (credentials instanceof MyPreAuthCredentials) { userId = ((MyPreAuthCredentials) credentials).getUserId(); if (userId == null) { log.debug("Could not extract userId/credentials"); } else { sharedState.put(SHARED_KEY_PRE_AUTH_LOGIN, new PreAuthenticatedLogin(userId)); sharedState.put(SHARED_KEY_CREDENTIALS, new SimpleCredentials(userId, new char[0])); sharedState.put(SHARED_KEY_LOGIN_NAME, userId); log.debug("login succeeded with trusted user: {}", userId); } } [...] // subsequent login modules need to succeed and process the 'PreAuthenticatedLogin' return false; } @Overwrite public boolean commit() { // this module leaves subject population to the subsequent modules // that already handled the login with 'PreAuthenticatedLogin' marker. return false; } }
Like in Jackrabbit-core the repository internal authentication verification can be skipped by calling Repository#login()
or Repository#login(null, wspName)
. In this case the repository implementation expects the verification to be performed prior to the login call.
This behavior is provided by the default implementation of the LoginContextProvider
[1] which expects a Subject
to be available with the current java.security.AccessControlContext
. However, in contrast to Jackrabbit-core the current implementation does not try to extend the pre-authenticated subject but skips the internal verification step altogether.
Since the LoginContextProvider
is a configurable with the authentication setup OAK users also have the following options by providing a custom LoginContextProvider
:
Subject
.JaasLoginContext
org.apache.jackrabbit.oak.spi.security.authentication.LoginContext
[2] interface.Example how to use this type of pre-authentication:
String userId = "test"; /** * Retrive valid principals e.g. by using Jackrabbit or Oak API: * - PrincipalManager#getPrincipal and/or #getGroupMembership * - PrincipalProvider#getPrincipals(String userId) */ Set<? extends Principal> principals = getPrincipals(userId); AuthInfo authInfo = new AuthInfoImpl(userId, Collections.<String, Object>emptyMap(), principals); Subject subject = new Subject(true, principals, Collections.singleton(authInfo), Collections.<Object>emptySet()); Session session; try { session = Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Session>() { @Override public Session run() throws Exception { return login(null, null); } }, null); } catch (PrivilegedActionException e) { throw new RepositoryException("failed to retrieve session.", e); }