The purpose of the external login module is to provide a base implementation that allows easy integration of 3rd party authentication and identity systems, such as LDAP. The general mode of the external login module is to use the external system as authentication source and as a provider for users and groups that may also be synchronized into the repository.
what it does:
what it does not:
The external identity and login handling is split into 3 parts:
This modularization allows to reuse the same external login module for different combinations of IDPs and synchronization handlers. Although in practice, systems usually have 1 of each.
An example where multiple such entities come into play would be the case to use several LDAP servers for authentication. Here we would configure 2 LDAP IDPs, 1 Sync handler and 2 ExtLMs.
The external login module has 2 main tasks. One is to authenticate credentials against a 3rd party system, the other is to coordinate syncing of the respective users and groups with the JCR repository (via the UserManager).
If a user needs re-authentication (for example, if the cache validity expired or if the user is not yet present in the local system at all), the login module must check the credentials with the external system during the login() method.
The details of the default user/group synchronization mechanism are described in section User and Group Synchronization : The Default Implementation
As of Oak 1.5.1 the ExternalLoginModule can deal for any kind of Credentials implementations. By default (i.e. unless configured otherwise) the module supports SimpleCredentials and thus behaves backwards compatible to previous versions.
Additional/other credentials can be supported by providing an ExternalIdentityProvider that additionally implements the CredentialsSupport interface. See section Pluggability for instructions and an example.
The details of the external authentication are as follows:
Phase 1: Login
Phase 2: Commit
See section Example Configurations for some common setup scenarios.
The ExternalLoginModule is designed to work with a pluggable ExternalIdentityProvider implementation that is responsible for validating the authentication request and provide information about the user that is associated with the specified credentials.
See External Identity Management for further information regarding the identity management API defined by Oak. Section LDAP further describes the LDAPIdentityProvider implementation shipped with Oak.
The synchronization of users and groups is triggered by the external login module, after a user is successfully authenticated against the IDP or if it’s no longer present on the IDP.
See section User Synchronization for further details and a description of the default implementation.
The external authentication module comes with the following configuration parameters for the ExternalLoginModuleFactory/[ExternalLoginModule].
Parameter | Type | Default | Description |
---|---|---|---|
PARAM_IDP_NAME | String | - | Name of the external IDP to be retrieved from the ExternalIdentityProviderManager |
PARAM_SYNC_HANDLER_NAME | String | - | Name of the sync handler to be retrieved from the SyncManager |
Optional (OSGi-setup) | |||
JAAS_RANKING | int | 50 | Ranking of the ExternalLoginModule in the JAAS configuration, see LoginModuleFactory |
JAAS_CONTROL_FLAG | String | SUFFICIENT | See LoginModuleControlFlag for supported values. |
JAAS_REALM_NAME | String | - | See LoginModuleFactory |
The following JAAS configuration shows how the ExternalLoginModule could be used in a setup that not solely uses third party login (Note: JAAS configuration equivalents of the parameters defined by org.apache.felix.jaas.LoginModuleFactory are omitted):
jackrabbit.oak { org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule sufficient; org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl sufficient; org.apache.jackrabbit.oak.spi.security.authentication.external.impl.ExternalLoginModule required sync.handlerName="default" idp.name="ldap"; };
The design of the ExternalLoginModule allows for customization of the key features associated with third party authentication. In an OSGi-based setup these are covered by references within the ExternalLoginModuleFactory:
The default implementations (ExternalIDPManagerImpl and SyncManagerImpl) extend AbstractServiceTracker and will automatically keep track of new ExternalIdentityProvider and SyncHandler services, respectively.
Since Oak 1.5.1 support for different or multiple types of Credentials can easily be plugged by providing an ExternalIdentityProvider that additionally implements CredentialsSupport. This is an optional extension point for each IDP; if missing the ExternalLoginModule will fall back to a default implementation and assume the IDP only supports SimpleCredentials. See details below.
The following steps are required in order to change or extend the set credential classes supported by the ExternalLoginModule:
Don’t forget to make sure that ExternalIdentityProvider.authenticate(Credentials) handles the same set of supported credentials!
@Component() @Service(ExternalIdentityProvider.class, CredentialsSupport.class) public class MyIdentityProvider implements ExternalIdentityProvider, CredentialsSupport { public MyCredentialsSupport() {} //-----------------------------------------< CredentialsSupport >--- @Nonnull @Override public Set<Class> getCredentialClasses() { return ImmutableSet.<Class>of(MyCredentials.class); } @CheckForNull @Override public String getUserId(@Nonnull Credentials credentials) { if (credentials instanceof MyCredentials) { return ((MyCredentials) credentials).getID(); } else { return null; } } @Nonnull @Override public Map<String, ?> getAttributes(@Nonnull Credentials credentials) { // our credentials never contain additional attributes return ImmutableMap.of(); } //-------------------------------------< ExternalIdentityProvider >--- @CheckForNull @Override public ExternalUser authenticate(@Nonnull Credentials credentials) { if (credentials instanceof MyCredentials) { MyCredentials mc = (MyCredentials) credentials; if (internalAuthenticate(mc)) { return new MyExternalUser(mc.getID()); } else { throw new LoginException(); } } else { return null; } } [...] //----------------------------------------------< SCR Integration >--- @Activate private void activate() { // TODO } }