cancel
Showing results for 
Search instead for 
Did you mean: 

what does SSO authentication touch point used for ?

zengqingyi12
Champ in-the-making
Champ in-the-making
I find there are following code in NTLMAuthenticationFilter.java which used for alfresco share SSO authenticate:

               Response remoteRes;
               if (cachedNtlm)
               {
                   Connector conn = connectorService.getConnector(this.endpoint, session);
                   ConnectorContext ctx = new ConnectorContext(null, getConnectionHeaders(conn));
                   remoteRes = conn.call("/touch", ctx, req, null);
               }
               else
               {
                   Connector conn = connectorService.getConnector(this.endpoint, AuthenticationUtil.getUserId(req),
                           session);
                   ConnectorContext ctx = new ConnectorContext();
                   remoteRes = conn.call("/touch", ctx);
               }
and I found the alfresco/service/touch 's description is:
SSO Authentication touch point, so what is touch point used for ?  And how it works with SSO ?  
Can someone point me the direction or the references ?
Thanks !
10 REPLIES 10

afaust
Legendary Innovator
Legendary Innovator
/touch is just a No-Op API used to verify and/or authenticate a SSO session. Share doesn't actually authenticate you (when using NTLM at least) - it delegates this job to the repository and the touch point is the way it does that since the configured filters of the repository will intercept the call and perform any SSO functionality on their part.

I guess there is no better reference for that than some lines of code below of what you posted. It hasn't been put into the wiki or a book since 99.9% of all users and developers never have to deal with this.

zengqingyi12
Champ in-the-making
Champ in-the-making
/touch is just a No-Op API used to verify and/or authenticate a SSO session. Share doesn't actually authenticate you (when using NTLM at least) - it delegates this job to the repository and the touch point is the way it does that since the configured filters of the repository will intercept the call and perform any SSO functionality on their part.

I guess there is no better reference for that than some lines of code below of what you posted. It hasn't been put into the wiki or a book since 99.9% of all users and developers never have to deal with this.
It seems that I got it:
when we call the touch point, share will automatically get the connector( and authenticator)   and authenticate through the alfresco repository, which can be maintained for subsequent requests. And the authenticator can automatically pick up the username and password from request header and authenticate them through alfresco repository,  right ?

afaust
Legendary Innovator
Legendary Innovator
The gist of it is correct.

The connector has been retrieved prior to the call thus it is not "automatically". The authenticator is usually a part of the connector and will only submit username and password from the CredentialsVault filled through the Share login page. Since in a SSO scenario you do not login with username and password, the authenticator can not pass anything (in fact, the connector configured in the wiki doesn't even have an authenticator).

In a NTLM SSO scenario authentication/validation occurs through forwarding the HTTP requests NTLM authentication header in the call to the repository. This is done in a call similar to:

Response remoteRes = conn.call("/touch", ctx, req, null); //req contains the NTLM authentication header and its headers will be copied

In the code you posted this is only performed to validate/refresh a repository session. When the session has already timed out or the user first accesses Share, the entire 3-way handshake for NTLM is started up (again). This is managed by the following code (and the methods called therein) at the end of doFilter()

        // Check the authorization header
        if (authHdr == null)
        {
            if (logger.isDebugEnabled())
                logger.debug("New NTLM auth request from " + req.getRemoteHost() + " (" +
                             req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
           
            restartAuthProcess(session, res);
        }
        else
        {
            // Decode the received NTLM blob and validate
            final byte[] authHdrByts = authHdr.substring(5).getBytes();
            final byte[] ntlmByts = Base64.decode(authHdrByts);
            int ntlmTyp = NTLMMessage.isNTLMType(ntlmByts);
           
            if (ntlmTyp == NTLM.Type1)
            {
                // Process the type 1 NTLM message
                Type1NTLMMessage type1Msg = new Type1NTLMMessage(ntlmByts);
                // Start with a fresh session
                session.invalidate();
                session = req.getSession();
                processType1(type1Msg, req, res, session);
            }
            else if (ntlmTyp == NTLM.Type3)
            {
                // Process the type 3 NTLM message
                Type3NTLMMessage type3Msg = new Type3NTLMMessage(ntlmByts);
                processType3(type3Msg, req, res, session, chain);
            }
            else
            {
                if (logger.isDebugEnabled())
                    logger.debug("NTLM not handled, redirecting to login page");
               
                redirectToLoginPage(req, res);
            }
        }

zengqingyi12
Champ in-the-making
Champ in-the-making
Thanks very much !   
Because I want to implement an login page while SSO is working: 
So I have following solution:   
in NTLMAuthenticationFilter of share:
if(request.getURI().contains("mylogin"))
{
    Session.Invalidate();
    RedirectToLoginPage();
    return;
}
with the code above, I then can type http://localhost:8080/share/page/mylogin to browse to the login page of share.
and then I can key in the username and password to login, this works well in Firefox, but in IE I found the username and password don't get submitted:
in LoginController:
String username = request.getParamters("username"); and it get a null reference.  (If I refresh the login page several times and then relogin, it works like a charm)
Do you have any ideas about this problem ?

And in the handshake above, why the browser can smartly response to the alfresco's NTLM package ? Is it implemented by browser by default ?

" will only submit username and password from the CredentialsVault filled through the Share login page. " , Could you please explain the concept of CredentialsValut? and where CredentialsVault get the username and password, and where does it store them ?

"In a NTLM SSO scenario authentication/validation occurs through forwarding the HTTP requests NTLM authentication header in the call to the repository. This is done in a call similar to:"   Do you mean the actually authenticate will occur in the alfresco repository ? Then why are there three handshake in Share's filter ?

I am sorry for my long questions and really thanks for your patience!

afaust
Legendary Innovator
Legendary Innovator
1) Login - username problem: I do not have a concrete idea what might be the problem with IE/FF differences there, but I do remember I too had some problems in that general area when I backported some Kerberos bugfixes to an older Alfresco/Share version and also switched from SSO/login-only to SSO/login-mixed.

2) NTLM handshake: This is a feature of the browser. Firefox and IE recognize a response header WWW-Authenticate: NTLM and either use the OS credentials of the user (domain login) or prompt for a login, and then submit this (partially) hashed to the web server.

3) CredentialsVault: A CredentialVault is simply a session scoped object that is used to persist credentials by endpointId. These credentials are created and stored e.g. by the AlfrescoUserFactory in the call to authenticate() from the LoginServlet (which in turn uses the values of the login form). There are different types of CredentialVault implementations for different persistence types. The default saves the credentials in the session (in memory) while a XMLCredentialVault will use a XML file on disk.

4) NTLM SSO handshakes in Share filter: Yes, the actual authentication is always performed at the repository level. Since Share has to delegate the NTLM messages between client and repository, it has to know a) the state of the handshake and b) save previously sent messages for performance optimizations (e.g. repeated messages with a type3 header don't need to be delegated to the repository if the password hash is identical to a that of a validated, previous type3 header). When you look at the code you see it is mostly session and cache handling related.


Depending on the version of Alfresco you use, little has to be done to enable combined login and SSO behaviour of Share. We use Alfresco 3.2.2.1 and only needed these changes/patches:
1) modified restartAuthProcess in the filter to automatically redirect to the default Share login page when a) the browser does not support NTLM or b) the user cancelled any prompt

private void restartAuthProcess(HttpSession session, HttpServletRequest req, HttpServletResponse res) throws IOException
    {
        // Clear any cached logon details from the sessiom
        session.invalidate();
       
        // restart the authentication process for NTLM
        res.setHeader(HEADER_WWWAUTHENTICATE, AUTH_NTLM);
        res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        res.setContentType("text/html; charset=utf-8");
       
        final PrintWriter out = res.getWriter();
        out.println("<html><head>");
        out.println("<meta http-equiv=\"Refresh\" content=\"0; url=" +
                req.getContextPath() + "/page?f=default&pt=login" +
                "\">");
        out.println("</head><body><p>Please <a href=\"" +
                req.getContextPath() + "/page?f=default&pt=login" +
                "\">log in</a>.</p>");
        out.println("</body></html>");
        out.close();

       
        res.flushBuffer();
    }

2) enabled ticket validation for HTTP parameter "alf_ticket" in addition to default "ticket" on repository web client side

Index: projects/web-client/source/java/org/alfresco/web/app/servlet/NTLMAuthenticationFilter.java
===================================================================
— projects/web-client/source/java/org/alfresco/web/app/servlet/NTLMAuthenticationFilter.java   (revision 23221)
+++ projects/web-client/source/java/org/alfresco/web/app/servlet/NTLMAuthenticationFilter.java   (working copy)
@@ -83,7 +83,8 @@
         }
        
         // Use the web client user attribute name
-        setUserAttributeName(AuthenticationHelper.AUTHENTICATION_USER);       
+        setUserAttributeName(AuthenticationHelper.AUTHENTICATION_USER);
+        setTicketLogons(true);
     }
   

Index: projects/web-client/source/java/org/alfresco/web/app/servlet/KerberosAuthenticationFilter.java
===================================================================
— projects/web-client/source/java/org/alfresco/web/app/servlet/KerberosAuthenticationFilter.java   (revision 23221)
+++ projects/web-client/source/java/org/alfresco/web/app/servlet/KerberosAuthenticationFilter.java   (working copy)
@@ -87,6 +87,7 @@
        
         // Use the web client user attribute name
         setUserAttributeName(AuthenticationHelper.AUTHENTICATION_USER);
+        setTicketLogons(true);
     }

        
Index: projects/remote-api/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java
===================================================================
— projects/remote-api/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java   (revision 23221)
+++ projects/remote-api/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java   (working copy)
@@ -222,6 +222,10 @@

        boolean ticketValid = false;
        String ticket = req.getParameter(ARG_TICKET);
+       if(ticket == null){
+          //failover for alf_ticket
+          ticket = req.getParameter("alf_ticket");
+       }
        
        if (ticket != null && ticket.length() != 0)
        {


3) implemented and configured an authenticator that subclasses the AlfrescoAuthenticator and also checks for the session attribute _alfExternalAuth=true in isAuthenticated (Note: SessionTrackerFilter is a custom filter just used for tracking the current session as you do not always have access to either the request or the session directly or indirectly)

       public boolean isAuthenticated(String endpoint,
         ConnectorSession connectorSession) {
      final boolean ticketAuthentication = super.isAuthenticated(endpoint,
            connectorSession);

      final HttpSession session = SessionTrackerFilter.getCurrentSession();
      final boolean externalAuthentication = session != null
            && Boolean.TRUE
                  .equals(session
                        .getAttribute(UserFactory.SESSION_ATTRIBUTE_EXTERNAL_AUTH));
      final boolean result = ticketAuthentication || externalAuthentication;

      return result;
   }

With all this our users are able to use SSO if their system supports it or login with username+password if not, they do not have a domain account or want to access a special Alfresco-only account.

zengqingyi12
Champ in-the-making
Champ in-the-making
1) Login - username problem: I do not have a concrete idea what might be the problem with IE/FF differences there, but I do remember I too had some problems in that general area when I backported some Kerberos bugfixes to an older Alfresco/Share version and also switched from SSO/login-only to SSO/login-mixed.
So do you remember how do you fix this problem then ?
For the solution you offered, can you explain why do I need to configure a authenticator, and where is the isAuthenticated function from ?   And why after doing this the system can support SSO & login form mixed as I really want to know more insight about your solution.

In the LoginController:

        try
        {
            // check whether there is already a user logged in
            HttpSession session = request.getSession(false);
            if (session != null && request.getSession().getAttribute(UserFactory.SESSION_ATTRIBUTE_KEY_USER_ID) != null)
            {
                // log out the current user
                AuthenticationUtil.logout(request, response);
            }
           
            UserFactory userFactory = FrameworkUtil.getServiceRegistry().getUserFactory();
           
            // see if we can authenticate the user
            boolean authenticated = userFactory.authenticate(request, username, password);
            if (authenticated)
            {
                // this will fully reset all connector sessions
                RequestContext context = FrameworkUtil.getCurrentRequestContext();
                AuthenticationUtil.login(request, response, username);
               
                // mark the fact that we succeeded
                success = true;
            }
        }
share call the SlingshotUserFactory directly to authenticate the user, and I think CredentialsVault will keep this authentication into session. And this is what happens when using login form.
in SSO, NTLMAuthenticationFilter  call  /wcs/touch to authenticate the user. Then does that mean: in the "/wcs/touch" 's handler or filter, it will have similar mechanism to read the username and password from Session and authenticate it through  AlfrescoUserFactory, right ?

afaust
Legendary Innovator
Legendary Innovator
I am sorry I didn't get back to you sooner - it was quite a busy time at work and somehow the email notification about your reply got lost between all the other emails…

1) As far as I can remember, the problems I had in that direction disappeared after I implemented my own authenticator for Share that prevents unnecessary authentication request when the user has been authenticated via SSO. The base problem for me was that in a SSO scenario, the default authenticator of Share did not know of the SSO and was trying to login my user although it did not know of a password. The login request submitted contained a username but no password (null reference), causing users to accidentally have their domain account locked after 3 attempts to log them in this way.

2) The reason why you need the authenticator is outlined in 1). Whenever a remote connector is tasked to make a call to the repository, it first asks the authenticator if the current user has already been authenticated. If not, it initiates a login handshake. The default authenticator does not know of SSO authentication and thus ALWAYS starts a login handshake (even if it does not know the password). The isAuthenticated method is defined in the interface Authenticator.

3) The filter that authenticates the request made to "/wcs/touch" does not use username and password directly to authenticate the user. The request made by the client contains a special header with a so-called NTLM Token. This token gets passed to "/wcs/touch" and the filter uses that token to authenticate the user. The token contains both the user name and a hashed version of the password (the cleartext password cannot be read from this). Depending on wether you are using alfrescoNtlm or passthru, the filter compares the hashed password with the one stored in the database or delegates the token to the domain controller of your network (which then checks the users hashed password). There is no such thing as an AlfrescoUserFactory on the repository side - here, the AuthenticationService (or more precisely an implementation of this interface) is used.

zengqingyi12
Champ in-the-making
Champ in-the-making
Thanks very much for your kindness. 
After I implemented sso and login page mixed solution, I found such a problem:
If I use http://localhost:8080/share to sso alfreso, and then logout(clear all the attributes in session), it redirect me to the login page, after I typed in the username and password, IE keep sending the authorization header, without sending any username password.   So finally the login is failed.
If I use a new IE instance, and directly to the login page, then key in username and password, everything is fine.

It seems that IE will remember the url the use SSO, and then keep using it in later requests. You have to start a new IE instance so as to use the login form to login.
Do you have any advices ?
Thanks !

I am sorry I didn't get back to you sooner - it was quite a busy time at work and somehow the email notification about your reply got lost between all the other emails…

1) As far as I can remember, the problems I had in that direction disappeared after I implemented my own authenticator for Share that prevents unnecessary authentication request when the user has been authenticated via SSO. The base problem for me was that in a SSO scenario, the default authenticator of Share did not know of the SSO and was trying to login my user although it did not know of a password. The login request submitted contained a username but no password (null reference), causing users to accidentally have their domain account locked after 3 attempts to log them in this way.

2) The reason why you need the authenticator is outlined in 1). Whenever a remote connector is tasked to make a call to the repository, it first asks the authenticator if the current user has already been authenticated. If not, it initiates a login handshake. The default authenticator does not know of SSO authentication and thus ALWAYS starts a login handshake (even if it does not know the password). The isAuthenticated method is defined in the interface Authenticator.

3) The filter that authenticates the request made to "/wcs/touch" does not use username and password directly to authenticate the user. The request made by the client contains a special header with a so-called NTLM Token. This token gets passed to "/wcs/touch" and the filter uses that token to authenticate the user. The token contains both the user name and a hashed version of the password (the cleartext password cannot be read from this). Depending on wether you are using alfrescoNtlm or passthru, the filter compares the hashed password with the one stored in the database or delegates the token to the domain controller of your network (which then checks the users hashed password). There is no such thing as an AlfrescoUserFactory on the repository side - here, the AuthenticationService (or more precisely an implementation of this interface) is used.

afaust
Legendary Innovator
Legendary Innovator
Hello,

there is no advice I can give you here, since this is intentional behavior and all NTLM-enabled browsers will behave the same way. You can in effect not log out of a SSO application.