Announcement Announcement Module
Collapse
No announcement yet.
LDAP Remember-me cookie determined to have expired when it hasn't Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • LDAP Remember-me cookie determined to have expired when it hasn't

    I have an app where remember-me was working fine for db-based accounts, but not for LDAP accounts (we support both types of authentication at the same time). Upon reading the docs and this thread:

    http://forum.springsource.org/showth...t=80513&page=3

    I found out that for remember-me to work with LDAP, one must use a persistent token repository so I added "tokenRepository" bean (see my config file below). I now see a cookie being created with the correct expiration date (14 days default, but I have also set 28 days and was set correctly according to IE7). There's also an entry in the persistent_logins table:

    Code:
    SQL> select * from persistent_logins;
    
    USERNAME  SERIES                           TOKEN                LAST_USED
    ---            ----------------         --------------    ---------------------
    rxf  O/IMY9qbksPZ2lg+wjRKhg==  ohaFD4CNtXxs/TNzVi98pQ==  28-MAY-10 05.56.46.283000 PM
    However, when I try to login again (without logging out, after restarting the browser), I get the following messages from Spring Security:

    Code:
    17:57:44,987 DEBUG PersistentTokenBasedRememberMeServices - Remember-me cookie detected
    ...
    17:57:45,581 DEBUG PersistentTokenBasedRememberMeServices - Cancelling cookie
    17:57:45,581 DEBUG PersistentTokenBasedRememberMeServices - No persistent token found for series id: O/IMY9qbksPZ2lg+wjRKhg==
    And I get the same whether I use an LDAP or DB account, so remember-me doesn't work anymore even for DB-type authentication.

    Can anyone tell me what I'm missing? Thanks.


    My full spring security configuration:

    Code:
    <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
    
       <http auto-config="false">
    
          <intercept-url pattern="/login.html*" filters="none"/>
          <intercept-url pattern="/**" access="IS_AUTHENTICATED_REMEMBERED" />
    
          <form-login login-page="/login.html"
                      default-target-url="/index.html"
                      always-use-default-target="false"
                      authentication-failure-url="/login.html?login_error=1"/>
    
          <logout logout-success-url="/login.html" />
    
          <remember-me token-repository-ref="tokenRepository" />
                       <!--             token-validity-seconds="2419200" --> 
       </http>
    
       <ldap-server id="dex_ldap" url="ldap://my.ldapserver.com:333/dc=company,dc=com"/>
    
       <ldap-authentication-provider
          server-ref="dex_ldap"
          user-dn-pattern="uid={0},ou=people">
       </ldap-authentication-provider>
    
       <beans:bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
          <beans:property name="url" value="jdbc:oracle:thin:@my.dbserver.com:1521:mydb"/>
          <beans:property name="username" value="dexuser"/>
          <beans:property name="password" value="secret"/>
       </beans:bean>
    
       <beans:bean id="BCrypt" class="com.my.company.dexcenter.BCryptSecurity"/>
    
       <authentication-provider>
          <password-encoder ref="BCrypt"/>
          <jdbc-user-service data-source-ref="dataSource"/>
       </authentication-provider>
    
       <beans:bean id="tokenRepository"
          class="org.springframework.security.ui.rememberme.JdbcTokenRepositoryImpl">
          <beans:property name="createTableOnStartup" value="false" />
          <beans:property name="dataSource" ref="dataSource"/>
       </beans:bean>
    
    </beans:beans>

  • #2
    Can you post the table definition? (DDL for create table statement that you used)?
    Last edited by pmularien; May 29th, 2010, 12:42 PM.

    Comment


    • #3
      I just used

      Code:
      <beans:property name="createTableOnStartup" value="true" />
      the first time I deployed it so that the table would be created for me (I'm using oracle EE 10.2.0.3.0):

      Code:
      SQL> describe persistent_logins;
       Name                                      Null?    Type
       ----------------------------------------- -------- ----------------------------
       USERNAME                                  NOT NULL VARCHAR2(64)
       SERIES                                    NOT NULL VARCHAR2(64)
       TOKEN                                     NOT NULL VARCHAR2(64)
       LAST_USED                                 NOT NULL TIMESTAMP(6)

      Comment


      • #4
        UserDetailsService

        Did you also create a UserDetailsService implementation and if so, what does it look like?

        Comment


        • #5
          Nope, just using the default SS provides.

          Comment


          • #6
            So I added an implementation of UserDetailsService (see below) since the docs say that LDAP must have one, and now the cookie is accepted on the first request, but then Spring Security "cancels the cookie" which means it gets deleted along with the token entry in the DB.

            On the browser, I get a 500 error and the exception message: "Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."

            Am I attempting the impossible? That is, have both DB and LDAP accounts with remember-me at the same time? I've never seen a configuration that supports both at the same time.

            Please see the attached log for details of what's going on.

            Updates to the config and UserDetailsService implementation:



            Code:
            ...
               <remember-me key="DEXcenterRocks"
                               user-service-ref="dexUserDetailsService"
                               token-repository-ref="tokenRepository" />
            ...
               <beans:bean id="dexUserDetailsService" class="com.iti.dexcenter.server.security.DexUserDetailsService">
               </beans:bean>
            
            
            SOURCE CODE:
            
            public class DexUserDetailsService implements UserDetailsService {
               
               private UserDelegate userDelegate = new UserDelegate();
            
               public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            
                  User user = userDelegate.getUser(username); 
            
                  if (user == null) { 
                     throw new UsernameNotFoundException("User not found"); 
                  } 
            
                  GrantedAuthority[] authorities = new GrantedAuthority[0]; // no roles needed for now
                  
                  org.springframework.security.userdetails.User authUser =
                     new org.springframework.security.userdetails.User(user.getUsername(),
                           user.getPassword(), user.isEnabled(), true, true, true, authorities); 
            
                  return authUser; 
               }

            Comment


            • #7
              And the log I mentioned is here...

              Comment


              • #8
                I do not think this impossible, but I'd first caution that using two different authentication providers (i.e. ldap and a database) can cause problems when relating data to a user. This is because uniqueness across the two cannot easily be guaranteed by the database or ldap. For example, if you are storing a message for a user with username of "user" you must ensure you know which "user" it is (the one in ldap or the one in the database). This is not to say that it is impossible to distinguish between the two users, but you must be careful.

                Can you expand upon this statement:

                now the cookie is accepted on the first request, but then Spring Security "cancels the cookie" which means it gets deleted along with the token entry in the DB.
                What are the exact steps to produce this problem? What user are you logging in as (one in the database or one in ldap...or does the username exist in both). Does it matter if you login as a user within the database or ldap?

                One thing to consider is the UserDetailsService that the tokenRepository references will need to be able to distinguish between users in the database and ldap. It is likely important that the implementation does not accidentally return a user with username "user" from the database if the user saved was actually "user" from ldap (this ensures the proper granted authorities are returned). Additionally it will need to be able to lookup users from both locations.

                Regards,
                Rob

                Comment


                • #9
                  Thanks Rob for your help.
                  I actually tested my original configuration as posted (without a custom UserDetailsService) on another server and it worked. I then tested on yet another one and it also worked, so it seems that there's some conflict on my environment. One obvious difference is that on my machine I run it on a Jetty server that I control from my build process (for quick testing/startup) and the other servers use JBoss 5.0.

                  I still don't know why it doesn't work on my environment (everything else has always worked on that environment), so if you have any ideas, let me know.

                  Just a bit more info:
                  The problems occur on my environment whether I use an LDAP or a DB account. Because of the way the app works, LDAP accounts always have a corresponding DB account; there's an account on the DB with the same username, even if they don't have the same password.
                  Regarding the quote you asked, what I see for example is this; if I try accessing a url that is protected using the remember-me cookie, I can access the page as long as it doesn't launch another request (ajax for instance), but as soon as another request is done, I'm kicked out, even if I try accessing a second time the same static url.

                  For now, this works where it matters (testing and pre-production stage).

                  Comment

                  Working...
                  X