Thursday, June 7, 2012

How to add a custom Login Module in CQ / AEM

Use Case:

1) You want to create a custom login module in CQ / AEM
2) You already have a custom login module in CQ5.4 and integrate it with CQ5.5

Prerequisite: Please check http://dev.day.com/content/kb/home/cq5/CQ5Troubleshooting/cq55prerelease-installandconfigchanges.html

Solution:

CQ 5.6 and less

Any custom login module should be added in CQ5.6 and less as a JAAS module. That mean CQ should have access to module during load time.

Here is set of steps you might have to do to create custom login module

1) Create a OSGI bundle with class that extend Jackrabbit AbstarctLoginModule (Example http://wemcode.wemblog.com/custom-login-module)

2) Create bundle as frangment bundle attached it to com.day.crx.sling.server



<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>!com.day.crx.core.token,!org.apache.jackrabbit.*,*</Import-Package>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>....... Package you want to export .... </Export-Package>
<Fragment-Host>com.day.crx.sling.server</Fragment-Host>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-Description>${project.description}</Bundle-Description>
<Bundle-Version>${project.version}</Bundle-Version>
<Include-Resource>lib=${basedir}/lib</Include-Resource>
<Bundle-ClassPath>
.,
lib/<Your custom jar file>.jar,
</Bundle-ClassPath>
</instructions>
</configuration>
</plugin>

3) Deploy and start your bundle as start level 15. You can do following things for that

curl -u admin:admin -T <Your custom module bundle>.jar http://<host>:<port>/apps/<Your path>/install/15/<Your login module>.jar

Or


<profiles>
<profile>
<id>install-packages</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.sling</groupId>
<artifactId>maven-sling-plugin</artifactId>
<executions>
<execution>
<id>install-package</id>
<goals>
<goal>install</goal>
</goals>
<configuration>
<slingUrl>${crx.url}/system/console/install</slingUrl>
<user>${crx.user}</user>
<password>${crx.password}</password>
<bundleStartLevel>15</bundleStartLevel>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

4) Change repository.xml 

Open the file crx-quickstart/repository/repository.xml (on the server's filesystem) and remove the <LoginModule>...</LoginModule> element.
Find the <SecurityManager> element and add the WorkspaceAccessManager as shown below:
<SecurityManager class="com.day.crx.core.CRXSecurityManager">
<WorkspaceAccessManager class="org.apache.jackrabbit.core.security.simple.SimpleWorkspaceAccessManager"/>
<UserManager class="com.day.crx.core.CRXUserManagerImpl">
<param name="usersPath" value="/home/users"/>
<param name="groupsPath" value="/home/groups"/>
<param name="defaultDepth" value="1"/>
</UserManager>
</SecurityManager>

5) Create custom jaas config

Now create the file crx-quickstart/conf/jaas.conf. In this file you would have something like this:

com.day.crx {
<Your login Module class name> sufficient
com.day.crx.core.CRXLoginModule sufficient;
};

6) Change start up script

Update your crx-quickstart/bin/start script with this jvm parameter (Replace /path/to/your/jaas.conf with the real path to the jaas.conf file):
-Djava.security.auth.login.config=/path/to/your/jaas.conf

7) Restart your CQ

Note: if you already have Non osgi version of your custom login module, You can use http://www.wemblog.com/2012/04/how-to-integrate-3rd-party-jar-file-in.html to convert to OSGI bundle. Make sure that you follow step 2.

To debug custom login module, you can follow steps mention in http://www.wemblog.com/2011/09/how-to-set-up-debug-mode-for.html

Note that LDAP login module com.day.crx.security.ldap.LDAPLoginModule in CQ is good example of custom Login Module.

AEM / CQ 6

From CQ6 onward, Login module can now be configured as pluggable login module in OSGI. To implement Login Module in CQ6 onward you can

Option 1:

implement org.apache.sling.jcr.jackrabbit.server.security.LoginModulePlugin Here is example of Pluggable Login Module with Authentication handler http://svn.apache.org/repos/asf/sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/

More info: https://sling.apache.org/documentation/the-sling-engine/authentication/authentication-authenticationhandler/form-based-authenticationhandler.html

Option 2:

You can also use OAK external Login Module https://github.com/apache/jackrabbit-oak/tree/trunk/oak-auth-external To create custom login module (Which you register and service) as well. Example of that would be LDAP Login Module in AEM6 https://github.com/apache/jackrabbit-oak/tree/trunk/oak-auth-ldap

Here is one more example of how to use this in AEM 6 onward: https://helpx.adobe.com/experience-manager/using/oak-login.html


You do not have to restart your instance after creating custom login module in AEM 6.

19 comments:

  1. Hi,
    Does this work for CQ5.4 too? Or do you know how to write a custom login module for CQ5.4?

    Kind regards, Christoph

    ReplyDelete
    Replies
    1. I doubt (As in CQ5.4 CRX starts up before launchpad and hence OSGI). In CQ5.4 you have to put non OSGI version of jar file under crx libs directory in file system.

      Delete
  2. I'm confused of Sling AuthenticationHandler Interface with Custom Login Module. Are they the same thing, or are they completely different for what they are meant to solve?

    ReplyDelete
    Replies
    1. Charles,

      There are two things,
      1) Login Module (Example LDAP Login Module in CQ, Used for user synching and Password verification)
      2) Authentication Handler (Example SlingAuthentication handler, FormBasedAuthenticationHandler etc in CQ)

      Sling Authentication handler is one of the example of custom Authentication Handler that CQ uses. Here is source code for that http://svn.apache.org/repos/asf/sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/

      So suppose you have third party system to get user/group data then,
      1) you will use Login Module to sync data from third party system
      2) and custom authentication handler to authenticate against your third party system

      Hope it make sense

      Yogesh

      Delete
    2. Thanks Yogesh. To further understand your points, what should I read to know more about how exactly a Login Module works and how to develop one? and how adding a Login Module to CQ means in terms of its interaction with Authentication Handler. I think I'm lack of the overall basic picture thus having difficulty understanding.

      Say I'd like to have a 3rd party (instead of CQ logon page) to authentication users (authors, writers, etc.). On success, redirect the authenticated user back to CQ with the user id and from there the CQ will continue to authorize the user according to the ACL in CRX. How would I achieve that?

      Delete
    3. Charles,

      You can have your custom login module to interact with third party system to sync user / Group, For that follow above process and override AbstractLoginModule methods to have custom user / group sync.

      I will create a post for creating custom authentication handler soon.

      Yogesh

      Delete
    4. See http://www.wemblog.com/2013/03/how-to-create-custom-authentication.html

      Yogesh

      Delete
  3. Hi Yogesh,

    Can you also provide a sample for LDAP configuration with CQ in your style? you make it really easy to understand things?

    ReplyDelete
    Replies
    1. Runal,

      Sample "Login Module" conf will look like this

      com.day.crx {
      com.day.crx.core.CRXLoginModule sufficient
      disableNTLMAuth="true"
      tokenExpiration="43200000";
      required
      autocreate.user.emailAddress="profile/email"
      autocreate.user.firstName="profile/givenName"
      autocreate.user.lastName="profile/familyName"
      autocreate.user.companyName="profile/companyName"
      autocreate.user.ID="profile/myid"
      autocreate.user.locale="preferences/toolboxLanguage"
      autocreate.user.customerID="profile/organizationId"
      cacheMaxSize="10000"
      cache.expiration="86400"
      cache.maxsize="6000";
      };

      Delete
  4. This is nice article. It seems we can configure multiple LoginModule but how do we configure which module to use for different authentication handler.

    ReplyDelete
    Replies
    1. Hello,

      You need to bind your login module to appropriate authentication handler for this. That happen in activate method of authentication handler.

      Here is example,

      @Activate
      protected void activate(ComponentContext componentContext) {
      context = componentContext;
      Dictionary props = context.getProperties();
      facebookIdAttribute = OsgiUtil.toString(props.get(PROP_FACEBOOK_ID_USER_ATTR), DEFAULT_FACEBOOK_ID_USER_ATTR);

      this.loginModule = null;
      try {
      this.loginModule = FaceBookLoginModule.register(this, componentContext.getBundleContext());
      } catch (Throwable t) {
      log.info("Cannot register FaceBookLoginModulePlugin. This is expected if Sling LoginModulePlugin services are not supported");
      log.debug("dump", t);
      }

      }

      Delete
  5. My requirement is, user will enter userid in CQ based form and this userid will be validated by 3rd party system using REST call from CQ. Now once user is validated in 3rd party system then user should be able to login to CQ. We do not store user information in CQ.

    So, for the purpose of implementation I was thinking about a custom login module or a custom authentication handler, but I am confused on which one to implement.

    Best regards,
    Sam

    ReplyDelete
    Replies
    1. Hello Sam,

      This is common use case. In this case you will authenticate against third party system using protocol they are using and then skip sync using login module. Here is relevant example http://www.wemblog.com/2013/03/how-to-create-custom-authentication.html pay attention to authSucceded method.

      Yogesh

      Delete
  6. Hi,

    Looking to configure Login Module in AEM6. http://svn.apache.org/repos/asf/sling/trunk/bundles/auth/openid/src/main/java/org/apache/sling/auth/openid/impl/ is not working. Can you please provide an alternative.

    ReplyDelete
    Replies
    1. Here is example http://www.wemblog.com/2012/04/how-to-use-ldap-with-cq55.html check AEM 6 section. Note that LDAP is one of OOTB login module. If you have custom login module then you have to create first and then register it using above method.

      Yogesh

      Delete
  7. Hi Yogesh,
    thanks a lot for your article!
    Is it also possible to authenticate external Users and dont sync them with the Repository, e.g. prevent Synchronization?

    ReplyDelete
    Replies
    1. Hello,

      Yes, In that case you would just use authentication handler as mentioned in http://www.wemblog.com/2013/03/how-to-create-custom-authentication.html

      Yogesh

      Delete
    2. Hello Yogesh,
      many thanks for your reply!
      Is this also the solution to accept Users from 3rd Party, e.g having stored them an own node of JCR-Repository?
      The LoginModulePlugin is not supported by AEM. Do you have another solution?

      Delete
  8. Hello Yogesh, thanks for this article.

    This is the use case: Client does have an authentication system exposed via SOAP. I have imported the WSDL to generate the Java classes.

    I am following Option 2 for AEM 6.1. That is, using a custom Oak Login Module, since I do not want to override the default login form.

    However, I am lost in how to tell AEM to use my Custom Login Module, because I do not know what to specify as value for Identity Provider Name (in http://localhost:4502/system/console/configMgr). Besides this line is not being printed: log.info("######## Inside Custom External Login Module Login Method #######");

    Helpx says that in order to test the module, AEM should be configured for LDAP, but, how I configure a SOAP service?

    Or what approach do you suggest in this case? Thanks.

    ReplyDelete