[seam-commits] Seam SVN: r8340 - trunk/src/main/org/jboss/seam/security.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Fri Jun 6 11:57:23 EDT 2008


Author: shane.bryzak at jboss.com
Date: 2008-06-06 11:57:22 -0400 (Fri, 06 Jun 2008)
New Revision: 8340

Added:
   trunk/src/main/org/jboss/seam/security/JpaTokenStore.java
   trunk/src/main/org/jboss/seam/security/RememberMe.java
   trunk/src/main/org/jboss/seam/security/TokenStore.java
Modified:
   trunk/src/main/org/jboss/seam/security/Credentials.java
   trunk/src/main/org/jboss/seam/security/FacesSecurityEvents.java
   trunk/src/main/org/jboss/seam/security/Identity.java
Log:
JBSEAM-2079

Modified: trunk/src/main/org/jboss/seam/security/Credentials.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/Credentials.java	2008-06-06 15:20:22 UTC (rev 8339)
+++ trunk/src/main/org/jboss/seam/security/Credentials.java	2008-06-06 15:57:22 UTC (rev 8340)
@@ -15,7 +15,6 @@
 import org.jboss.seam.annotations.Install;
 import org.jboss.seam.annotations.Name;
 import org.jboss.seam.annotations.Scope;
-import org.jboss.seam.annotations.Startup;
 import org.jboss.seam.annotations.intercept.BypassInterceptors;
 import org.jboss.seam.core.Events;
 import org.jboss.seam.log.LogProvider;
@@ -25,9 +24,9 @@
 @Scope(SESSION)
 @Install(precedence = BUILT_IN)
 @BypassInterceptors
- at Startup
 public class Credentials implements Serializable
 {
+   public static final String EVENT_INIT_CREDENTIALS = "org.jboss.seam.security.initCredentials";
    public static final String EVENT_CREDENTIALS_UPDATED = "org.jboss.seam.security.credentialsUpdated";
    
    private static final LogProvider log = Logging.getLogProvider(Credentials.class);
@@ -37,8 +36,16 @@
    
    private boolean invalid = false;
    
+   private boolean initialized;
+   
    public String getUsername()
    {
+      if (!initialized && Events.exists())
+      {
+         initialized = true;
+         Events.instance().raiseEvent(EVENT_INIT_CREDENTIALS, this);
+      }
+      
       return username;
    }
    

Modified: trunk/src/main/org/jboss/seam/security/FacesSecurityEvents.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/FacesSecurityEvents.java	2008-06-06 15:20:22 UTC (rev 8339)
+++ trunk/src/main/org/jboss/seam/security/FacesSecurityEvents.java	2008-06-06 15:57:22 UTC (rev 8340)
@@ -3,7 +3,6 @@
 import static org.jboss.seam.ScopeType.APPLICATION;
 import static org.jboss.seam.annotations.Install.BUILT_IN;
 
-import javax.faces.context.FacesContext;
 import javax.security.auth.login.LoginException;
 
 import org.jboss.seam.annotations.Install;
@@ -12,13 +11,12 @@
 import org.jboss.seam.annotations.Scope;
 import org.jboss.seam.annotations.Startup;
 import org.jboss.seam.annotations.intercept.BypassInterceptors;
-import org.jboss.seam.faces.Selector;
 import org.jboss.seam.international.StatusMessages;
 import org.jboss.seam.international.StatusMessage.Severity;
 
 /**
- * Produces FacesMessages for certain security events, and decouples the
- * Identity component from JSF - and also handles cookie functionality.
+ * Produces FacesMessages in response of certain security events, and helps to decouple the
+ * Identity component from JSF.
  * 
  * @author Shane Bryzak
  */
@@ -27,58 +25,15 @@
 @Install(precedence = BUILT_IN, classDependencies = "javax.faces.context.FacesContext")
 @BypassInterceptors
 @Startup
-public class FacesSecurityEvents extends Selector
+public class FacesSecurityEvents 
 {  
-   @Override
-   public String getCookieName()
-   {
-      return "org.jboss.seam.security.username";
-   }   
-   
-   @Observer("org.jboss.seam.postCreate.org.jboss.seam.security.identity")
-   public void initCredentialsFromCookie(Identity identity)
-   {       
-      FacesContext ctx = FacesContext.getCurrentInstance();
-      if (ctx != null)
-      {
-         setCookiePath(ctx.getExternalContext().getRequestContextPath());
-      }
-      
-      identity.setRememberMe(isCookieEnabled());      
-      
-      String username = getCookieValue();
-      if (username!=null)
-      {
-         setCookieEnabled(true);
-         identity.setUsername(username);
-         postRememberMe(identity);
-      }
-            
-      setDirty();
-   }
-   
-   @Observer(Credentials.EVENT_CREDENTIALS_UPDATED)
-   public void credentialsUpdated()
-   {
-      setDirty();
-   }
-   
    @Observer(Identity.EVENT_POST_AUTHENTICATE)
    public void postAuthenticate(Identity identity)
-   {
-      // Password is set to null during authentication, so we set dirty
-      setDirty();
-            
-      if ( !identity.isRememberMe() ) clearCookieValue();
-      setCookieValueIfEnabled( identity.getUsername() );      
+   {         
+      //org.jboss.security.saml.SSOManager.processManualLoginNotification(
+            //ServletContexts.instance().getRequest(), identity.getPrincipal().getName());
    }
    
-   @Observer(Identity.EVENT_REMEMBER_ME)
-   public void postRememberMe(Identity identity)
-   {
-      setCookieEnabled(identity.isRememberMe());
-   }     
-   
    @Observer(Identity.EVENT_LOGIN_FAILED)
    public void addLoginFailedMessage(LoginException ex)
    {
@@ -111,7 +66,7 @@
                getLoginSuccessfulMessageSeverity(), 
                getLoginSuccessfulMessageKey(), 
                getLoginSuccessfulMessage(), 
-               Identity.instance().getUsername());
+               Identity.instance().getCredentials().getUsername());
    }
    
    @Observer(Identity.EVENT_NOT_LOGGED_IN)

Modified: trunk/src/main/org/jboss/seam/security/Identity.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/Identity.java	2008-06-06 15:20:22 UTC (rev 8339)
+++ trunk/src/main/org/jboss/seam/security/Identity.java	2008-06-06 15:57:22 UTC (rev 8340)
@@ -56,8 +56,8 @@
    public static final String EVENT_PRE_AUTHENTICATE = "org.jboss.seam.security.preAuthenticate";
    public static final String EVENT_POST_AUTHENTICATE = "org.jboss.seam.security.postAuthenticate";
    public static final String EVENT_LOGGED_OUT = "org.jboss.seam.security.loggedOut";
-   public static final String EVENT_REMEMBER_ME = "org.jboss.seam.security.rememberMe";
-   public static final String EVENT_ALREADY_LOGGED_IN = "org.jboss.seam.security.alreadyLoggedIn";   
+   public static final String EVENT_ALREADY_LOGGED_IN = "org.jboss.seam.security.alreadyLoggedIn";
+   public static final String EVENT_QUIET_LOGIN = "org.jboss.seam.security.quietLogin";
    
    protected static boolean securityEnabled = true;
    
@@ -76,7 +76,7 @@
    private Principal principal;   
    private Subject subject;
    
-   private boolean rememberMe;
+   private RememberMe rememberMe;   
    
    private String jaasConfigName = null;
    
@@ -99,7 +99,8 @@
          permissionMapper = (PermissionMapper) Component.getInstance(PermissionMapper.class);
       }
       
-      credentials = (Credentials) Component.getInstance(Credentials.class);
+      rememberMe = (RememberMe) Component.getInstance(RememberMe.class, true);      
+      credentials = (Credentials) Component.getInstance(Credentials.class);     
    }
    
    public static boolean isSecurityEnabled()
@@ -258,12 +259,18 @@
    {
       try
       {
-         if (credentials.isSet()) 
+         if (Events.exists()) Events.instance().raiseEvent(EVENT_QUIET_LOGIN, this);         
+          
+         // Ensure that we haven't been authenticated as a result of the EVENT_QUIET_LOGIN event
+         if (!isLoggedIn(false))
          {
-            authenticate();
-            if (isLoggedIn(false) && Contexts.isEventContextActive())
+            if (credentials.isSet()) 
             {
-               Contexts.getEventContext().set(SILENT_LOGIN, true);
+               authenticate();
+               if (isLoggedIn(false) && Contexts.isEventContextActive())
+               {
+                  Contexts.getEventContext().set(SILENT_LOGIN, true);
+               }
             }
          }
       }
@@ -323,7 +330,7 @@
     * authenticated user.  This method may be overridden by a subclass if
     * different post-authentication logic should occur.
     */
-   protected void postAuthenticate()
+   void postAuthenticate()
    {
       // Populate the working memory with the user's principals
       for ( Principal p : getSubject().getPrincipals() )
@@ -630,8 +637,26 @@
    public void setPassword(String password)
    {
       credentials.setPassword(password);
+   }   
+   
+   /**
+    * @see org.jboss.seam.security.RememberMe#isEnabled()
+    */
+   @Deprecated
+   public boolean isRememberMe()
+   {
+      return rememberMe != null ? rememberMe.isEnabled() : false;
    }
    
+   /**
+    * @see org.jboss.seam.security.RememberMe#setEnabled(boolean)
+    */
+   @Deprecated
+   public void setRememberMe(boolean remember)
+   {
+      if (rememberMe != null) rememberMe.setEnabled(remember);
+   }   
+   
    public Credentials getCredentials()
    {
       return credentials;
@@ -647,20 +672,6 @@
       this.authenticateMethod = authMethod;
    }
    
-   public boolean isRememberMe()
-   {
-      return rememberMe;
-   }
-   
-   public void setRememberMe(boolean remember)
-   {
-      if (this.rememberMe != remember)
-      {
-         this.rememberMe = remember;
-         if (Events.exists()) Events.instance().raiseEvent(EVENT_REMEMBER_ME, this);
-      }
-   }
-   
    public String getJaasConfigName()
    {
       return jaasConfigName;

Added: trunk/src/main/org/jboss/seam/security/JpaTokenStore.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/JpaTokenStore.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/security/JpaTokenStore.java	2008-06-06 15:57:22 UTC (rev 8340)
@@ -0,0 +1,162 @@
+package org.jboss.seam.security;
+
+import static org.jboss.seam.ScopeType.APPLICATION;
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.io.Serializable;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+
+import org.jboss.seam.annotations.Create;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.annotations.security.TokenUsername;
+import org.jboss.seam.annotations.security.TokenValue;
+import org.jboss.seam.core.Expressions;
+import org.jboss.seam.core.Expressions.ValueExpression;
+import org.jboss.seam.security.management.IdentityManagementException;
+import org.jboss.seam.util.AnnotatedBeanProperty;
+
+/**
+ * A TokenStore implementation, stores tokens inside a database table.
+ * 
+ * @author Shane Bryzak
+ */
+ at Name("org.jboss.seam.security.tokenStore")
+ at Install(precedence = BUILT_IN, value=false) 
+ at Scope(APPLICATION)
+ at BypassInterceptors
+public class JpaTokenStore implements TokenStore, Serializable
+{
+   private Class tokenClass;
+   
+   private ValueExpression<EntityManager> entityManager;    
+   
+   private AnnotatedBeanProperty<TokenUsername> tokenUsernameProperty;
+   private AnnotatedBeanProperty<TokenValue> tokenValueProperty;
+   
+   @Create
+   public void create()
+   {
+      if (entityManager == null)
+      {
+         entityManager = Expressions.instance().createValueExpression("#{entityManager}", EntityManager.class);
+      }       
+      
+      tokenUsernameProperty = AnnotatedBeanProperty.scanForProperty(tokenClass, TokenUsername.class);
+      tokenValueProperty = AnnotatedBeanProperty.scanForProperty(tokenClass, TokenValue.class);
+      
+      if (tokenUsernameProperty == null) 
+      {
+         throw new IllegalStateException("Invalid tokenClass " + tokenClass.getName() + 
+               " - required annotation @TokenUsername not found on any Field or Method.");
+      }
+      
+      if (tokenValueProperty == null) 
+      {
+         throw new IllegalStateException("Invalid tokenClass " + tokenClass.getName() + 
+               " - required annotation @TokenValue not found on any Field or Method.");
+      }       
+   }
+   
+   public void createToken(String username, String value)
+   {
+      if (tokenClass == null)
+      {
+         throw new IllegalStateException("Could not create token, tokenClass not set");
+      }   
+      
+      try
+      {
+         Object token = tokenClass.newInstance();
+         
+         tokenUsernameProperty.setValue(token, username);
+         tokenValueProperty.setValue(token, value);
+         
+         lookupEntityManager().persist(token);
+      }
+      catch (Exception ex)
+      {
+         if (ex instanceof IdentityManagementException)
+         {
+            throw (IdentityManagementException) ex;
+         }
+         else
+         {
+            throw new IdentityManagementException("Could not create account", ex);
+         }
+      }       
+   }
+   
+   public boolean validateToken(String username, String value)
+   {
+      return lookupToken(username, value) != null;
+   }
+   
+   public void invalidateToken(String username, String value)
+   {
+      Object token = lookupToken(username, value);
+      lookupEntityManager().remove(token);
+   }
+   
+   public void invalidateAll(String username)
+   {
+      Query query = lookupEntityManager().createQuery(
+         "select t from " + tokenClass.getName() + " t where " + tokenUsernameProperty.getName() +
+         " = :username")
+         .setParameter("username", username);
+      
+      for (Object token : query.getResultList())
+      {
+         lookupEntityManager().remove(token);
+      }      
+   }
+   
+   public Object lookupToken(String username, String value)       
+   {
+      try
+      {
+         Object token = lookupEntityManager().createQuery(
+            "select t from " + tokenClass.getName() + " t where " + tokenUsernameProperty.getName() +
+            " = :username and " + tokenValueProperty.getName() + " = :value")
+            .setParameter("username", username)
+            .setParameter("value", value)
+            .getSingleResult();
+         
+         return token;
+      }
+      catch (NoResultException ex)
+      {
+         return null;        
+      }      
+   }   
+   
+   public Class getTokenClass()
+   {
+      return tokenClass;
+   }
+   
+   public void setTokenClass(Class tokenClass)
+   {
+      this.tokenClass = tokenClass;
+   }
+   
+   private EntityManager lookupEntityManager()
+   {
+      return entityManager.getValue();
+   }
+   
+   public ValueExpression getEntityManager()
+   {
+      return entityManager;
+   }
+   
+   public void setEntityManager(ValueExpression expression)
+   {
+      this.entityManager = expression;
+   }    
+}

Added: trunk/src/main/org/jboss/seam/security/RememberMe.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/RememberMe.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/security/RememberMe.java	2008-06-06 15:57:22 UTC (rev 8340)
@@ -0,0 +1,322 @@
+package org.jboss.seam.security;
+
+import static org.jboss.seam.ScopeType.SESSION;
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.rmi.server.UID;
+import java.util.Random;
+
+import javax.faces.context.FacesContext;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.Create;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import org.jboss.seam.faces.Selector;
+import org.jboss.seam.security.management.IdentityManager;
+import org.jboss.seam.util.Base64;
+
+/**
+ * Remember-me functionality is provided by this class, in two different flavours.  The first mode
+ * provides username-only persistence, and is considered to be secure as the user (or their browser)
+ * is still required to provide a password.  The second mode provides an auto-login feature, however
+ * is NOT considered to be secure and is vulnerable to XSS attacks compromising the user's account.
+ * 
+ * Use the auto-login mode with caution!
+ * 
+ * @author Shane Bryzak
+ */
+ at Name("org.jboss.seam.security.rememberMe")
+ at Scope(SESSION)
+ at Install(precedence = BUILT_IN, classDependencies = "javax.faces.context.FacesContext")
+ at BypassInterceptors
+public class RememberMe
+{
+   class UsernameSelector extends Selector
+   {
+      @Override
+      public String getCookieName()
+      {
+         return "org.jboss.seam.security.username";
+      }       
+      
+      @Override
+      public void setDirty()
+      {
+         super.setDirty();
+      }
+      
+      @Override
+      public String getCookieValue()
+      {
+         return super.getCookieValue();
+      }
+      
+      @Override
+      public void clearCookieValue()
+      {
+         super.clearCookieValue();
+      }
+      
+      @Override
+      public void setCookieValueIfEnabled(String value)
+      {
+         super.setCookieValueIfEnabled(value);
+      }
+   }
+   
+   class TokenSelector extends UsernameSelector
+   {
+      @Override
+      public String getCookieName()
+      {
+         return "org.jboss.seam.security.token";
+      }
+   }
+   
+   private class DecodedToken
+   {
+      private String username;
+      private String value;
+      
+      public DecodedToken(String cookieValue)
+      {
+         String decoded = new String(Base64.decode(cookieValue));
+         
+         username = decoded.substring(0, decoded.indexOf(':'));
+         value = decoded.substring(decoded.indexOf(':') + 1);                  
+      }
+      
+      public String getUsername()
+      {
+         return username;
+      }
+      
+      public String getValue()
+      {
+         return value;
+      }
+   }
+      
+   private UsernameSelector usernameSelector;
+   
+   private TokenSelector tokenSelector;   
+   private TokenStore tokenStore;
+      
+   private boolean enabled;
+   
+   private boolean autoLoggedIn;
+   
+   private Random random = new Random(System.currentTimeMillis());
+   
+   public enum Mode { disabled, usernameOnly, autoLogin}
+   
+   private Mode mode = Mode.usernameOnly;
+   
+   public Mode getMode()
+   {
+      return mode;
+   }
+   
+   public void setMode(Mode mode)
+   {
+      this.mode = mode;
+   }
+   
+   public boolean isEnabled()
+   {
+      return enabled;
+   }
+   
+   public void setEnabled(boolean enabled)
+   {
+      if (this.enabled != enabled)
+      {
+         this.enabled = enabled;
+         usernameSelector.setCookieEnabled(enabled);
+         usernameSelector.setDirty();
+      }      
+   }
+   
+   public TokenStore getTokenStore()
+   {
+      return tokenStore;
+   }
+   
+   public void setTokenStore(TokenStore tokenStore)
+   {
+      this.tokenStore = tokenStore;
+   }
+   
+   @Create
+   public void create()
+   {
+      if (mode.equals(Mode.usernameOnly))
+      {      
+         usernameSelector = new UsernameSelector();
+      }
+      else if (mode.equals(Mode.autoLogin))
+      {
+         tokenSelector = new TokenSelector();
+
+         // Default to JpaTokenStore
+         if (tokenStore == null)
+         {
+            tokenStore = (TokenStore) Component.getInstance(JpaTokenStore.class, true);
+         }         
+      }
+   }
+   
+   protected String generateTokenValue()
+   {
+      StringBuilder sb = new StringBuilder();
+      sb.append(new UID().toString());
+      sb.append(":");
+      sb.append(random.nextLong());
+      return sb.toString();
+   }
+   
+   protected String encodeToken(String username, String value)
+   {
+      StringBuilder sb = new StringBuilder();
+      sb.append(username);
+      sb.append(":");
+      sb.append(value);
+      return Base64.encodeBytes(sb.toString().getBytes());      
+   }
+   
+   @Observer(Credentials.EVENT_INIT_CREDENTIALS)
+   public void initCredentials(Credentials credentials)
+   {             
+      if (mode.equals(Mode.usernameOnly))
+      {
+         FacesContext ctx = FacesContext.getCurrentInstance();
+         if (ctx != null)
+         {
+            usernameSelector.setCookiePath(ctx.getExternalContext().getRequestContextPath());
+         }
+         
+         String username = usernameSelector.getCookieValue();
+         if (username!=null)
+         {
+            setEnabled(true);
+            credentials.setUsername(username);
+         }
+               
+         usernameSelector.setDirty();
+      }
+      else if (mode.equals(Mode.autoLogin))
+      {
+         FacesContext ctx = FacesContext.getCurrentInstance();
+         if (ctx != null)
+         {
+            tokenSelector.setCookiePath(ctx.getExternalContext().getRequestContextPath());
+         }
+         
+         String token = usernameSelector.getCookieValue();
+         if (token != null)
+         {
+            setEnabled(true);
+            
+            DecodedToken decoded = new DecodedToken(token);
+
+            if (tokenStore.validateToken(decoded.getUsername(), decoded.getValue()))
+            {
+               credentials.setUsername(decoded.getUsername());
+               credentials.setPassword(decoded.getValue());               
+            }
+            else
+            {
+               // Have we been compromised? Just in case, invalidate all authentication tokens
+               tokenStore.invalidateAll(decoded.getUsername());
+            }
+         }
+      }
+   }
+   
+   @Observer(Identity.EVENT_QUIET_LOGIN)
+   public void quietLogin(Identity identity)
+   {
+      if (mode.equals(Mode.autoLogin) && isEnabled())
+      {
+         // Double check our credentials again
+         if (tokenStore.validateToken(identity.getCredentials().getUsername(), 
+               identity.getCredentials().getPassword()))
+         {
+            // Success, authenticate the user
+            identity.getSubject().getPrincipals().add(new SimplePrincipal(
+                  identity.getCredentials().getUsername()));            
+            // And populate the roles
+            for (String role : IdentityManager.instance().getImpliedRoles(
+                  identity.getCredentials().getUsername()))
+            {
+               identity.addRole(role);
+            }
+            
+            identity.postAuthenticate();
+            
+            autoLoggedIn = true;
+         }
+      }
+   }
+   
+   @Observer(Identity.EVENT_LOGGED_OUT)
+   public void loggedOut()
+   {
+      if (mode.equals(Mode.autoLogin))
+      {
+         tokenSelector.getCookieValue();
+      }
+   }
+   
+   @Observer(Identity.EVENT_POST_AUTHENTICATE)
+   public void postAuthenticate(Identity identity)
+   {
+      if (mode.equals(Mode.usernameOnly))
+      {
+         // Password is set to null during authentication, so we set dirty
+         usernameSelector.setDirty();
+               
+         if ( !enabled ) usernameSelector.clearCookieValue();
+         usernameSelector.setCookieValueIfEnabled( Identity.instance().getCredentials().getUsername() );
+      }
+      else if (mode.equals(Mode.autoLogin))
+      {
+         tokenSelector.setDirty();
+         
+         DecodedToken decoded = new DecodedToken(tokenSelector.getCookieValue());
+         
+         // Invalidate the current token whether enabled or not
+         tokenStore.invalidateToken(decoded.getUsername(), decoded.getValue());
+         
+         if ( !enabled ) 
+         {
+            tokenSelector.clearCookieValue();         
+         }
+         else
+         {
+            String value = generateTokenValue();
+            tokenStore.createToken(decoded.getUsername(), value);
+            tokenSelector.setCookieValueIfEnabled(encodeToken(decoded.getUsername(), value));            
+         }
+      }
+   }        
+   
+   @Observer(Credentials.EVENT_CREDENTIALS_UPDATED)
+   public void credentialsUpdated()
+   {
+      usernameSelector.setDirty();
+   }      
+   
+   /**
+    * A flag that an application can use to protect sensitive operations if the user has been
+    * auto-authenticated. 
+    */
+   public boolean isAutoLoggedIn()
+   {
+      return autoLoggedIn;
+   }
+}
\ No newline at end of file

Added: trunk/src/main/org/jboss/seam/security/TokenStore.java
===================================================================
--- trunk/src/main/org/jboss/seam/security/TokenStore.java	                        (rev 0)
+++ trunk/src/main/org/jboss/seam/security/TokenStore.java	2008-06-06 15:57:22 UTC (rev 8340)
@@ -0,0 +1,15 @@
+package org.jboss.seam.security;
+
+/**
+ * A store containing user authentication tokens.  Used in conjunction with the RememberMe
+ * component to auto-login users that present a valid cookie-based token.
+ * 
+ * @author Shane Bryzak
+ */
+public interface TokenStore
+{
+   void createToken(String username, String value);
+   boolean validateToken(String username, String value);
+   void invalidateToken(String username, String value);
+   void invalidateAll(String username);
+}




More information about the seam-commits mailing list