Sunday, March 3, 2013

How to Create Custom Authentication Handler in CQ

Use Case:
  • You want to use custom Authentication handler instead of OOTB one for authentication. 
  • Custom User Registration
Pre requisite:
Available Authentication Handler in CQ:



How to create your Own:


1) Create custom class extending Sling Authentication Handler and override available methods


2) Create a form (Your custom Login form) 

which will be something like this

String action = currentPage.getPath() +"/j_mycustom_security_check";

<form method="POST" action="<%= xssAPI.getValidHref(action) %>"> 
Enter User Name: <input  name="j_username" type="text" />
Enter Passord: <input  name="j_password" type="text" />
<input type="button" name="Click Here to login">
</form>

You can also use Ajax post or something to see if response is 200 (Which mean successful login)

3) Then under apache sling post servlet, Make sure that you allow parameter you are posting. In this case j_*


4) Add your custom authentication prefix to sling authenticator service


5) Once you have your bundle deployed, You should see your additional authentication handler. 




Integrate it with Custom Pluggable Login Module (AEM 6)

Step1 : create pluggable login Module


Step2 : Plug it in your custom auth handler



Example: https://svn.apache.org/repos/asf/sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/

Example of Open Source Extended Authentication Handler:



CQ OOTB Extended authentication Handler

Day CRX Sling - Token Authenticationcom.day.crx.sling.crx-auth-token)Adobe Granite SSO Authentication Handlercom.adobe.granite.auth.sso)Day Communique 5 PIN Authentication Handlercom.day.cq.cq-pinauthhandler

There are a lot of things needed for creating your custom user registration process (You might / Might not) Need following,

1) Custom Login Module http://www.wemblog.com/2012/06/how-to-add-custom-login-module-in-cq55.html to sync users / group in CQ from third party system
2) Custom Authentication handler as above
3) Reverse replication to sync user across (If user registration is in publish)

Note: Above code is just Pseudo code. Please test, You might have to add your custom logic for this to work.

Let me know if you have any question or comment.

38 comments:

  1. Hi Yogesh,

    I really like this article and this blog, so thank you for putting this information together and taking the time to maintain and update this blog. I just wanted to call out one point I noticed:

    "And more ..... If you can decompile these bundle, You should be able to see example of how it is implemented."

    If a client decompiles an OOTB proprietary jar, then technically they're in violation of their license agreement and could lose support and/or license. As such, I would really not suggest decompiling the jars, even if they are good examples.

    That being said, again I really do like this blog and appreciate the effort.

    ReplyDelete
    Replies
    1. Doug,

      Thank you very much for your comment. When I said decompiled, I meant Open source (Sling) authentication handler (Including form based). But I can see confusion here. Thus deleted that portion.

      Again thanks for review.

      Yogesh

      Delete
  2. Hi Yogesh,

    This post is what I was looking for. I am bit confused about some of the parameters and methods you have defined in the code though. Constant ATTR_HOST_NAME_FROM_REQUEST is not defined. Is it same to assume it as "hostname"?

    Regards,
    Anup

    ReplyDelete
    Replies
    1. Anup,

      You do not need that variable. This is just an example of how you can set custom attribute in credential object

      Yogesh

      Delete
  3. Can we have a sample package to get into the details?

    ReplyDelete
    Replies
    1. Hello,

      Sample Package is difficult in this case because each custom authentication requirement is different. Let me know if you have any specific question.

      Yogesh

      Delete
  4. Hello Yogesh,

    Great article. If I were to create a Custom authentication module, which uses a simple CSV file for User name and Password, can I still use the OOTB login page ? I dont want users to Register and I prefer the CSV approach as it is just the list of users who I want to control access for a very short period of time.

    ReplyDelete
    Replies
    1. Silican,

      You do not need custom authentication handler to create user and group. You can use Java API to parse CSV and then Jackrabbit API to create user. Here is example of creating user and group in CQ http://wemcode.wemblog.com/user-group-management. That site also have example of how you can create Role.

      Yogesh

      Delete
  5. Hi Yogesh,

    Thank you for the above tips. I have implemented something similar in order to do some additional checks on the user who is authenticating (e.g. check their account has not expired).
    Do you know how I might then call the default AuthenticationHandler from my extractCredentials? In other words, revert to the normal "j_security_check" ? I am trying to call the default handler's extractCredentials() and return the AuthenticationInfo object if my own handler is satisfied with it's additional checks. Any ideas?

    ReplyDelete
    Replies
    1. John,
      You can just return null from custom authentication handler and then next authentication handler will pick up.

      Yogesh

      Delete
    2. Thanks Yogesh, I guess what I am asking is after I have authenticated in my custom AuthenticationHandler and return AuthenticationInfo, how do I then get CQ to create the logged in session cookie? When I return the AuthenticationInfo , it fires the authenticationSucceeded, but there is no session cookie created.

      Delete
    3. I ended up using com.day.crx.security.token.TokenUtil.createCredentials() in my authenticationSucceeded() to create the token, just in case anybody is trying to do something similar.

      Delete
    4. Thank you John Sharing this. This will work if Order of Token Auth Handler comes after your custom Auth handler.

      Yogesh

      Delete
  6. Thanks Yogesh,
    if I return null from my custom handler, wouldn't this mean that my custom conditions are not included? I want to include my conditions PLUS the default conditions handled by j_security_ check..

    ReplyDelete
  7. I have question related to same custom login approach that you have suggested. Say I want to authenticate user against DataBase, Say I do that in authenticationSucceeded and it returns true for the first time. So the Question is how do you maintain a session then? like how would you ensure access to your secure area is being authenticated against logged in user each time.

    Apologies but I am little unaware of the login token purpose. Also I dont want to maintain users in CQ Repository what to do then, how their access to repository pages will be handled?

    ReplyDelete
  8. I have question related to Auto login in CQ5.6. I have created registration form for registring new user in my site. I am using a custom post servlet for creating users.

    I want my users to get logged in once they have completed registration process.

    How can we make user logged in automatically once registration process is completed?

    ReplyDelete
    Replies
    1. Ankur,

      Try something like
      TokenUtil.createCredentials(request, response, slingRepository, userID, true) in your servlet service method.

      Maven dependency for TokenUtil:

      com.day.crx.sling
      crx-auth-token
      2.4.23
      provided


      Also this could be useful for your use case:
      http://www.cqcon.eu/content/dam/cqcon/Pr%C3%A4sentation_Antonio_Sanso.pdf

      Please confrim that this works for you.

      Delete
  9. I tried to follow the above steps to implement the Custom Authentication handler but was stuck at some point.

    1. When I implemented using the component properies "@Property(name = AuthenticationHandler.PATH_PROPERTY, value = "/")," I was not able to reach to the Custom Authentication Handler. But when I used "@Property(name = AuthenticationHandler.PATH_PROPERTY, value = "/content")" . What is the correct way to do so?

    2. Even if I reach the authenticationSucceeded() function I am obtaining a 403 Forbidden from the "j_mycustom_security_check" handler. Has anyone faced similar problem?

    3. What does the value "static final String AUTH_TYPE" symbolizes.

    Thanks for helping in advance.

    ReplyDelete
    Replies
    1. Just to add I am using CQ5.6.1 and version of jar for org.apache.sling.auth.core is 1.1.6

      Delete
    2. @HK, did you get any resolution to 403 error? I am facing the same issue

      Delete
  10. Hi Yogesh,
    I am trying to authenticate from open am , In request cookie value is available once user get authenticate from open am , We provide the filter in dispatcher like /content/mysite*. How we can aunthenticate in cq5. When we access from openam the dispatcher url it show us a prompt "Your request could not be completed becauseyou have signed out".Please let me know how to go around

    ReplyDelete
    Replies
    1. Hello Raj,

      Usually you get that error when there is some exception in login process. Can you check your error log and make sure that nothing is wrong ?

      Yogesh

      Delete
  11. Hi Yogesh,

    I am working out with LDAP Authentication, I created a login component which sends a request to your_path/j_security_check. with username and password and the user is authenticated against the LdapLoginModule i believe which is good.

    Now, if the user is authenticated i want to check whether they are authorized to use the system or not. There is a property on user profile node with which we check that.

    The problem is once i make a request to path/j_security_check it automatically logs in the user to the system. I wan to avoid that before i know if the user is authorized to use the system or not.

    Can you please let me know how can i just override the authorization mechanism.

    ReplyDelete
    Replies
    1. Just in case somebody is interested, i have got this working using form Authentication handler http://svn.apache.org/repos/asf/sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java

      I almost ended up using most parts of this handler as i wanted to avoid writing any security code because it might just have flaws.

      Since i kept my handler service ranking to be 0 and Token Authentication handler rank was higher, i ended up creating com.day.crx.security.token.TokenUtil.createCredentials() in the authenticationSucceeded method. If you happen to have custom Authentications before you login the user you can do it in authenticationSucceeded method as well.

      Note in case your form has got a resource for redirection in form of an input hidden field i saw a weird behavior where even with valid creds i was seeing INVALID_CREDNETIALS error but as soon i put a call to TokenAuth the login redirection started working.

      Delete
    2. Sam,

      Thank a lot of sharing your problem and solution in Blog. Really appreciated.

      Yogesh

      Delete
  12. Hi Yogesh,

    I am working with LDAP Authentication, Siteminder authenticates the user with credentials from LDAP and sends user data to response.

    So I want to store that user data in session bean so that I can add more details specific to that user in that bean.
    For it I created a user bean in my top header component jsp which is in every page.
    And I am trying to make the page "session=true" it sends "500: Internal Server error".

    Can you please help

    ReplyDelete
    Replies
    1. You can simply use request.getRemoteuser() right ? Not sure why you need to store this in session. Note that session is not scalable in case you are planning to use caching in future.

      Delete
  13. Hi Yogesh,

    I built my authentication handler by following your instructions and the sample code, https://github.com/davidjgonzalez/com.activecq.samples/blob/master/core/src/main/java/com/activecq/samples/slingauthenticationhandler/impl/TokenSlingAuthenticationHandler.java.

    in the method "authenticationSucceeded", I call "return false" to continue the framework. however, I see an error is reported in the error.log: org.apache.sling.auth.core.impl.SlingAuthenticator handleSecurity: AuthenticationHandler did not block request; access denied
    did I miss any configurations?

    could you please help?

    ReplyDelete
  14. Hi, I have imported org.apache.sling.auth.core.AuthUtil in the java class. But this is showing an error.
    Kindly help.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  15. In addition to the above comment

    http://sling.apache.org/apidocs/sling5/org/apache/sling/jcr/resource/JcrResourceConstants.html package dos't contains "JcrResourceConstants.AUTHENTICATION_INFO_CREDENTIALS"

    ReplyDelete
    Replies
    1. Hello Renju,

      Thanks for your Feedback. I updated document, also added how you can create your pluggable login module.

      Yogesh

      Delete
  16. is it possible to call the j_security_check from my own servlet? instead of from action

    ReplyDelete
    Replies
    1. Hello Fabian,

      What is the use case for this ? You might be able to achieve your use case using some other method.

      Yogesh

      Delete
  17. Hi Yogesh,

    I tried to follow the above steps to implement the Custom Authentication handler in CQ5.5 but was stuck at some point. I did configuration mentioned in above but Custom Authentication handler is not getting invoke. I got below warning:

    “org.apache.sling.auth.core.impl.SlingAuthenticator handleSecurity: AuthenticationHandler did not block request; access denied”.

    Can u help, will it require any other configuration and how to resolve it.

    ReplyDelete
  18. Hi Yogesh,

    Great article. I have one question here - Do we not need to create a login-tokrn cookie in the authenticationSucceeded method before sending the response as true? Otherwise how would the further requests be authenticated?

    ReplyDelete
    Replies
    1. Hello Karttik,

      Not in this case. You can have such mechanism to avoid call to server (Chose any cookie you want). In this case all request will go through authentication handler.

      Yogesh

      Delete