8 Replies Latest reply on Oct 17, 2012 9:37 AM by dragos.n

    JBoss 7.1.1 Webservice programatic JAAS authentication.

    dragos.n Newbie

      Hi,

       

      First of all please excuse my english and if I posted into wrong place please drive me to the right place.

       

      I have a simple POJO as a webservice something like that that is mapped to, lets say /public/authenticate :

       

      WebService
      @SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
      public class AuthWS{
      
          @WebMethod
          public boolean doAuthenticate(String securityToken) {
           ....
          }
      }
      

       

       

      This webservice doesn't requre authentication and is not a protected resource.

       

      I do have other private webservices mapped to path : /private/ws/*;

       

      For the moment I use a security-domain that has a Database login module setup. It works fine, but user first needs to authenticate trought a web form based that makes a post request to /j_security_check. Only after this step user can use other private webservices.

       

      I want to perform a programatically authentication after client calls this doAuthenticate method. So that client to be able to invoke other /private/ws/* webservice methods.

       

      I'll type what I want to achieve:

       

      WebService
      @SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
      public class AuthWS{
      
          @WebMethod
          public boolean doAuthenticate(String securityToken) {
                SomeSecurityManager manager= SomeSecurityManager.getDefaultManager()
                Map<String,Object> map = new HashMap<String, Object>();
                map.put("MY_CUSTOM_SECURITY_TOKEN",securityToken);
                manager.doLogin(map);
              // after webservice method returns, client should now be able to invoke other private webservice
              // this means that the manager should associate with this session an authenticated user.
              // in order that authorization to work.
          }
      }
      

       

      And my CustomLoginModule :

       

      class CustomLogModule implements LoginModule {
      
        ...
         public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
             // store what's needed
         } 
      
      
        public boolean login(){ 
          // get securityToken send from the SomeSecurityManager and validate it.
          // get user information from that token and store into Subject object.
        } 
      }


      And in my CustomLoginModule that implements JAAS LoginModule to check that securityToken with a custom logic, verify if it right signed with a public key for example. That securityToken contains information about principal.

       

       

      If you need more details, feel free to ask.

       

      Thanks.

        • 1. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
          dragos.n Newbie

          Hi again,

           

          Should I probably post this question to another project forum ?

          Project like : PicketBox or PicketLink maybe ?

          Please send me to the right place for getting answer.

           

          Thanks.

          • 2. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
            dragos.n Newbie

            1.) Created custom-login-module.jar together with module.xml

            <module xmlns="urn:jboss:module:1.1" name="custom.login.module">
                <resources>
                    <resource-root path="custom-login-module.jar"/>
                </resources>
            
            
                <dependencies>
                    <module name="org.picketbox"/>
                    <module name="javax.api"/>
                    <module name="org.slf4j"/>
                </dependencies>
            </module>
            

            2.) Added custom-login-module.jar and module.xml into jboss-as-7.1.1.Final\modules\custom\login\module

            3.) custom-login-module.jar contains :

             

            public class CustomCallbackHandler implements CallbackHandler {
                private static final Logger LOGGER = LoggerFactory.getLogger(CustomCallbackHandler.class);
                private String token;
            
                public CustomCallbackHandler(String token) {
                    this.token= token;
                }
            
                public String getToken() {
                    return token;
                }
            
                @Override
                public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                    for (Callback callback : callbacks) {
                        if (callback instanceof TokenCallback) {
                            ((TokenCallback) callback).setToken(token);
                        }
                    }
                }
            }
            

             

            public class TokenCallback implements Callback {
                private static final Logger LOGGER = LoggerFactory.getLogger(TokenCallback.class);
                private String token;
            
                public TokenCallback() {
                }
            
                public String getToken() {
                    return token;
                }
            
                public void setToken(String token) {
                    LOGGER.info("Setting token = " + token);
                    this.token = token;
                }
            }
            

             

            public class CustomLoginModule extends AbstractServerLoginModule {
                private static final Logger LOGGER = LoggerFactory.getLogger(CustomLoginModule.class);
            
                @Override
                public boolean login() throws LoginException {
                    LOGGER.info("Doing login()");
                    boolean login = super.login();
                    super.loginOk = true;
                    return login;
                }
            
                @Override
                protected Principal getIdentity() {
                    return new UserPrincipal("some user");
                }
            
                @Override
                protected Group[] getRoleSets() throws LoginException {
                    return new Group[]{new MyGroup()}; // that and has name 'dummy'
                }
            }
            

             

            These are only dummy implementations.

             

            My web application is deployed from within  a .war archive. And it Contains following :

            jboss-web.xml

             

            <?xml version='1.0' encoding='UTF-8' ?>
            <!DOCTYPE jboss-web
                    PUBLIC "-//JBoss//DTD Web Application 2.3V2//EN"
                    "http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">
             <jboss-web>
                <security-domain>custom-auth</security-domain>
            </jboss-web>
            

             

            web.xml

            <?xml version="1.0" encoding="UTF-8"?>
            <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
                     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
                     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
                     id="WebApp_ID" version="2.5">
                <display-name>WebApp</display-name>
                <session-config>
                    <session-timeout>120</session-timeout>
                </session-config>
                <security-constraint>
                    <web-resource-collection>
                        <web-resource-name>All resources</web-resource-name>
                        <description>Protects all private resources</description>
                        <url-pattern>/private/*</url-pattern>
                    </web-resource-collection>
                    <auth-constraint>
                        <role-name>dummy</role-name>
                    </auth-constraint>
                    <user-data-constraint>
                        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
                    </user-data-constraint>
                </security-constraint>
            
                <security-role>
                    <role-name>dummy</role-name>
                </security-role>
            
                 <servlet>
                    <servlet-name>Private</servlet-name>
                    <servlet-class>com.company.private.PrivateWs</servlet-class>
                </servlet>
                <servlet-mapping>
                    <servlet-name>Private</servlet-name>
                    <url-pattern>/private/PrivateWs</url-pattern>
                </servlet-mapping>
            
                <servlet>
                    <servlet-name>AuthWS</servlet-name>
                    <servlet-class>com.company.auth.AuthWS</servlet-class>
                </servlet>
                <servlet-mapping>
                    <servlet-name>AuthWS</servlet-name>
                    <url-pattern>/AuthWS</url-pattern>
                </servlet-mapping>
            </web-app>
            

             

            @WebService
            @SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
            public class AuthWS{
                private static final Logger LOGGER = LoggerFactory.getLogger(AuthWS.class);
            
            
                @WebMethod
                public boolean doAuthenticate(String token) {
                    tryProgrammaticLogin(token);
                    return true;
                }
            
            
                private void tryProgrammaticLogin(String token) {
                    LoginContext loginContext = null;
                    try {
                        loginContext = new LoginContext("custom-auth", new CustomCallbackHandler(token));
                        loginContext.login();
                    } catch (LoginException e) {
                        LOGGER.info("Some problem occured when trying to custom login.", e);
                    }
                }
            }
            

             


            The call to doAuthenticate from my ws client works but the problem is that after try ProgrammaticLogin an exception occurs. And the PrivateWS is not accesible by client.

             

            17:33:40,901 INFO  [com.mycompany.AuthWS] (http--0.0.0.0-8080-1) Some problem occured when trying to custom login.: javax.security.auth.login.LoginException: Login Failure: all modules ignored
                      at javax.security.auth.login.LoginContext.invoke(LoginContext.java:921) [rt.jar:1.6.0_26]
                      at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186) [rt.jar:1.6.0_26]
                      at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683) [rt.jar:1.6.0_26]
                      at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.6.0_26]
                      at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680) [rt.jar:1.6.0_26]
                      at javax.security.auth.login.LoginContext.login(LoginContext.java:579) [rt.jar:1.6.0_26]
            

             

            standalone.xml from jboss configuration directory contains:

             

            <security-domain name="custom-auth">
                 <authentication>
                      <login-module code="com.mycompany.CustomLoginModule" flag="required" module="custom.login.module"/>
                 </authentication>
            </security-domain>
            

             

             

            Please tell me if the way of doing authentication with creating a new LoginContext  object is the right way of doing. I can't understand why this problem occurs.

            • 3. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
              dragos.n Newbie

              Please at least give me an answer if here it is the right place to ask this kind of questions?

               

              Or suggest me a simpler solution as an alternative.

               

              Thanks.

              • 4. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
                Stephen Coy Master

                Web services are normally stateless, so the notion of your doAuthenticate method does not make much sense.

                 

                Standard JAX-WS allows you to set authentication parameters for BASIC authentication:

                 

                 

                {code:java}

                   MyWebService createMyWebServicePort(String username, String password) {

                 

                        MyWebServiceClient myWebServiceClient = new MyWebServiceClient();

                 

                        MyWebService myWebService = myWebServiceClient.getMyWebServicePort();

                 

                        Map<String, Object> myWebServiceContext = ((BindingProvider) myWebService).getRequestContext();

                 

                        myWebServiceContext.put(BindingProvider.USERNAME_PROPERTY, username);

                 

                        myWebServiceContext.put(BindingProvider.PASSWORD_PROPERTY, password);

                 

                        return desktopRegistration;

                 

                    }

                {code}

                 

                 

                In your case you may be able to just set the username to your authenticaton token.

                 

                Getting your login module working properly following this would be the next exercise. Information on this can be found at http://www.jboss.org/picketbox.

                1 of 1 people found this helpful
                • 5. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
                  dragos.n Newbie

                  Thanks Stephen for your answer.

                   

                  Now at least I know that this can be the right place for asking this kind of questions.

                   

                  I saw this kind of authentication trough Webservice on an old blog that used a class called WebAuthentication but this class does not exists in JBoss 7.1.1 now.

                  You can see solution on this blog : http://roneiv.wordpress.com/2008/02/19/perform-a-jaas-programmatic-login-in-jboss-try-to-solve-the-empty-remote-user-problem/

                  I think  he used an older version of JBoss 4.2.2

                   

                  Next.

                  I've tried your solution with basic authentication method. But I have a problem there too.

                  1.) My token is about 1800 characters long. I've tried to send it into BindingProvider.PASSWORD_PROPERTY field, but I get BadRequestException into client. I thing this is because my token is too long.

                  2.) I need to store my credentials into client, and fill credentials each time I make a Webservice request. I liked the old way when after I made the form authentication into JBoss, my Webservice requests from client were done with session cookie, and JBoss allowed me to use those webservices.

                   

                  I'll post latter my second workaround.

                   

                  Thanks.

                  • 6. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
                    Stephen Coy Master

                    Given the form of your token I think you may want to investigate JBoss WS-Security.

                    • 7. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
                      Leonardo Devai Newbie

                      Dragos, what a coincidence, I am going down the same way and am almost at the same point.

                       

                      I just tried this, with a token larger than 4096 and it works fine:

                       

                      CLIENT

                      {code}

                                     String wsdl = ...

                                     TestWS port = svc.getPort(portName, TestWS.class);

                       

                                          Map<String, Object> req_ctx = ((BindingProvider) port).getRequestContext();

                                          req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, wsdl);

                       

                                          Map<String, List<String>> headers = new HashMap<String, List<String>>();

                                            String token = ...

                                          headers.put("token", Collections.singletonList(token));

                                          req_ctx.put(MessageContext.HTTP_REQUEST_HEADERS, headers);


                                     port.test();

                      {code}

                       

                      SERVER

                       

                      {code}

                            @Resource

                             WebServiceContext wsctx;

                       

                                public String test() {

                                          MessageContext mctx = wsctx.getMessageContext();

                       

                                          Map http_headers = (Map) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);

                                          List tokenList = (List) http_headers.get("token");

                                          String token = (String) tokenList.get(0);

                       

                                          System.out.println("[Server] Token size: " + token.length());

                                          return "";

                                }

                       

                      {code}

                       

                       

                      I think this can be worked out with some filters, or perhaps it's not the best way...

                       

                      Hope it could be helpful, and thanks for putting this all together, saved me some time!

                      • 8. Re: JBoss 7.1.1 Webservice programatic JAAS authentication.
                        dragos.n Newbie

                        Hi Leonardo,

                        I'm happy that this post helped you.

                         

                        Let me summarize what I've managed to do so far and what I hoped to achieve :

                         

                        I know that webservices are stateless by definition but let my clarify something.

                        First of all my webservice client is an applet into a browser and I want a way  to authenticate to JBoss trough a web application (which contains the applet) in order to use protected Webservices that are authorized by JBoss. Services that are defined into web.xml.

                         

                        A.) Current process :

                         

                        1.) User enters url for applet into web application

                        2.) User is not authenticated then JBoss redirects to login form

                        3.) User enters credentials into the login form and does a submit to JBoss

                        4.) JBoss checks credentials and puts Subject into session and redirects back to applet url

                        5.) Applet is downloaded to client and is now able to access private Webservices because it sends the session cookie id, and JBoss allows this request because user is authenticated.

                         

                        B.) What I wanted to achieve :

                         

                        1.) User enters public applet url.

                        2.) Applet sends a token to JBoss web application trough a public webservice call.

                        3.) That public webservice call should validate that token and try to mark user as authenticated.

                        4.) Applet should now  be able to use other privade webservices.

                         

                        I couldn't find a way to programatically invoke JAAS from an Webservice method and also to populate the Session with Subject in a standard way.

                         

                        What I've managed to achieve is :

                        Changed the way protected url are secured by JBoss, and used basic authentication mechanism.

                         

                        1.) User enters public applet url.

                        2.) Applet sends a token to JBoss web application trough a public webservice call ( this call creates user if doesn't exists into users store )

                        3.) Applet then uses Authenticator class to authenticate to JBoss web

                        4.) When applet first makes a private webservice request call, it also does a Authentication with the help of Authenticator class.

                        5.) Applet then is able to use other private webservices because ( as far as I saw ) it sends an Digest value, and probably JBoss is able to identify the session and request is from an user that is allowed to use that private resource.

                         

                        Authenticator.setDefault(new Authenticator() {
                                                protected PasswordAuthentication getPasswordAuthentication() {
                                                    return new PasswordAuthentication(myuser, mypassword);
                                                }
                                            });
                        

                         

                        My first attempt was to use :

                         

                        Map requestContext = ((BindingProvider) eventPort).getRequestContext();
                        requestContext.put(BindingProvider.USERNAME_PROPERTY, "some user");
                        requestContext.put(BindingProvider.PASSWORD_PROPERTY, "some password");
                        

                         

                        This solution also worked with basic authentication method, but I didn't like this solution because it was heavy. Each time applet used a webservice, JBoss had to do an authentication ( which was an hit into database ).