You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2016/08/11 13:11:18 UTC

svn commit: r1755975 - in /jackrabbit/oak/trunk: oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/ oak-core/src/main/java...

Author: angela
Date: Thu Aug 11 13:11:18 2016
New Revision: 1755975

URL: http://svn.apache.org/viewvc?rev=1755975&view=rev
Log:
OAK-4129 : Use CredentialsSupport in TokenConfigurationImpl and TokenProviderImpl

Added:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/TokenExternalLoginModuleTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestCredentialsSupport.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleCredentialsSupportTest.java
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/token/
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/token/default.md
Modified:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplOSGiTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/tokenmanagement.md

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java Thu Aug 11 13:11:18 2016
@@ -31,6 +31,7 @@ import javax.security.auth.login.LoginEx
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import org.apache.jackrabbit.oak.api.AuthInfo;
 import org.apache.jackrabbit.oak.api.ContentSession;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
@@ -67,7 +68,7 @@ public class CustomCredentialsSupportTes
         try {
             AuthInfo info = cs.getAuthInfo();
             assertEquals("testUser", info.getUserID());
-            assertAttributes(((IDP) idp).getAttributes(creds), info);
+            assertAttributes(getCredentialsSupport().getAttributes(creds), info);
         } finally {
             cs.close();
         }
@@ -94,6 +95,14 @@ public class CustomCredentialsSupportTes
         return new IDP();
     }
 
+    static Credentials createTestCredentials() {
+        return new TestCredentials(USER_ID);
+    }
+
+    protected CredentialsSupport getCredentialsSupport() {
+        return (IDP) idp;
+    }
+
     private static final class TestCredentials implements Credentials {
 
         private final String uid;
@@ -103,7 +112,10 @@ public class CustomCredentialsSupportTes
         }
     }
 
-    private static final class IDP implements ExternalIdentityProvider, CredentialsSupport {
+    static final class IDP implements ExternalIdentityProvider, CredentialsSupport {
+
+        private final Map attributes = Maps.newHashMap(ImmutableMap.of("a", "a"));
+
         @Nonnull
         @Override
         public String getName() {
@@ -207,10 +219,20 @@ public class CustomCredentialsSupportTes
         @Override
         public Map<String, ?> getAttributes(@Nonnull Credentials credentials) {
             if (credentials instanceof TestCredentials) {
-                return ImmutableMap.of("a", "a");
+                return attributes;
             } else {
                 return ImmutableMap.of();
             }
         }
+
+        @Override
+        public boolean setAttributes(@Nonnull Credentials credentials, @Nonnull Map<String, ?> attributes) {
+            if (credentials instanceof TestCredentials) {
+                this.attributes.putAll(attributes);
+                return true;
+            } else {
+                return false;
+            }
+        }
     }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/TokenExternalLoginModuleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/TokenExternalLoginModuleTest.java?rev=1755975&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/TokenExternalLoginModuleTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/TokenExternalLoginModuleTest.java Thu Aug 11 13:11:18 2016
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.jcr.Credentials;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.security.authentication.token.TokenConfigurationImpl;
+import org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.apache.jackrabbit.oak.spi.security.authentication.token.CompositeTokenConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class TokenExternalLoginModuleTest extends CustomCredentialsSupportTest {
+
+    private CredentialsSupport credentialsSupport;
+    private Registration registration;
+    private TokenConfigurationImpl tc;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+
+        credentialsSupport = getCredentialsSupport();
+
+        // NOTE: should be replaced by proper OSGi setup
+        CompositeTokenConfiguration composite = ((CompositeTokenConfiguration) getSecurityProvider().getConfiguration(TokenConfiguration.class));
+        tc = (TokenConfigurationImpl) composite.getDefaultConfig();
+        tc.bindCredentialsSupport(credentialsSupport);
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            tc.unbindCredentialsSupport(credentialsSupport);
+        } finally {
+            root.refresh();
+            super.after();
+        }
+    }
+
+    @Override
+    protected Configuration getConfiguration() {
+        return new Configuration() {
+            @Override
+            public AppConfigurationEntry[] getAppConfigurationEntry(String s) {
+                return new AppConfigurationEntry[]{
+                        new AppConfigurationEntry(
+                                TokenLoginModule.class.getName(),
+                                AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT,
+                                Collections.<String, Object>emptyMap()),
+
+                        new AppConfigurationEntry(
+                                ExternalLoginModule.class.getName(),
+                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                                options)
+                };
+            }
+        };
+    }
+
+    @Test
+    public void testTokenCreation() throws Exception {
+        Credentials creds = createTestCredentials();
+        assertTrue(credentialsSupport.setAttributes(creds, ImmutableMap.<String, Object>of(".token", "")));
+
+        String expectedUserId = credentialsSupport.getUserId(creds);
+
+        ContentSession cs = login(creds);
+        try {
+            assertEquals(expectedUserId, cs.getAuthInfo().getUserID());
+
+            Map<String, ?> attributes = credentialsSupport.getAttributes(creds);
+            String token = attributes.get(".token").toString();
+            assertFalse(token.isEmpty());
+
+            root.refresh();
+            User user = getUserManager(root).getAuthorizable(expectedUserId, User.class);
+
+            Tree tokenParent = root.getTree(user.getPath()).getChild(".tokens");
+            assertTrue(tokenParent.exists());
+            assertEquals(1, tokenParent.getChildrenCount(100));
+        } finally {
+            cs.close();
+        }
+    }
+
+    @Test
+    public void testTokenLogin() throws Exception {
+        Credentials creds = createTestCredentials();
+        assertTrue(credentialsSupport.setAttributes(creds, ImmutableMap.<String, Object>of(".token", "")));
+
+        String expectedUserId = credentialsSupport.getUserId(creds);
+
+        ContentSession cs = login(creds);
+        try {
+            String token = credentialsSupport.getAttributes(creds).get(".token").toString();
+            cs.close();
+
+            cs = login(new TokenCredentials(token));
+            assertEquals(expectedUserId, cs.getAuthInfo().getUserID());
+        } finally {
+            cs.close();
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java Thu Aug 11 13:11:18 2016
@@ -27,6 +27,9 @@ import org.apache.felix.scr.annotations.
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.spi.commit.MoveTracker;
@@ -35,6 +38,8 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
 import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.SimpleCredentialsSupport;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
@@ -72,6 +77,12 @@ import org.apache.jackrabbit.oak.spi.sec
 })
 public class TokenConfigurationImpl extends ConfigurationBase implements TokenConfiguration {
 
+    @Reference(
+            cardinality = ReferenceCardinality.OPTIONAL_UNARY,
+            policy = ReferencePolicy.DYNAMIC)
+    private CredentialsSupport credentialsSupport = SimpleCredentialsSupport.getInstance();
+
+    @SuppressWarnings("UnusedDeclaration")
     public TokenConfigurationImpl() {
         super();
     }
@@ -80,12 +91,26 @@ public class TokenConfigurationImpl exte
         super(securityProvider, securityProvider.getParameters(NAME));
     }
 
+    //----------------------------------------------------------------< SCR >---
     @SuppressWarnings("UnusedDeclaration")
     @Activate
     private void activate(Map<String, Object> properties) {
         setParameters(ConfigurationParameters.of(properties));
     }
 
+    @SuppressWarnings("UnusedDeclaration")
+    public void bindCredentialsSupport(CredentialsSupport credentialsSupport) {
+        this.credentialsSupport = credentialsSupport;
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    public void unbindCredentialsSupport(CredentialsSupport credentialsSupport) {
+        if (credentialsSupport == this.credentialsSupport) {
+            // reset to default implementation
+            this.credentialsSupport = SimpleCredentialsSupport.getInstance();
+        }
+    }
+
     //----------------------------------------------< SecurityConfiguration >---
     @Nonnull
     @Override
@@ -111,6 +136,6 @@ public class TokenConfigurationImpl exte
     @Override
     public TokenProvider getTokenProvider(Root root) {
         UserConfiguration uc = getSecurityProvider().getConfiguration(UserConfiguration.class);
-        return new TokenProviderImpl(root, getParameters(), uc);
+        return new TokenProviderImpl(root, getParameters(), uc, credentialsSupport);
     }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java Thu Aug 11 13:11:18 2016
@@ -33,8 +33,8 @@ import javax.annotation.Nonnull;
 import javax.jcr.AccessDeniedException;
 import javax.jcr.Credentials;
 import javax.jcr.RepositoryException;
-import javax.jcr.SimpleCredentials;
 
+import com.google.common.collect.ImmutableMap;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
 import org.apache.jackrabbit.api.security.user.Authorizable;
@@ -49,6 +49,8 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.name.NamespaceConstants;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.SimpleCredentialsSupport;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
@@ -104,14 +106,20 @@ class TokenProviderImpl implements Token
 
     private final Root root;
     private final ConfigurationParameters options;
+    private final CredentialsSupport credentialsSupport;
 
     private final long tokenExpiration;
     private final UserManager userManager;
     private final IdentifierManager identifierManager;
 
-    TokenProviderImpl(Root root, ConfigurationParameters options, UserConfiguration userConfiguration) {
+    TokenProviderImpl(@Nonnull Root root, @Nonnull ConfigurationParameters options, @Nonnull UserConfiguration userConfiguration) {
+        this(root, options, userConfiguration, SimpleCredentialsSupport.getInstance());
+    }
+
+    TokenProviderImpl(@Nonnull Root root, @Nonnull ConfigurationParameters options, @Nonnull UserConfiguration userConfiguration, @Nonnull CredentialsSupport credentialsSupport) {
         this.root = root;
         this.options = options;
+        this.credentialsSupport = credentialsSupport;
 
         this.tokenExpiration = options.getConfigValue(PARAM_TOKEN_EXPIRATION, DEFAULT_TOKEN_EXPIRATION);
         this.userManager = userConfiguration.getUserManager(root, NamePathMapper.DEFAULT);
@@ -126,18 +134,18 @@ class TokenProviderImpl implements Token
      * a {@link #TOKEN_ATTRIBUTE} attribute with an empty value.
      *
      * @param credentials The current credentials.
-     * @return {@code true} if the specified credentials are {@link SimpleCredentials}
-     *         or {@link ImpersonationCredentials} and if the (extracted) simple credentials
-     *         object contain a {@link #TOKEN_ATTRIBUTE} attribute with an empty value;
-     *         {@code false} otherwise.
+     * @return {@code true} if the specified credentials or those extracted from
+     * {@link ImpersonationCredentials} are supported and and if the (extracted)
+     * credentials object contain a {@link #TOKEN_ATTRIBUTE} attribute with an
+     * empty value; {@code false} otherwise.
      */
     @Override
     public boolean doCreateToken(@Nonnull Credentials credentials) {
-        SimpleCredentials sc = extractSimpleCredentials(credentials);
-        if (sc == null) {
+        Credentials creds = extractCredentials(credentials);
+        if (creds == null) {
             return false;
         } else {
-            Object attr = sc.getAttribute(TOKEN_ATTRIBUTE);
+            Object attr = credentialsSupport.getAttributes(creds).get(TOKEN_ATTRIBUTE);
             return (attr != null && "".equals(attr.toString()));
         }
     }
@@ -155,18 +163,18 @@ class TokenProviderImpl implements Token
     @CheckForNull
     @Override
     public TokenInfo createToken(@Nonnull Credentials credentials) {
-        SimpleCredentials sc = extractSimpleCredentials(credentials);
+        Credentials creds = extractCredentials(credentials);
+        String uid = (creds != null) ? credentialsSupport.getUserId(creds) : null;
+
         TokenInfo tokenInfo = null;
-        if (sc != null) {
-            String[] attrNames = sc.getAttributeNames();
-            Map<String, String> attributes = new HashMap<String, String>(attrNames.length);
-            for (String attrName : sc.getAttributeNames()) {
-                attributes.put(attrName, sc.getAttribute(attrName).toString());
-            }
-            tokenInfo = createToken(sc.getUserID(), attributes);
+        if (uid != null) {
+            Map<String, ?> attributes = credentialsSupport.getAttributes(creds);
+            tokenInfo = createToken(uid, attributes);
             if (tokenInfo != null) {
-                // also set the new token to the simple credentials.
-                sc.setAttribute(TOKEN_ATTRIBUTE, tokenInfo.getToken());
+                // also set the new token to the credentials.
+                if (!credentialsSupport.setAttributes(creds, ImmutableMap.of(TOKEN_ATTRIBUTE, tokenInfo.getToken()))) {
+                    log.debug("Cannot set token attribute to " + creds);
+                }
             }
         }
 
@@ -267,20 +275,17 @@ class TokenProviderImpl implements Token
     }
 
     @CheckForNull
-    private static SimpleCredentials extractSimpleCredentials(Credentials credentials) {
-        if (credentials instanceof SimpleCredentials) {
-            return (SimpleCredentials) credentials;
-        }
-
+    private Credentials extractCredentials(@Nonnull Credentials credentials) {
+        Credentials creds = credentials;
         if (credentials instanceof ImpersonationCredentials) {
-            Credentials base = ((ImpersonationCredentials) credentials).getBaseCredentials();
-            if (base instanceof SimpleCredentials) {
-                return (SimpleCredentials) base;
-            }
+            creds = ((ImpersonationCredentials) credentials).getBaseCredentials();
         }
 
-        // cannot extract SimpleCredentials
-        return null;
+        if (credentialsSupport.getCredentialClasses().contains(creds.getClass())) {
+            return creds;
+        } else {
+            return null;
+        }
     }
 
     @Nonnull

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java Thu Aug 11 13:11:18 2016
@@ -21,7 +21,6 @@ import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.jcr.Credentials;
-import javax.jcr.SimpleCredentials;
 
 import org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
 
@@ -64,4 +63,15 @@ public interface CredentialsSupport {
      */
     @Nonnull
     Map<String, ?> getAttributes(@Nonnull Credentials credentials);
+
+    /**
+     * Writes the attributes to the specified {@code Credentials}.
+     * If the specified credentials are not supported or doesn't allow to write
+     * attributes this method will return {@code false}.
+     *
+     * @param credentials The credentials as passed to the repository login.
+     * @param attributes The attributes to be written to the given credentials.
+     * @return {@code true}, if the attributes were set; {@code false} otherwise.
+     */
+    boolean setAttributes(@Nonnull Credentials credentials, @Nonnull Map<String, ?> attributes);
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java Thu Aug 11 13:11:18 2016
@@ -74,4 +74,17 @@ public final class SimpleCredentialsSupp
             return Collections.emptyMap();
         }
     }
+
+    @Override
+    public boolean setAttributes(@Nonnull Credentials credentials, @Nonnull Map<String, ?> attributes) {
+        if (credentials instanceof SimpleCredentials) {
+            SimpleCredentials sc = (SimpleCredentials) credentials;
+            for (Map.Entry<String, ?> entry : attributes.entrySet()) {
+                sc.setAttribute(entry.getKey(), entry.getValue());
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java Thu Aug 11 13:11:18 2016
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.0.0")
+@Version("2.0.0")
 package org.apache.jackrabbit.oak.spi.security.authentication.credentials;
 
 import aQute.bnd.annotation.Version;
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestCredentialsSupport.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestCredentialsSupport.java?rev=1755975&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestCredentialsSupport.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestCredentialsSupport.java Thu Aug 11 13:11:18 2016
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.authentication.token;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.Credentials;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Dummy implementation of {@link CredentialsSupport} that only supports
+ * {@link org.apache.jackrabbit.oak.security.authentication.token.TestCredentialsSupport.Creds}
+ * and always returns the same user ID upon {@link CredentialsSupport#getUserId(Credentials)}.
+ */
+public class TestCredentialsSupport implements CredentialsSupport {
+
+    private final String uid;
+
+    TestCredentialsSupport() {
+        this.uid = null;
+    }
+
+    TestCredentialsSupport(@Nonnull String uid) {
+        this.uid = uid;
+    }
+
+    @Nonnull
+    @Override
+    public Set<Class> getCredentialClasses() {
+        return ImmutableSet.<Class>of(Creds.class);
+    }
+
+    @CheckForNull
+    @Override
+    public String getUserId(@Nonnull Credentials credentials) {
+        if (credentials instanceof Creds) {
+            return uid;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Nonnull
+    @Override
+    public Map<String, ?> getAttributes(@Nonnull Credentials credentials) {
+        if (credentials instanceof Creds) {
+            return ((Creds) credentials).attributes;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public boolean setAttributes(@Nonnull Credentials credentials, @Nonnull Map<String, ?> attributes) {
+        if (credentials instanceof Creds) {
+            ((Creds) credentials).attributes.putAll(attributes);
+            return true;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    static final class Creds implements Credentials {
+
+        private final Map attributes;
+
+        Creds() {
+            attributes = new HashMap();
+            attributes.put(TokenConstants.TOKEN_ATTRIBUTE, "");
+        }
+    }
+}

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java?rev=1755975&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TestLoginModule.java Thu Aug 11 13:11:18 2016
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.authentication.token;
+
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.jcr.Credentials;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
+import org.apache.jackrabbit.oak.spi.security.authentication.AuthInfoImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+
+public class TestLoginModule extends AbstractLoginModule {
+
+    private CredentialsSupport credentialsSupport;
+    private Credentials credentials;
+    private String userId;
+
+    @Override
+    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
+        super.initialize(subject, callbackHandler, sharedState, options);
+
+        credentialsSupport = (CredentialsSupport) options.get("credsSupport");
+    }
+
+    @Nonnull
+    @Override
+    protected Set<Class> getSupportedCredentials() {
+        return new TestCredentialsSupport().getCredentialClasses();
+    }
+
+    @Override
+    public boolean login() throws LoginException {
+        credentials = getCredentials();
+        if (credentials != null) {
+            userId = credentialsSupport.getUserId(credentials);
+            sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
+            sharedState.put(SHARED_KEY_LOGIN_NAME, credentialsSupport.getUserId(credentials));
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean commit() throws LoginException {
+        if (userId != null) {
+            subject.getPrincipals().add(EveryonePrincipal.getInstance());
+            setAuthInfo(new AuthInfoImpl(userId, credentialsSupport.getAttributes(credentials), subject.getPrincipals()), subject);
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplOSGiTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplOSGiTest.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplOSGiTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplOSGiTest.java Thu Aug 11 13:11:18 2016
@@ -16,15 +16,22 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.token;
 
+import java.util.Hashtable;
+import javax.jcr.SimpleCredentials;
+
 import com.google.common.collect.ImmutableMap;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
 import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
 import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
 import org.junit.Rule;
 import org.junit.Test;
+import org.osgi.framework.ServiceRegistration;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 public class TokenConfigurationImplOSGiTest extends AbstractSecurityTest {
 
@@ -33,13 +40,20 @@ public class TokenConfigurationImplOSGiT
 
     private final TokenConfigurationImpl tokenConfiguration = new TokenConfigurationImpl();
 
+    private SimpleCredentials sc;
+
     @Override
     public void before() throws Exception {
         super.before();
 
+        tokenConfiguration.setSecurityProvider(getSecurityProvider());
+
         context.registerInjectActivateService(tokenConfiguration, ImmutableMap.<String, Object>of(
                 TokenProvider.PARAM_TOKEN_EXPIRATION, 25,
                 TokenProvider.PARAM_TOKEN_LENGTH, 4));
+
+        sc = new SimpleCredentials(getTestUser().getID(), new char[0]);
+        sc.setAttribute(TokenConstants.TOKEN_ATTRIBUTE, "");
     }
 
     @Test
@@ -48,4 +62,37 @@ public class TokenConfigurationImplOSGiT
         assertEquals(25, params.getConfigValue(TokenProvider.PARAM_TOKEN_EXPIRATION, TokenProviderImpl.DEFAULT_TOKEN_EXPIRATION).longValue());
         assertEquals(4, params.getConfigValue(TokenProvider.PARAM_TOKEN_LENGTH, TokenProviderImpl.DEFAULT_KEY_SIZE).intValue());
     }
+
+    @Test
+    public void testDefaultCredentialsSupport() throws Exception {
+        TokenProvider tp = tokenConfiguration.getTokenProvider(root);
+        assertTrue(tp.doCreateToken(sc));
+    }
+
+    @Test
+    public void testBindCredentialsSupport() {
+        context.registerService(CredentialsSupport.class, new TestCredentialsSupport(sc.getUserID()));
+
+        TokenProvider tp = tokenConfiguration.getTokenProvider(root);
+
+        assertFalse(tp.doCreateToken(sc));
+        assertTrue(tp.doCreateToken(new TestCredentialsSupport.Creds()));
+    }
+
+    @Test
+    public void testUnbindCredentialsSupport() {
+        CredentialsSupport testSupport = new TestCredentialsSupport(sc.getUserID());
+        ServiceRegistration registration = context.bundleContext().registerService(CredentialsSupport.class.getName(), testSupport, new Hashtable());
+
+        TokenProvider tp = tokenConfiguration.getTokenProvider(root);
+        assertFalse(tp.doCreateToken(sc));
+        assertTrue(tp.doCreateToken(new TestCredentialsSupport.Creds()));
+
+        registration.unregister();
+
+        tp = tokenConfiguration.getTokenProvider(root);
+        assertTrue(tp.doCreateToken(sc));
+        assertFalse(tp.doCreateToken(new TestCredentialsSupport.Creds()));
+
+    }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleCredentialsSupportTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleCredentialsSupportTest.java?rev=1755975&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleCredentialsSupportTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModuleCredentialsSupportTest.java Thu Aug 11 13:11:18 2016
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.authentication.token;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.apache.jackrabbit.oak.spi.security.authentication.token.CompositeTokenConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class TokenLoginModuleCredentialsSupportTest extends AbstractSecurityTest {
+
+    private String userId;
+    private TokenConfigurationImpl tc;
+    private CredentialsSupport credentialsSupport;
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+
+        userId = getTestUser().getID();
+        credentialsSupport = new TestCredentialsSupport(userId);
+
+        CompositeTokenConfiguration composite = ((CompositeTokenConfiguration) getSecurityProvider().getConfiguration(TokenConfiguration.class));
+        tc = (TokenConfigurationImpl) composite.getDefaultConfig();
+        tc.bindCredentialsSupport(credentialsSupport);
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            tc.unbindCredentialsSupport(credentialsSupport);
+        } finally {
+            root.refresh();
+            super.after();
+        }
+    }
+
+    @Override
+    protected Configuration getConfiguration() {
+        return new Configuration() {
+            @Override
+            public AppConfigurationEntry[] getAppConfigurationEntry(String s) {
+                AppConfigurationEntry tokenEntry = new AppConfigurationEntry(
+                        TokenLoginModule.class.getName(),
+                        AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT,
+                        Collections.<String, Object>emptyMap());
+
+                AppConfigurationEntry testEntry = new AppConfigurationEntry(
+                        TestLoginModule.class.getName(),
+                        AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT,
+                        ImmutableMap.of("credsSupport", credentialsSupport));
+
+                AppConfigurationEntry defaultEntry = new AppConfigurationEntry(
+                        LoginModuleImpl.class.getName(),
+                        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                        Collections.<String, Object>emptyMap());
+
+                return new AppConfigurationEntry[] {tokenEntry, testEntry, defaultEntry};
+            }
+        };
+    }
+
+    @Test
+    public void testCustomCredentials() throws Exception {
+        TestCredentialsSupport.Creds credentials = new TestCredentialsSupport.Creds();
+
+        ContentSession cs = login(credentials);
+        try {
+            assertEquals(userId, cs.getAuthInfo().getUserID());
+
+            Map<String, ?> attributes = credentialsSupport.getAttributes(credentials);
+            String token = attributes.get(TokenConstants.TOKEN_ATTRIBUTE).toString();
+            assertFalse(token.isEmpty());
+
+            cs.close();
+
+            cs = login(new TokenCredentials(token));
+            assertEquals(userId, cs.getAuthInfo().getUserID());
+        } finally {
+            cs.close();
+        }
+    }
+}

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java Thu Aug 11 13:11:18 2016
@@ -71,5 +71,26 @@ public class SimpleCredentialsSupportTes
         assertEquals(expected, attributes);
     }
 
+    @Test
+    public void testSetAttributes() {
+        Map<String, ?> attributes = credentialsSupport.getAttributes(new TestCredentials());
+        assertNotNull(attributes);
+        assertTrue(attributes.isEmpty());
+
+        SimpleCredentials sc = new SimpleCredentials("uid", new char[0]);
+
+        Map<String, ?> expected = ImmutableMap.of("a", "a", "b", Boolean.TRUE, "c", new TestCredentials());
+        credentialsSupport.setAttributes(sc, expected);
+
+        for (Map.Entry<String, ?> entry : expected.entrySet()) {
+            assertEquals(entry.getValue(), sc.getAttribute(entry.getKey()));
+        }
+
+        attributes = credentialsSupport.getAttributes(sc);
+        assertNotNull(attributes);
+        assertEquals(3, attributes.size());
+        assertEquals(expected, attributes);
+    }
+
     private static final class TestCredentials implements Credentials {}
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/token/default.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/token/default.md?rev=1755975&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/token/default.md (added)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/token/default.md Thu Aug 11 13:11:18 2016
@@ -0,0 +1,300 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+Token Management : The Default Implementation
+---------------------------------------------
+
+### General Notes
+
+The default implementation of the token management API stores login tokens along
+with the user's home directory in the repository. Along with the hash of the
+login token separated properties defining the expiration time of the token
+as well as as additional properties associated with the login tokens. This
+additional information may be mandatory (thus validated during the login) or
+optional. The optional properties are meant to have informative value only and
+will be transferred to public attributes as exposed by the [AuthInfo] present
+with each [ContentSession].
+
+### Token Management Operations
+
+#### Token Creation
+
+The creation of a new token is triggered by valid and supported `Credentials` 
+passed to the login module chain that contain an additional, empty `.token` attribute.
+
+The [TokenLoginModule] will obtain these `Credentials` from the shared state
+during the commit phase (i.e. phase 2 of the JAAS authentication) and will pass 
+them to the configured [TokenProvider] implementation the following sequence:
+
+    Credentials shared = getSharedCredentials();
+    if (shared != null && tokenProvider.doCreateToken(shared)) {
+        [...]
+        TokenInfo ti = tokenProvider.createToken(shared);
+        [...]
+    }
+    
+In case of success these steps will have generated a new token and stored 
+it's hash along with all mandatory and informative attributes to the new 
+content node  representing the token.
+
+##### Supported Credentials for Token Creation
+
+By default the implementation deals with shared `SimpleCredentials`.
+
+With Oak 1.5.8 the token management has been extended in order to allow 
+for custom `Credentials` implementations. This is achieved by registering 
+a custom implementation of the [CredentialsSupport] interface.
+The default the token management uses [SimpleCredentialsSupport].
+
+See also [OAK-4129] and section [Pluggability](#pluggability) below) for 
+additional information.
+
+#### Token Validation
+
+Once a token has been created it can be used for subsequent repository 
+logins with [TokenCredentials]. This time the [TokenLoginModule] will 
+attempt to perform the login phase (i.e. phase 1 of the JAAS authentication).
+ 
+This includes resolving the login token (`TokenProvider.getTokenInfo`) and
+asserting it's validity in case it exists. The validation consists of following steps:
+
+- check that the token has not expired (`TokenInfo.isExpired`)
+- verify that all mandatory attributes are present and match the expectations (`TokenInfo.matches`)
+
+Only if these steps have been successfully completed the login of the
+[TokenLoginModule] will succeed.
+ 
+#### Token Removal
+
+A given login token (and the node associated with it) will be removed if 
+the authentication fails due to an expired token or with an explicit API call 
+i.e. `TokenInfo.remove`.
+
+#### Resetting Expiration Time
+
+The default `TokenProvider` implementation will automatically reset the expiration
+time of a given token upon successful authentication.
+
+This behavior can be disabled by setting the `tokenRefresh` configuration parameter
+to `false` (see `PARAM_TOKEN_REFRESH` below). In this case expiration time will
+not be reset and an attempt to do so using the API (e.g. calling `
+TokenInfo.resetExpiration(long loginTime)`) will return `false` indicating
+that the expiration time has not been reset. The token will consequently expire
+and the user will need to login again using the configured login
+mechanism (e.g. using the credentials support for token creation).
+
+<a name="representation"/>
+### Representation in the Repository
+
+#### Content Structure
+
+The login tokens issued for a given user are all located underneath a node
+named `.tokens` that will be created by the `TokenProvider` once the first token
+is created. The default implementation creates a distinct node for each login
+token as described below
+
+    testUser {
+        "jcr:primaryType": "rep:User",
+        ...
+        ".tokens" {
+            "jcr:primaryType": "rep:Unstructured",
+            "2014-04-10T16.09.07.159+02.00" {
+                "jcr:primaryType": "rep:Token",
+                ...
+            "2014-05-07T12.08.57.683+02.00" {
+                "jcr:primaryType": "rep:Token",
+                ...
+            }
+            "2014-06-25T16.00.13.018+02.00" {
+                "jcr:primaryType": "rep:Token",
+                ...
+            }
+        }
+    }
+
+#### Token Nodes
+
+As of Oak 1.0 the login token are represented in the repository as follows:
+
+- the token node is referenceable with the dedicated node type `rep:Token` (used to be unstructured in Jackrabbit 2.x)
+- expiration and key properties are defined to be mandatory and protected
+- expiration time is obtained from `PARAM_TOKEN_EXPIRATION` specified in the
+  login attributes and falls back to the configuration parameter with the same
+  name as specified in the configuration options of the `TokenConfiguration`.
+
+The definition of the new built-in node type `rep:Token`:
+
+    [rep:Token] > mix:referenceable
+    - rep:token.key (STRING) protected mandatory
+    - rep:token.exp (DATE) protected mandatory
+    - * (UNDEFINED) protected
+    - * (UNDEFINED) multiple protected
+
+The following example illustrates the token nodes resulting from this node type
+definition:
+
+    testUser {
+            "jcr:primaryType": "rep:User",
+            ...
+            ".tokens" {
+                "2014-04-10T16.09.07.159+02.00" {
+                    "jcr:primaryType": "rep:Token",
+                    "jcr:uuid": "30c1f361-35a2-421a-9ebc-c781eb8a08f0",
+                    "rep:token.key": "{SHA-256}afaf64dba5d862f9-1000-3e2d4e58ac16189b9f2ac95d8d5b692e61cb06db437bcd9be5c10bdf3792356a",
+                    "rep:token.exp": "2014-04-11T04:09:07.159+02:00",
+                    ".token.ip": "0:0:0:0:0:0:0:1%0"
+                    ".token.otherMandatoryProperty": "expectedValue",
+                    "referer": "http://localhost:4502/crx/explorer/login.jsp"
+                    "otherInformalProperty": "somevalue"
+                },
+                "2014-05-07T12.08.57.683+02.00" {
+                    "jcr:primaryType": "rep:Token",
+                    "jcr:uuid": "c95c91e2-2e08-48ab-93db-6e7c8cdd6469",
+                    "rep:token.key": "{SHA-256}b1d268c55abda258-1000-62e4c368972260576d37e6ba14a10f9f02897e42992624890e22c522220f7e54",
+                    "rep:token.exp": "2014-05-08T00:08:57.683+02:00"
+                },
+                ...
+            }
+        }
+    }
+
+<a name="validation"/>
+### Validation
+
+The consistency of this content structure both on creation and modification is
+asserted by a dedicated `TokenValidator`. The corresponding errors are
+all of type `Constraint` with the following codes:
+
+| Code              | Message                                                  |
+|-------------------|----------------------------------------------------------|
+| 0060              | Attempt to create reserved token property in other ctx   |
+| 0061              | Attempt to change existing token key                     |
+| 0062              | Change primary type of existing node to rep:Token        |
+| 0063              | Creation/Manipulation of tokens without using provider   |
+| 0064              | Create a token outside of configured scope               |
+| 0065              | Invalid location of token node                           |
+| 0066              | Invalid token key                                        |
+| 0067              | Mandatory token expiration missing                       |
+| 0068              | Invalid location of .tokens node                         |
+| 0069              | Change type of .tokens parent node                       |
+
+<a name="configuration"/>
+### Configuration
+
+The default Oak [TokenConfiguration] allows to define the following configuration
+options for the `TokenProvider`:
+
+#### Configuration Parameters
+
+| Parameter                           | Type    | Default                  |
+|-------------------------------------|---------|--------------------------|
+| PARAM_TOKEN_EXPIRATION              | long    | 2 * 3600 * 1000 (2 hours)|
+| PARAM_TOKEN_LENGTH                  | int     | 8                        |
+| PARAM_TOKEN_REFRESH                 | boolean | true                     |
+| PARAM_PASSWORD_HASH_ALGORITHM       | String  | SHA-256                  |
+| PARAM_PASSWORD_HASH_ITERATIONS      | int     | 1000                     |
+| PARAM_PASSWORD_SALT_SIZE            | int     | 8                        |
+| | | |
+
+
+<a name="pluggability"/>
+### Pluggability
+
+In an OSGi-based setup the default `TokenConfiguration` you can bind a 
+custom implementation of the [CredentialsSupport] interface. Doing so 
+allows to support any type of custom credentials, which do not reveal
+the ID of the user logging into repository.
+
+In particular when chaining the `TokenLoginModule` and the `ExternalLoginModule`
+the [CredentialsSupport] can be used to authenticate and synchronize 
+users provided by third party systems during phase 1 (login) and generate 
+a login token during phase 2 (commit). See section 
+[Authentication with the External Login Module](../externalloginmodule.html)
+for additional details. For this to work the same [CredentialsSupport] 
+must be configured with the [ExternalIdentityProvider] and the `TokenConfiguration`
+and `CredentialsSupport.getUserId` must reveal the ID of the synced user (i.e. `ExternalUser.getId`).
+
+In general the following steps are required in order to plug a different `CredentialsSupport`
+into the default `TokenConfiguration`:
+
+- implement the `CredentialsSupport` interface (e.g. as extension to the `ExternalIdentityProvider`)
+- make sure the implementation is an OSGi service and deploy it to the Oak repository.
+
+##### Examples
+
+###### Example CredentialsSupport
+
+In an OSGi-based setup it's sufficient to make the service available to the repository
+in order to enable a custom `CredentialsSupport`.
+
+    @Component
+    @Service(value = {CredentialsSupport.class})
+    /**
+     * Custom implementation of the {@code CredentialsSupport} interface.
+     */
+    final class MyCredentialsSupport implements CredentialsSupport {
+
+        @Nonnull
+        @Override
+        public Set<Class> getCredentialClasses() {
+            return ImmutableSet.<Class>of(MyCredentials.class);
+        }
+
+        @CheckForNull
+        @Override
+        public String getUserId(@Nonnull Credentials credentials) {
+            if (credentials instanceof MyCredentials) {
+                // TODO: resolve user id
+                return resolveUserId(credentials);
+            } else {
+                return null;
+            }
+        }
+
+        @Nonnull
+        @Override
+        public Map<String, ?> getAttributes(@Nonnull Credentials credentials) {
+            // TODO: optional implementation
+            return ImmutableMap.of();
+        }
+
+        @Override
+        public boolean setAttributes(@Nonnull Credentials credentials, @Nonnull Map<String, ?> attributes) {
+           // TODO: optional implementation
+           return false;
+        }
+        
+        [...]
+    }
+    
+###### Example CredentialsSupport in Combination with External Authentication
+
+See section [Authentication with the External Login Module](../externalloginmodule.html#pluggability) 
+for an example.
+
+<!-- references -->
+
+[TokenCredentials]: http://svn.apache.org/repos/asf/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/authentication/token/TokenCredentials.java
+[AuthInfo]: /oak/docs/apidocs/org/apache/jackrabbit/oak/api/AuthInfo.html
+[ContentSession]: /oak/docs/apidocs/org/apache/jackrabbit/oak/api/ContentSession.html
+[TokenProvider]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/token/TokenProvider.html
+[TokenInfo]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/token/TokenInfo.html
+[TokenLoginModule]: /oak/docs/apidocs/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.html
+[CredentialsSupport]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.html
+[SimpleCredentialsSupport]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.html
+[OAK-4129]: https://issues.apache.org/jira/browse/OAK-4129
+[ExternalIdentityProvider]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.html
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/tokenmanagement.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/tokenmanagement.md?rev=1755975&r1=1755974&r2=1755975&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/tokenmanagement.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/tokenmanagement.md Thu Aug 11 13:11:18 2016
@@ -59,7 +59,16 @@ authentication phases behave as follows:
   create a new instance of `TokenCredentials`, push the public attributes to the
   shared stated and update the subject with the new credentials;
   finally the commit call **returns `false`**
+  
+##### Example JAAS Configuration
+
+  jackrabbit.oak {
+       org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule sufficient;
+       org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl required;
+   };
+
 
+<a name="api_extensions"/>
 ### Token Management API
 
 Oak 1.0 defines the following interfaces used to manage login tokens:
@@ -71,174 +80,32 @@ Oak 1.0 defines the following interfaces
 In addition Oak comes with a default implementation of the provider interface
 that is able to aggregate multiple `TokenProvider`s:
 
-- [CompositeTokenProvider]
-
-
-### Characteristics of the TokenProvider Implementation
-
-The default implementation of the token management API stores login tokens along
-with the user's home directory in the repository. Along with the hash of the
-login token separated properties defining the expiration time of the token
-as well as as additional properties associated with the login tokens. This
-additional information may be mandatory (thus validated during the login) or
-optional. The optional properties are meant to have informative value only and
-will be transferred to public attributes as exposed by the [AuthInfo] present
-with each [ContentSession].
-
-#### Token Creation
-
-The creation of a new token is triggered by valid `SimpleCredentials` passed
-to the login module chain that contain an additional, empty `.token` attribute.
-The default `TokenProvider` implementation will consequently generate a new
-token and store it's hash along with all mandatory and informative attributes
-to the new content node representing the new token.
-
-#### Token Removal
-
-In the default implementation a given login token (and the node associated with it)
-will be removed if the authentication fails due to an expired token.
-
-#### Resetting Expiration Time
-
-The default `TokenProvider` implementation will automatically reset the expiration
-time of a given token upon successful authentication.
-
-This behavior can be disabled by setting the `tokenRefresh` configuration parameter
-to `false` (see `PARAM_TOKEN_REFRESH` below). In this case expiration time will
-not be reset and an attempt to do so using the API (e.g. calling `
-TokenInfo.resetExpiration(long loginTime)`) will return `false` indicating
-that the expiration time has not been reset. The token will consequently expire
-and the user will need to login again using the configured default login
-mechanism (e.g. using `SimpleCredentials`).
-
-#### Token Representation in the Repository
-
-##### Content Structure
-
-The login tokens issued for a given user are all located underneath a node
-named `.tokens` that will be created by the `TokenProvider` once the first token
-is created. The default implementation creates a distinct node for each login
-token as described below
-
-    testUser {
-        "jcr:primaryType": "rep:User",
-        ...
-        ".tokens" {
-            "jcr:primaryType": "rep:Unstructured",
-            "2014-04-10T16.09.07.159+02.00" {
-                "jcr:primaryType": "rep:Token",
-                ...
-            "2014-05-07T12.08.57.683+02.00" {
-                "jcr:primaryType": "rep:Token",
-                ...
-            }
-            "2014-06-25T16.00.13.018+02.00" {
-                "jcr:primaryType": "rep:Token",
-                ...
-            }
-        }
-    }
-
-##### Token Nodes
-
-As of Oak 1.0 the login token are represented in the repository as follows:
+- [CompositeTokenConfiguration]: Extension of the `CompositeConfiguration` to combined different token management implementations.
+- [CompositeTokenProvider]: Aggregation of the `TokenProvider` implementations defined by the configurations contained the `CompositeTokenConfiguration`
 
-- the token node is referenceable with the dedicated node type `rep:Token` (used to be unstructured in Jackrabbit 2.x)
-- expiration and key properties are defined to be mandatory and protected
-- expiration time is obtained from `PARAM_TOKEN_EXPIRATION` specified in the
-  login attributes and falls back to the configuration parameter with the same
-  name as specified in the configuration options of the `TokenConfiguration`.
-
-The definition of the new built-in node type `rep:Token`:
-
-    [rep:Token] > mix:referenceable
-    - rep:token.key (STRING) protected mandatory
-    - rep:token.exp (DATE) protected mandatory
-    - * (UNDEFINED) protected
-    - * (UNDEFINED) multiple protected
-
-The following example illustrates the token nodes resulting from this node type
-definition:
-
-    testUser {
-            "jcr:primaryType": "rep:User",
-            ...
-            ".tokens" {
-                "2014-04-10T16.09.07.159+02.00" {
-                    "jcr:primaryType": "rep:Token",
-                    "jcr:uuid": "30c1f361-35a2-421a-9ebc-c781eb8a08f0",
-                    "rep:token.key": "{SHA-256}afaf64dba5d862f9-1000-3e2d4e58ac16189b9f2ac95d8d5b692e61cb06db437bcd9be5c10bdf3792356a",
-                    "rep:token.exp": "2014-04-11T04:09:07.159+02:00",
-                    ".token.ip": "0:0:0:0:0:0:0:1%0"
-                    ".token.otherMandatoryProperty": "expectedValue",
-                    "referer": "http://localhost:4502/crx/explorer/login.jsp"
-                    "otherInformalProperty": "somevalue"
-                },
-                "2014-05-07T12.08.57.683+02.00" {
-                    "jcr:primaryType": "rep:Token",
-                    "jcr:uuid": "c95c91e2-2e08-48ab-93db-6e7c8cdd6469",
-                    "rep:token.key": "{SHA-256}b1d268c55abda258-1000-62e4c368972260576d37e6ba14a10f9f02897e42992624890e22c522220f7e54",
-                    "rep:token.exp": "2014-05-08T00:08:57.683+02:00"
-                },
-                ...
-            }
-        }
-    }
+See section [Pluggability](#pluggability) for an example.
 
-<a name="validation"/>
-##### Validation
+<a href="default_implementation"/>
+### Characteristics of the Default Implementation
 
-The consistency of this content structure both on creation and modification is
-asserted by a dedicated `TokenValidator`. The corresponding errors are
-all of type `Constraint` with the following codes:
-
-| Code              | Message                                                  |
-|-------------------|----------------------------------------------------------|
-| 0060              | Attempt to create reserved token property in other ctx   |
-| 0061              | Attempt to change existing token key                     |
-| 0062              | Change primary type of existing node to rep:Token        |
-| 0063              | Creation/Manipulation of tokens without using provider   |
-| 0064              | Create a token outside of configured scope               |
-| 0065              | Invalid location of token node                           |
-| 0066              | Invalid token key                                        |
-| 0067              | Mandatory token expiration missing                       |
-| 0068              | Invalid location of .tokens node                         |
-| 0069              | Change type of .tokens parent node                       |
+The characteristics of the default token management implementation is
+described in section [Token Management : The Default Implementation](token/default.html). 
 
+<a name="configuration"/>
 ### Configuration
 
-The Oak token management comes with it's own [TokenConfiguration] which allows
-to obtain a new `TokenProvider` instance with the specified configuration options.
-
-Apart from the default configuration implementation Oak provides a public
-[CompositeTokenConfiguration], which is used to combined different implementations
-plugged at runtime.
-
-#### Configuration Parameters
-
-| Parameter                           | Type    | Default                  |
-|-------------------------------------|---------|--------------------------|
-| PARAM_TOKEN_EXPIRATION              | long    | 2 * 3600 * 1000 (2 hours)|
-| PARAM_TOKEN_LENGTH                  | int     | 8                        |
-| PARAM_TOKEN_REFRESH                 | boolean | true                     |
-| | | |
-
-
-#### Examples
-
-##### Example JAAS Configuration
-
-    jackrabbit.oak {
-         org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule sufficient;
-         org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl required;
-     };
+The configuration options of the default implementation are described in
+the [Configuration](token/default.html#configuration) section.
 
 
+<a name="pluggability"/>
 ### Pluggability
 
-The default security setup as present with Oak 1.0 is able to provide custom
-`TokenProvider` implementations and will automatically combine the
-different implementations using the `CompositeTokenProvider`.
+The default security setup as present with Oak 1.0 is able to deal with 
+custom token management implementations and will collect multiple
+implementations within `CompositeTokenConfiguration` present with the
+`SecurityProvider`. The `CompositeTokenConfiguration` itself will 
+combine the different `TokenProvider` implementations using the `CompositeTokenProvider`.
 
 In an OSGi setup the following steps are required in order to add a custom
 token provider implementation:
@@ -291,4 +158,5 @@ token provider implementation:
 [ContentSession]: /oak/docs/apidocs/org/apache/jackrabbit/oak/api/ContentSession.html
 [TokenProvider]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/token/TokenProvider.html
 [TokenInfo]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/token/TokenInfo.html
+[CompositeTokenConfiguration]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/token/CompositeTokenConfiguration.html
 [CompositeTokenProvider]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/token/CompositeTokenProvider.html
\ No newline at end of file