Custom Principal and LoginModule for Wildfly

posted by Roberto Cortez on

Did you ever had the need to implement your own custom JAAS Principal and LoginModule for you JEE application? There are a couple of reasons for it. I’ve done it in the following cases:

  • Authenticate the user using different strategies.
  • Have additional user information on the Principal object.
  • Share user information between applications using the Principal object.

Maybe you have your own specific reason, it doesn’t matter. Today’s post will guide you on how to do it for Wildfly. There are a few articles on the topic, but each of them deal with a different aspect of the problem. I got motivated to write this post to aggregate all the steps in a single article, including the Arquillian test.

Wildfly uses PicketBox for Java Application Security and already implements some handy classes to take care of the authentication of the user for you. Have a look into UsersRolesLoginModule, DatabaseServerLoginModule, LdapUsersLoginModule, BaseCertLoginModule and so on. Let’s start by creating a Maven project with the following dependency:

Next, just create a CustomPrincipal and a CustomLoginModule classes:

Note that we’re extending the org.jboss.security.SimplePrincipal present in PicketBox, but you can also implement java.security.Principal instead.

Here again we’re extending a PicketBox class, org.jboss.security.auth.spi.UsersRolesLoginModule. You can code your own login module by implementing javax.security.auth.spi.LoginModule, but I recommend to extend one of the PicketBox classes, since they already have a lot of the behaviour that you will need. org.jboss.security.auth.spi.UsersRolesLoginModule is a very simple login module that authenticates an user by matching his login and password to a file. You shouldn’t use it for production applications, but it’s very handy for prototypes.

CustomLoginModule is also overriding two methods. These are needed to access our CustomPrincipal in a JEE application. The login() method as the name says is called when the user is performing the login action, so in here we create our CustomPrincipal object. On the other hand, the getIdentity() method is called to return the Principal that corresponds to the user primary identity, so we return our own instance if the login was successful.

Ok, great. How do we test it now? We use Arquillian, JUnit and HttpUnit. Start by adding the needed Maven dependencies (these are all the project dependencies):

Note that we also included the javaee-api 7 dependency. Next, we’re creating a simple EJB to access our CustomPrincipal and a Servlet to perform the authentication:

Now the Servlet:

I think the code is self-explanatory, but we still need to wire everything together. The way we configure our login module to be used on Wildfly and on our application is by using Security Domains. You can add a Security Domain by hand using the standalone/configuration/standalone.xml and domain/configuration/domain.xml files on the Wildfly installation folder, but we’re going to do something more interesting.

Using the Command Line Interface (CLI), is really easy to make changes to the server configuration without modifying any XML. This also allows you to setup a test environment and clean up your changes in the end. To achieve that, create the following files:

jboss-add-login-module.cli

As you probably have guessed, jboss-add-login-module.cli contains the CLI commands to add our Security Domain to the Wildfly instance. We first add the Security Domain and then assign the login modules to the domain. Both commands should be able to be executed as a single command, but for some reason I was getting an error so I had to split them apart. The configuration is not available on the server unless you perform a reload command. For the login module, please note the FQN of our CustomLoginModule associated with the configuration. That’s how we wire the custom login module to the Security Domain. The configuration also references two other files: user.properties and roles.properties that are used to perform the user credential verification and load the user roles. Here are examples for both files:

user.properties

Define all valid usernames and their corresponding passwords.

roles.properties

Define the sets of roles for valid usernames.

We still need the CLI commands to remove the Security Domain at the cleanup phase:

jboss-remove-login-module.cli

Almost done? Not yet! We still need to associate our Security Domain to our JEE application so our custom code runs when we perform authentication or execute Principal related behaviour. We need the following files now:

jboss-web.xml

The previous file sets the Security Domain into Servlets.

jboss-ejb3.xml

This file sets the Security Domain in EJB’s.

Uff! Now we’re finally ready to see some action! We required a bit of setup to have this example working! Here is the test class:

That’s it! Now your servlet login method will authenticate using your custom login module and methods like getUserPrincipal() from the servlet request or getCallerPrincipal() from the EJBContext will return the CustomPrincipal instance.

Fire up a Wildfly instance and run the test using mvm test or just use your favourite IDE.

A few problems:

  • It should be possible to add the security domain using only one single CLI command, but for some reason I was getting an erro. I need to have a better look into this.
  • I couldn’t find a way to run code before and after the Arquillian deployment, so the code to add the Security Domain is inside the deployment method. I’ll try to find a way to do it.

If you want additional information, please check the following references:

If you’re too lazy to write the code on your own or just want a working sample, you can download it here. Enjoy!

Comments ( 40 )

  1. ReplyNickname ( required )

    Cool. Thanks!

  2. ReplyMike

    Thank You for this article. I have a question:
    There is boolen authenticate(HttpServletResponse response) method in HttpServletRequest interface. How can i force this method to use Your custom login module?

  3. ReplyRoberto Cortez

    Thank you for your time to read it!

    On jboss-web.xml you have there the CustomSecurityDomain which is linked to the CustomLoginModule. This should be enough.

    Hope I could help.

    • ReplyMike

      But it’s not enough. Your example gives always false at request.authenticate(response);.
      Probably I have to write some kind of authenticator. But I dont know how :(. Can You help me?

      • ReplyRoberto Cortez

        Hi Mike,

        Sorry for this late response. I coded an example for you, you can grab it here.

        So authenticate, forces the server to ask the user for authentication. So you need the web.xml to define the login-config auth method. For you to experience the login popup I didn’t add an Arquillian test, so you need to deploy it into Wildfly. Remember that you need to add the login module configuration contained in the jboss-add-login-module.cli. You can do that by running the following script in Wildfly bin folder: jboss-cli.sh –file=PATH_TO_FILE/jboss-add-login-module.cli.

        Let me know if you need anything else.

  4. Replym_karadas

    Thanks for the article. Helped me a lot to implement my own CustomPrincipal.

    • ReplyRoberto Cortez

      Hi Murat,

      I’m glad that the article helped you!

      Cheers,
      Roberto

  5. ReplyPradeep Kumar Choudhary

    Hi Roberto,

    Thanks for nice article. I am trying to execute blow snippet in the servlet

    CustomCallbackHandler callbackHandler = new CustomCallbackHandler(user,
    password);
    boolean authenticatedFlag = true;
    try {
    LoginContext loginContext = new LoginContext(“CustomSecurityDomain”,
    callbackHandler);
    loginContext.login();

    } catch (LoginException e) {
    authenticatedFlag = false;
    }
    if (authenticatedFlag)
    printWriter.println(“Authentication Success ..!”);
    else
    printWriter.println(“Authentication failed ..!”);

    But it is not working no respond on console or log file. I have keep security domain at standalone.xml

    Kindly guide.

    • ReplyRoberto Cortez

      Hi Pradeep,

      Thank you so much for your feedback.

      Can you please detail me what do you want to do? Thank you.

  6. ReplyAkash

    Nice blog Roberto. It helps in getting better understanding in terms of Custom Login Module working. However I have a very unique requirement and haven’t been able to get it to work as yet. For my requirement i have to use Authentication Method at run-time based on the URL hit by the user.
    Example for URL http:// then use Form-based authentication but when URL hit is http://?sso=true then need to call Basic Authentication. Now in previous JBoss version this was achieved by overriding the complete Tomcat Authenticator layer. For Wildfly i have been trying to do the same for undertow but it’s not working out.
    I have tried adding a CustomHandler (HttpHandler) by ServletExtension mechanism but no success. Do you have pointers as to how we can achieve run-time authentication method based on URL irrespective of what we define in web.xml or standalone.xml

    • ReplyRoberto Cortez

      Hi Akash,

      I don’t remember any pointer at the moment, but how about this:

      Create two war’s that handle the authentication differently and then redirect to each of them depending on the url. Set up SingleSignOn between the two applications and you should be good to go.

  7. Replyvinit

    Hi Roberto,
    Thanks for providing a wonderful easy to follow example. I am trying to configure JAAS on our custom web application based on JSF and running on EAP 6.0. I find it little hard because my requirement is different then traditional user-name/password combination. In my case, our application require three parameters user-name, domain and password. Basically, domain is something like a branch location where there are more than 600 possible values. So, the role of the user is determined by the selection of the domain. e.g. “John Doe” may be a “branch-manager” for “New York” domain. However, he may be a “Cashier” for “Boston” domain.

    So, on the log on screen, user is required to enter:

    1. username
    2. domain (a drop down value, preferably an AJAX call based on the value entered in the user-name field. It would be too much to scroll through over 600 domain value in drop down.)
    3. password

    Our application uses Primeface 5.x JSF. Also, we want to use AJAX to select drop down values based on username to narrow down the selection instead of showing over 600 domain values. Is it possible to implement this kind of solution using JAAS? If so, how do I do that? So far, all the examples I have seen have only username/password using J_security_check. How, can I use j_security_check with an additional parameter “domain”?

    thanks,

    Vinit

    • ReplyRoberto Cortez

      Hi vinit,

      Thank you very much for your comment.

      There is a trick that I’ve used in the past. In the login / username fields you could send something login@domain. Then in the CustomLoginModule you can parse the login and domain that will be passed down in the username, separated with the @ character. After parsing the value, you can pretty much do whatever you want.

      I should have a sample of that around, but it’s not published. Let me know if you need a sample and I can make it available for you.

      Cheers,
      Roberto

  8. Replyvinit

    I also tried to use the example provided in this article. However, login() method of CustomLoginModule.java never get called. So, request.login(username, password) of LoginServlet.java throws ServletException. I added the following line in standalone.xml:

    and my jboss-web.xml has the following:

    CustomSecurityDomain

  9. Replyvinit

    security-domain name=”CustomSecurityDomain” cache-type=”default”>

    /security-domain

  10. Replyvinit

    authentication
    login-module code=”custom.CustomLoginModule” flag=”required”
    module-option name=”usersProperties” value=”user.properties”
    module-option name=”rolesProperties” value=”roles.properties”
    login-module
    authentication

    • ReplyRoberto Cortez

      Hi vinit,

      Weird. Did you tried the github project?

      Cheers,
      Roberto

  11. Replyvinit

    Thanks Roberto for you response!
    I did take the code from Github. The only difference was I modified standalone.xml and jboss-web.xml manually instead of CLI. Would you please upload your standalone.xml and jboss-web.xml on github?
    Thanks,

    Vinit

    • ReplyRoberto Cortez

      Hum, I usually don’t modify the standalone.xml directly. What you can do is to comment the method com.cortez.wildfly.security.CustomLoginModuleTest#removeSecurityDomain and run the sample. The changes made by the CLI should be stored in the standalone.xml and you can check them out.

      Hope it helps!

      Cheers,
      Roberto

  12. ReplyRasmus

    Thanks for the article. In JBoss 7 and earlier you could store session data in a custom Principal and expect to get the same object back each time you called sessionContext.getCallerPrincipal() or request.getUserPrincipal(). In WildFly 9.0.2 it looks like a new instance of the custom Principal is returned with every call, making it useless for storing session data. My question is whether this is a bug or were principals never intended to be used for storing session data?

    Thanks,
    Rasmus

    • ReplyRoberto Cortez

      Hi Rasmus,

      Thank you for your feedback.

      In my opinion, Principals need to be used carefully. They shouldn’t be used to store session data, just for the single reason that in any given second the credentials of the user might change and you might have a security hole if you keep a cached version of the last successful authentication.

      Regarding the change between JBoss 7 and Wildfly, I’m not sure if the change was made on purpose or just a bug. I remember that there was a configuration that allowed you to control that, but don’t remember exactly which one. Maybe it’s related.

  13. ReplyEric Eng

    Thanks for the article, I am trying to do this in wildfly 9 overriding the Database login module. However, I am deploying the login module as a jboss module and including a module.xml. Are you aware of steps for doing this. I am getting a transaction exception during the login, and that probably because my module doesn’t have access to the classes it needs.

    • ReplyEric Eng

      I did get it to work by copying all the dependencies of picketbox into my custom module.

      • ReplyRoberto Cortez

        Hi Eric,

        Cool! Thank you for sharing your problem and your solution 🙂

  14. ReplyBill

    Thanks for the tutorial man. I want to write a Loginmodule to use with MongoDB, I don’t know how to start, could you give me some suggestions please?

  15. Replyasd

    Why you not use JAX-RS client api to test?

    • ReplyRoberto Cortez

      Hi asd,

      Good point! I guess I didn’t remember it when I wrote the code 🙂

      Cheers,
      Roberto

  16. Replyremi

    Hi Roberto thanks for this article which helped me to figure out how to enable JAAS on Wildfly months ago.
    I currently use a Database module to enable JAAS for JAX-RS services deployed to Wildfly and it works fine.
    Now, I’d like to enable OAUTH2 in my application using JAAS, but I could not find yet any existing module to do that with Picketbox.
    I was thinking about implementing a custom login module, just like in your article, using Picketlink OAUTH2 client, and maybe do a pull request to Picket box project.
    What do you think about that? Does it make sense?
    Thanks for your feedback,
    Rémi

    • ReplyRoberto Cortez

      Hi Remi,

      I never usar OAuth2 with Wildfly, so I’m not sure if this works, but have a look here:
      http://keycloak.jboss.org

      Maybe it can help before you have to write your own implementation.

      Cheers,
      Roberto

  17. Replyremi

    Thanks Roberto, indeed keycloak suits my needs. Too bad it’s not part of Wildfly 10 yet.

    • ReplyRoberto Cortez

      Cool, I’ve never done it, but you should be able to use it, even in Wildfly 10.

  18. Replykawakami

    Hello. Thank you very much for this post.

    Do I need to make a jar for CustomLoginModule and CustomPrincipal?
    or these classes can be included in an web application project for WildFly?

    • Replykawakami

      they can be included in web application project.
      It works. Thank you.

      • ReplyRoberto Cortez

        You can use them either way 🙂

        Could be interesting to have them in a separate JAR if you share them with multiple apps.

  19. ReplyTri

    Hi Roberto,

    many thanks for a good post. I have 2 questions:

    1. In case the Servlet and EJB are in 2 separate JEE containers, do we need to include the custom implementation file (e.g., “com.cortez.wildfly.security.CustomLoginModule”) on each container?

    2. I see some login implementation like the OpenAM Policy Agent. It is a Servlet Filter that intercepts SAML response, extracts user attributes from the SAML assertion and login to the container. The thing is it does not create a custom Principal (to hole additional user attributes from the SAML assertions), but use a default Java Principal and pass additional attributes as HTTP headers further to the web application. My question is, do we need to implement a custom Principal, since we can store additional user attributes in Httpservletrequest? What is your opinion?

    Many thanks

    • ReplyRoberto Cortez

      Hi,

      1 – Yes, you need the custom classes in a separate jar and then jar needs to be present in all containers that are going to use them.

      2 – Either approach works. In my opinion the Custom Principal is cleaner since it clearly identifies additional attribute to your Principal and its extendable to multiple app. Or you can have specific Principals per app.

  20. Replynk

    Hi Roberto. Is it possible to use @Autowired spring annotation in the custom login module?

    • ReplyRoberto Cortez

      Unfortunately no, because it would need to be a Spring Bean to be able to @Autowired. If you need to use Spring Bean, you can just programatically grab the Spring Context and retrieve the bean from there.

  21. ReplyMavis

    Have you ever thought about writing an e-book or guest authoring on other sites?
    I have a blog based on the same subjects you discuss and would really like to have
    you share some stories/information. I know my
    audience would value your work. If you are even remotely interested, feel free
    to send me an e-mail.

Leave a Reply to Roberto Cortez Cancel reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>