Refresh tokens work for applications for simple cases now.
diff --git a/component-test/src/main/java/TestApplications.java b/component-test/src/main/java/TestApplications.java
index 1ce8d10..94062c3 100644
--- a/component-test/src/main/java/TestApplications.java
+++ b/component-test/src/main/java/TestApplications.java
@@ -14,32 +14,43 @@
  * limitations under the License.
  */
 
+import io.mifos.anubis.api.v1.TokenConstants;
 import io.mifos.anubis.api.v1.domain.AllowedOperation;
 import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.anubis.token.TenantRefreshTokenSerializer;
+import io.mifos.anubis.token.TokenSerializationResult;
 import io.mifos.core.api.context.AutoUserContext;
+import io.mifos.core.api.util.FeignTargetWithCookieJar;
 import io.mifos.core.api.util.NotFoundException;
 import io.mifos.core.lang.security.RsaKeyPairFactory;
 import io.mifos.identity.api.v1.PermittableGroupIds;
+import io.mifos.identity.api.v1.client.IdentityManager;
+import io.mifos.identity.api.v1.domain.Authentication;
 import io.mifos.identity.api.v1.domain.CallEndpointSet;
 import io.mifos.identity.api.v1.domain.Permission;
+import io.mifos.identity.api.v1.domain.User;
 import io.mifos.identity.api.v1.events.*;
 import org.apache.commons.lang.RandomStringUtils;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author Myrle Krantz
  */
 public class TestApplications extends AbstractComponentTest {
 
+  private static final String CALL_ENDPOINT_SET_IDENTIFIER = "doughboy";
+
   @Test
   public void testSetApplicationSignature() throws InterruptedException {
     try (final AutoUserContext ignored
                  = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
-      final ApplicationSignatureEvent appPlusSig = setApplicationSignature();
+      final ApplicationSignatureTestData appPlusSig = setApplicationSignature();
 
       final List<String> foundApplications = getTestSubject().getApplications();
       Assert.assertTrue(foundApplications.contains(appPlusSig.getApplicationIdentifier()));
@@ -54,7 +65,7 @@
   public void testCreateAndDeleteApplicationPermission() throws InterruptedException {
     try (final AutoUserContext ignored
                  = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
-      final ApplicationSignatureEvent appPlusSig = setApplicationSignature();
+      final ApplicationSignatureTestData appPlusSig = setApplicationSignature();
 
       final Permission identityManagementPermission = new Permission();
       identityManagementPermission.setPermittableEndpointGroupIdentifier(PermittableGroupIds.IDENTITY_MANAGEMENT);
@@ -101,7 +112,7 @@
   public void testDeleteApplication() throws InterruptedException {
     try (final AutoUserContext ignored
                  = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
-      final ApplicationSignatureEvent appPlusSig = setApplicationSignature();
+      final ApplicationSignatureTestData appPlusSig = setApplicationSignature();
 
       getTestSubject().deleteApplication(appPlusSig.getApplicationIdentifier());
 
@@ -124,7 +135,7 @@
 
   @Test
   public void testApplicationPermissionUserApprovalProvisioning() throws InterruptedException {
-    final ApplicationSignatureEvent appPlusSig;
+    final ApplicationSignatureTestData appPlusSig;
     final Permission identityManagementPermission;
     try (final AutoUserContext ignored
                  = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
@@ -208,7 +219,7 @@
   public void manageApplicationEndpointSet() throws InterruptedException {
     try (final AutoUserContext ignored
                  = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
-      final ApplicationSignatureEvent appPlusSig = setApplicationSignature();
+      final ApplicationSignatureTestData appPlusSig = setApplicationSignature();
 
       final String endpointSetIdentifier = testEnvironment.generateUniqueIdentifer("epset");
       final CallEndpointSet endpointSet = new CallEndpointSet();
@@ -256,7 +267,7 @@
 
   @Test
   public void applicationIssuedRefreshTokenHappyCase() throws InterruptedException {
-    final ApplicationSignatureEvent appPlusSig;
+    final ApplicationSignatureTestData appPlusSig;
     final Permission rolePermission = buildRolePermission();
     final Permission userPermission = buildUserPermission();
     try (final AutoUserContext ignored
@@ -271,6 +282,14 @@
       Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
               new ApplicationPermissionEvent(appPlusSig.getApplicationIdentifier(),
                       userPermission.getPermittableEndpointGroupIdentifier())));
+      getTestSubject().createApplicationCallEndpointSet(
+              appPlusSig.getApplicationIdentifier(),
+              new CallEndpointSet(CALL_ENDPOINT_SET_IDENTIFIER,
+                      Arrays.asList(rolePermission.getPermittableEndpointGroupIdentifier(),
+                              userPermission.getPermittableEndpointGroupIdentifier())));
+      Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_CALLENDPOINTSET,
+              new ApplicationCallEndpointSetEvent(appPlusSig.getApplicationIdentifier(),
+                      CALL_ENDPOINT_SET_IDENTIFIER)));
     }
 
     final String userid;
@@ -289,8 +308,33 @@
               userPermission.getPermittableEndpointGroupIdentifier(),
               userid,
               true);
+      getTestSubject().setApplicationPermissionEnabledForUser(
+              appPlusSig.getApplicationIdentifier(),
+              rolePermission.getPermittableEndpointGroupIdentifier(),
+              userid,
+              true);
     }
-    //TODO: get me a refresh token here. use it to get an access token.  Then access like mad.
+
+    final TokenSerializationResult tokenSerializationResult =
+            new TenantRefreshTokenSerializer().build(new TenantRefreshTokenSerializer.Specification()
+                    .setUser(userid)
+                    .setEndpointSet(CALL_ENDPOINT_SET_IDENTIFIER)
+                    .setSecondsToLive(30)
+                    .setKeyTimestamp(appPlusSig.getKeyTimestamp())
+                    .setPrivateKey(appPlusSig.getKeyPair().privateKey())
+                    .setSourceApplication(appPlusSig.getApplicationIdentifier()));
+
+
+    final FeignTargetWithCookieJar<IdentityManager> identityManagerWithCookieJar
+            = apiFactory.createWithCookieJar(IdentityManager.class, testEnvironment.serverURI());
+
+    identityManagerWithCookieJar.putCookie("/token", TokenConstants.REFRESH_TOKEN_COOKIE_NAME, tokenSerializationResult.getToken());
+    final Authentication applicationAuthentication = identityManagerWithCookieJar.getFeignTarget().refresh();
+
+    try (final AutoUserContext ignored = new AutoUserContext(userid, applicationAuthentication.getAccessToken())) {
+      final List<User> users = getTestSubject().getUsers();
+      Assert.assertFalse(users.isEmpty());
+    }
   }
 
   private String createTestApplicationName()
@@ -298,15 +342,36 @@
     return "test" + RandomStringUtils.randomNumeric(3) + "-v1";
   }
 
-  private ApplicationSignatureEvent setApplicationSignature() throws InterruptedException {
+  static class ApplicationSignatureTestData {
+    private final String applicationIdentifier;
+    private final RsaKeyPairFactory.KeyPairHolder keyPair;
+
+    ApplicationSignatureTestData(final String applicationIdentifier, final RsaKeyPairFactory.KeyPairHolder keyPair) {
+      this.applicationIdentifier = applicationIdentifier;
+      this.keyPair = keyPair;
+    }
+
+    String getApplicationIdentifier() {
+      return applicationIdentifier;
+    }
+
+    RsaKeyPairFactory.KeyPairHolder getKeyPair() {
+      return keyPair;
+    }
+
+    String getKeyTimestamp() {
+      return keyPair.getTimestamp();
+    }
+  }
+
+  private ApplicationSignatureTestData setApplicationSignature() throws InterruptedException {
     final String testApplicationName = createTestApplicationName();
     final RsaKeyPairFactory.KeyPairHolder keyPair = RsaKeyPairFactory.createKeyPair();
     final Signature signature = new Signature(keyPair.getPublicKeyMod(), keyPair.getPublicKeyExp());
 
     getTestSubject().setApplicationSignature(testApplicationName, keyPair.getTimestamp(), signature);
 
-    final ApplicationSignatureEvent event = new ApplicationSignatureEvent(testApplicationName, keyPair.getTimestamp());
-    Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_PUT_APPLICATION_SIGNATURE, event));
-    return event;
+    Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_PUT_APPLICATION_SIGNATURE, new ApplicationSignatureEvent(testApplicationName, keyPair.getTimestamp())));
+    return new ApplicationSignatureTestData(testApplicationName, keyPair);
   }
 }
diff --git a/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java b/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java
index e5bcdca..d4bbe44 100644
--- a/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java
+++ b/service/src/main/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandler.java
@@ -31,6 +31,7 @@
 import io.mifos.core.lang.TenantContextHolder;
 import io.mifos.core.lang.config.TenantHeaderFilter;
 import io.mifos.core.lang.security.RsaPrivateKeyBuilder;
+import io.mifos.core.lang.security.RsaPublicKeyBuilder;
 import io.mifos.identity.api.v1.events.EventConstants;
 import io.mifos.identity.internal.command.AuthenticationCommandResponse;
 import io.mifos.identity.internal.command.PasswordAuthenticationCommand;
@@ -47,12 +48,15 @@
 import org.springframework.stereotype.Component;
 import org.springframework.util.Base64Utils;
 
+import javax.annotation.Nullable;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.ZoneId;
 import java.util.*;
+import java.util.stream.Collector;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -72,6 +76,10 @@
   private final TenantAccessTokenSerializer tenantAccessTokenSerializer;
   private final TenantRefreshTokenSerializer tenantRefreshTokenSerializer;
   private final TenantRsaKeyProvider tenantRsaKeyProvider;
+  private final ApplicationSignatures applicationSignatures;
+  private final ApplicationPermissions applicationPermissions;
+  private final ApplicationPermissionUsers applicationPermissionUsers;
+  private final ApplicationCallEndpointSets applicationCallEndpointSets;
   private final JmsTemplate jmsTemplate;
   private final Gson gson;
   private final Logger logger;
@@ -98,6 +106,10 @@
                                         final TenantRefreshTokenSerializer tenantRefreshTokenSerializer,
                                       @SuppressWarnings("SpringJavaAutowiringInspection")
                                         final TenantRsaKeyProvider tenantRsaKeyProvider,
+                                      final ApplicationSignatures applicationSignatures,
+                                      final ApplicationPermissions applicationPermissions,
+                                      final ApplicationPermissionUsers applicationPermissionUsers,
+                                      final ApplicationCallEndpointSets applicationCallEndpointSets,
                                       final JmsTemplate jmsTemplate,
                                       final ApplicationName applicationName,
                                       @Qualifier(IdentityConstants.JSON_SERIALIZER_NAME) final Gson gson,
@@ -111,6 +123,10 @@
     this.tenantAccessTokenSerializer = tenantAccessTokenSerializer;
     this.tenantRefreshTokenSerializer = tenantRefreshTokenSerializer;
     this.tenantRsaKeyProvider = tenantRsaKeyProvider;
+    this.applicationSignatures = applicationSignatures;
+    this.applicationPermissions = applicationPermissions;
+    this.applicationPermissionUsers = applicationPermissionUsers;
+    this.applicationCallEndpointSets = applicationCallEndpointSets;
     this.jmsTemplate = jmsTemplate;
     this.gson = gson;
     this.logger = logger;
@@ -147,21 +163,20 @@
       throw AmitAuthenticationException.userPasswordCombinationNotFound();
     }
 
-    final Optional<LocalDateTime> passwordExpiration = getExpiration(user);
-
-    final TokenSerializationResult accessToken = getAccessToken(
-            user.getIdentifier(),
-            getTokenPermissions(user, passwordExpiration, privateTenantInfo.getTimeToChangePasswordAfterExpirationInDays()),
-            privateSignature);
-
     final TokenSerializationResult refreshToken = getRefreshToken(user, privateSignature);
 
+    final AuthenticationCommandResponse ret = getAuthenticationResponse(
+            applicationName.toString(),
+            Optional.empty(),
+            privateTenantInfo,
+            privateSignature,
+            user,
+            refreshToken.getToken(),
+            refreshToken.getExpiration());
+
     fireAuthenticationEvent(user.getIdentifier());
 
-    return new AuthenticationCommandResponse(
-        accessToken.getToken(), DateConverter.toIsoString(accessToken.getExpiration()),
-        refreshToken.getToken(), DateConverter.toIsoString(refreshToken.getExpiration()),
-            passwordExpiration.map(DateConverter::toIsoString).orElse(null));
+    return ret;
   }
 
   private PrivateSignatureEntity checkedGetPrivateSignature() {
@@ -185,9 +200,16 @@
   private class TenantIdentityRsaKeyProvider implements TenantApplicationRsaKeyProvider {
     @Override
     public PublicKey getApplicationPublicKey(final String tokenApplicationName, final String timestamp) throws InvalidKeyTimestampException {
-      if (!applicationName.toString().equals(tokenApplicationName))
-        throw new IllegalArgumentException("Currently only supporting refresh tokens issued by identity");
-      return tenantRsaKeyProvider.getPublicKey(timestamp);
+      if (applicationName.toString().equals(tokenApplicationName))
+        return tenantRsaKeyProvider.getPublicKey(timestamp);
+
+      final ApplicationSignatureEntity signature = applicationSignatures.get(tokenApplicationName, timestamp)
+              .orElseThrow(() -> new InvalidKeyTimestampException(timestamp));
+
+      return new RsaPublicKeyBuilder()
+              .setPublicKeyMod(signature.getPublicKeyMod())
+              .setPublicKeyExp(signature.getPublicKeyExp())
+              .build();
     }
   }
 
@@ -202,18 +224,64 @@
     final PrivateSignatureEntity privateSignature = checkedGetPrivateSignature();
 
     final UserEntity user = getUser(deserializedRefreshToken.getUserIdentifier());
+    final String sourceApplicationName = deserializedRefreshToken.getSourceApplication();
 
+    return getAuthenticationResponse(
+            sourceApplicationName,
+            Optional.ofNullable(deserializedRefreshToken.getEndpointSet()),
+            privateTenantInfo,
+            privateSignature,
+            user,
+            command.getRefreshToken(),
+            LocalDateTime.ofInstant(deserializedRefreshToken.getExpiration().toInstant(), ZoneId.of("UTC")));
+  }
+
+  private AuthenticationCommandResponse getAuthenticationResponse(
+          final String sourceApplicationName,
+          @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+          final Optional<String> callEndpointSet,
+          final PrivateTenantInfoEntity privateTenantInfo,
+          final PrivateSignatureEntity privateSignature,
+          final UserEntity user,
+          final String refreshToken,
+          final LocalDateTime refreshTokenExpiration) {
     final Optional<LocalDateTime> passwordExpiration = getExpiration(user);
 
-    final TokenSerializationResult accessToken = getAccessToken(
+    final int gracePeriod = privateTenantInfo.getTimeToChangePasswordAfterExpirationInDays();
+    if (pastGracePeriod(passwordExpiration, gracePeriod))
+      throw AmitAuthenticationException.passwordExpired();
+
+    final Set<TokenPermission> tokenPermissions;
+
+    if (sourceApplicationName.equals(applicationName.toString())) { //ie, this is a token for the identity manager.
+      if (pastExpiration(passwordExpiration)) {
+        tokenPermissions = identityEndpointsAllowedEvenWithExpiredPassword();
+        logger.info("Password expired {}", passwordExpiration.map(LocalDateTime::toString).orElse("empty"));
+      }
+      else {
+        tokenPermissions = getUserTokenPermissions(user);
+      }
+    }
+    else {
+      tokenPermissions = getApplicationTokenPermissions(user, sourceApplicationName, callEndpointSet);
+    }
+
+    logger.info("Access token for tenant{}, user {}, application {}, and callEndpointSet {} being returned containing the permissions '{}'.",
+            TenantContextHolder.identifier().orElse("null"),
             user.getIdentifier(),
-            getTokenPermissions(user, passwordExpiration, privateTenantInfo.getTimeToChangePasswordAfterExpirationInDays()),
+            sourceApplicationName,
+            callEndpointSet.orElse("null"),
+            tokenPermissions.toString());
+
+    final TokenSerializationResult accessToken = getAuthenticationResponse(
+            user.getIdentifier(),
+            tokenPermissions,
             privateSignature);
 
     return new AuthenticationCommandResponse(
-        accessToken.getToken(), DateConverter.toIsoString(accessToken.getExpiration()),
-        command.getRefreshToken(), DateConverter.toIsoString(deserializedRefreshToken.getExpiration()),
-        passwordExpiration.map(DateConverter::toIsoString).orElse(null));
+            accessToken.getToken(), DateConverter.toIsoString(accessToken.getExpiration()),
+            refreshToken, DateConverter.toIsoString(refreshTokenExpiration),
+            passwordExpiration.map(DateConverter::toIsoString).orElse(null));
   }
 
   private Optional<LocalDateTime> getExpiration(final UserEntity user)
@@ -254,8 +322,8 @@
     );
   }
 
-  private TokenSerializationResult getAccessToken(
-          final String identifier,
+  private TokenSerializationResult getAuthenticationResponse(
+          final String userIdentifier,
           final Set<TokenPermission> tokenPermissions,
           final PrivateSignatureEntity privateSignatureEntity) {
 
@@ -270,48 +338,150 @@
               .setPrivateKey(privateKey)
               .setTokenContent(new TokenContent(new ArrayList<>(tokenPermissions)))
               .setSecondsToLive(accessTtl)
-              .setUser(identifier);
+              .setUser(userIdentifier);
 
       return tenantAccessTokenSerializer.build(x);
   }
 
-  private Set<TokenPermission> getTokenPermissions(
-          final UserEntity user,
-          @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
-          final Optional<LocalDateTime> passwordExpiration,
-          final long gracePeriod) throws AmitAuthenticationException {
+  private Set<TokenPermission> getUserTokenPermissions(
+          final UserEntity user) {
+
     final Optional<RoleEntity> userRole = roles.get(user.getRole());
-    final Set<TokenPermission> tokenPermissions;
+    final Set<TokenPermission> tokenPermissions = userRole
+            .map(r -> r.getPermissions().stream().flatMap(this::mapPermissions).collect(Collectors.toSet()))
+            .orElse(new HashSet<>());
 
-    if (pastGracePeriod(passwordExpiration, gracePeriod))
-      throw AmitAuthenticationException.passwordExpired();
+    tokenPermissions.addAll(identityEndpointsForEveryUser());
 
-    if (pastExpiration(passwordExpiration)) {
-      tokenPermissions = new HashSet<>();
-    }
-    else {
-      tokenPermissions = userRole.map(r -> r.getPermissions().stream().flatMap(this::mapPermissions).collect(Collectors.toSet()))
-              .orElse(new HashSet<>());
-    }
+    return tokenPermissions;
+  }
 
-    tokenPermissions.add(
-        new TokenPermission(
+  private Set<TokenPermission> getApplicationTokenPermissions(
+          final UserEntity user,
+          final String sourceApplicationName,
+          @SuppressWarnings("OptionalUsedAsFieldOrParameterType") final Optional<String> callEndpointSet) {
+    //If the call endpoint set was given, but does not correspond to a stored call endpoint set, throw an exception.
+    //If it wasn't given then return all of the permissions for the application.
+    final Optional<ApplicationCallEndpointSetEntity> applicationCallEndpointSet
+            = callEndpointSet.map(x -> applicationCallEndpointSets.get(sourceApplicationName, x)
+            .orElseThrow(AmitAuthenticationException::invalidToken));
+
+    final RoleEntity userRole = roles.get(user.getRole())
+            .orElseThrow(AmitAuthenticationException::userPasswordCombinationNotFound);
+
+    return applicationCallEndpointSet.map(x -> this.getApplicationCallEndpointSetTokenPermissions(user.getIdentifier(), userRole, x, sourceApplicationName))
+            .orElseGet(() -> this.getApplicationUserTokenPermissions(user.getIdentifier(), userRole, sourceApplicationName));
+  }
+
+  private Set<TokenPermission> getApplicationCallEndpointSetTokenPermissions(
+          final String userIdentifier,
+          final RoleEntity userRole,
+          final ApplicationCallEndpointSetEntity applicationCallEndpointSet,
+          final String sourceApplicationName) {
+    final List<PermissionType> permissionsForUser = userRole.getPermissions();
+    final Set<PermissionType> permissionsRequestedByApplication = applicationCallEndpointSet.getCallEndpointGroupIdentifiers().stream()
+            .map(x -> applicationPermissions.getPermissionForApplication(sourceApplicationName, x))
+            .filter(Optional::isPresent)
+            .map(Optional::get)
+            .collect(Collectors.toSet());
+
+    final Stream<PermissionType> applicationRequestedPermissionsTheUserHas
+            = intersectPermissionList(permissionsForUser, permissionsRequestedByApplication.stream());
+
+    final Set<PermissionType> permissionsPossible = applicationRequestedPermissionsTheUserHas
+            .filter(x ->
+                    applicationPermissionUsers.enabled(sourceApplicationName, x.getPermittableGroupIdentifier(), userIdentifier))
+            .collect(Collectors.toSet());
+
+    if (!permissionsPossible.containsAll(permissionsRequestedByApplication))
+      throw AmitAuthenticationException.applicationMissingPermissions(userIdentifier, sourceApplicationName);
+
+    return permissionsPossible.stream()
+            .flatMap(this::mapPermissions)
+            .collect(Collectors.toSet());
+  }
+
+  private Set<TokenPermission> getApplicationUserTokenPermissions(
+          final String userIdentifier,
+          final RoleEntity userRole,
+          final String sourceApplicationName) {
+    final List<PermissionType> permissionsForUser = userRole.getPermissions();
+    final List<PermissionType> permissionsRequestedByApplication = applicationPermissions.getAllPermissionsForApplication(sourceApplicationName);
+
+    final Stream<PermissionType> applicationRequestedPermissionsTheUserHas
+            = intersectPermissionList(permissionsForUser, permissionsRequestedByApplication.stream());
+
+    return applicationRequestedPermissionsTheUserHas
+            .filter(x ->
+                    applicationPermissionUsers.enabled(sourceApplicationName, x.getPermittableGroupIdentifier(), userIdentifier))
+            .flatMap(this::mapPermissions)
+            .collect(Collectors.toSet());
+  }
+
+  private Stream<PermissionType> intersectPermissionList(
+          final List<PermissionType> permissionsForUser,
+          final Stream<PermissionType> permissionsRequestedByApplication) {
+    final Map<String, Set<AllowedOperationType>> keyedUserPermissions = transformToSearchablePermissions(permissionsForUser);
+
+    return permissionsRequestedByApplication
+            .map(x -> new PermissionType(
+                    x.getPermittableGroupIdentifier(),
+                    intersectSets(keyedUserPermissions.get(x.getPermittableGroupIdentifier()), x.getAllowedOperations())))
+            .filter(x -> !x.getAllowedOperations().isEmpty());
+  }
+
+  static <T> Set<T> intersectSets(
+          final @Nullable Set<T> allowedOperations1,
+          final @Nullable Set<T> allowedOperations2) {
+    if (allowedOperations1 == null || allowedOperations2 == null)
+      return Collections.emptySet();
+
+    final Set<T> ret = new HashSet<>(allowedOperations1);
+    ret.retainAll(allowedOperations2);
+    return ret;
+  }
+
+  static Map<String, Set<AllowedOperationType>> transformToSearchablePermissions(final List<PermissionType> permissionsForUser) {
+    final Collector<Set<AllowedOperationType>, Set<AllowedOperationType>, Set<AllowedOperationType>> setToSetCollector
+            = Collector.of(
+            HashSet::new,
+            Set::addAll,
+            (x, y) -> {
+              final Set<AllowedOperationType> ret = new HashSet<>();
+              ret.addAll(x);
+              ret.addAll(y);
+              return ret;
+            });
+
+    return permissionsForUser.stream().collect(
+            Collectors.groupingBy(PermissionType::getPermittableGroupIdentifier,
+                    Collectors.mapping(PermissionType::getAllowedOperations, setToSetCollector)));
+  }
+
+  private Set<TokenPermission> identityEndpointsForEveryUser() {
+    final Set<TokenPermission> ret = identityEndpointsAllowedEvenWithExpiredPassword();
+
+    ret.add(new TokenPermission(
             applicationName + "/applications/*/permissions/*/users/{useridentifier}/enabled",
             AllowedOperation.ALL));
-    tokenPermissions.add(
-        new TokenPermission(
-            applicationName + "/users/{useridentifier}/password",
-            Collections.singleton(AllowedOperation.CHANGE)));
-    tokenPermissions.add(
-        new TokenPermission(
+    ret.add(new TokenPermission(
             applicationName + "/users/{useridentifier}/permissions",
             Collections.singleton(AllowedOperation.READ)));
-    tokenPermissions.add(
-        new TokenPermission(
+
+    return ret;
+  }
+
+  private Set<TokenPermission> identityEndpointsAllowedEvenWithExpiredPassword() {
+    final Set<TokenPermission> ret = new HashSet<>();
+
+    ret.add(new TokenPermission(
+            applicationName + "/users/{useridentifier}/password",
+            Collections.singleton(AllowedOperation.CHANGE)));
+    ret.add(new TokenPermission(
             applicationName + "/token/_current",
             Collections.singleton(AllowedOperation.DELETE)));
 
-    return tokenPermissions;
+    return ret;
   }
 
   static boolean pastExpiration(
diff --git a/service/src/test/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandlerTest.java b/service/src/test/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandlerTest.java
index 759cbb6..503a6c1 100644
--- a/service/src/test/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandlerTest.java
+++ b/service/src/test/java/io/mifos/identity/internal/command/handler/AuthenticationCommandHandlerTest.java
@@ -16,7 +16,10 @@
 package io.mifos.identity.internal.command.handler;
 
 import com.google.gson.Gson;
+import io.mifos.anubis.provider.TenantRsaKeyProvider;
 import io.mifos.anubis.token.TenantAccessTokenSerializer;
+import io.mifos.anubis.token.TenantRefreshTokenSerializer;
+import io.mifos.anubis.token.TokenDeserializationResult;
 import io.mifos.anubis.token.TokenSerializationResult;
 import io.mifos.core.lang.ApplicationName;
 import io.mifos.core.lang.DateConverter;
@@ -41,10 +44,9 @@
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.when;
@@ -82,11 +84,17 @@
     final ApplicationName applicationName = Mockito.mock(ApplicationName.class);
     final Gson gson = new Gson();
     final Logger logger = Mockito.mock(Logger.class);
+    final TenantRsaKeyProvider tenantRsaKeyProvider = Mockito.mock(TenantRsaKeyProvider.class);
+    final ApplicationSignatures applicationSignatures = Mockito.mock(ApplicationSignatures.class);
+    final ApplicationPermissions applicationPermissions = Mockito.mock(ApplicationPermissions.class);
+    final ApplicationPermissionUsers applicationPermissionUsers = Mockito.mock(ApplicationPermissionUsers.class);
+    final ApplicationCallEndpointSets applicationCallEndpointSets = Mockito.mock(ApplicationCallEndpointSets.class);
 
     commandHandler = new AuthenticationCommandHandler(
         users, roles, permittableGroups, signatures, tenants,
         hashGenerator,
-        tenantAccessTokenSerializer, tenantRefreshTokenSerializer,
+        tenantAccessTokenSerializer, tenantRefreshTokenSerializer, tenantRsaKeyProvider,
+            applicationSignatures, applicationPermissions, applicationPermissionUsers, applicationCallEndpointSets,
         jmsTemplate, applicationName,
         gson, logger);
 
@@ -123,8 +131,8 @@
     final TokenSerializationResult refreshTokenSerializationResult = new TokenSerializationResult("blah", LocalDateTime.now(ZoneId.of("UTC")).plusSeconds(REFRESH_TOKEN_TIME_TO_LIVE));
     when(tenantRefreshTokenSerializer.build(anyObject())).thenReturn(refreshTokenSerializationResult);
 
-    final TenantRefreshTokenSerializer.Deserialized deserialized = new TenantRefreshTokenSerializer.Deserialized(USER_NAME, Date.from(Instant.now().plusSeconds(REFRESH_TOKEN_TIME_TO_LIVE)), TEST_APPLICATION_NAME);
-    when(tenantRefreshTokenSerializer.deserialize(anyObject())).thenReturn(deserialized);
+    final TokenDeserializationResult deserialized = new TokenDeserializationResult(USER_NAME, Date.from(Instant.now().plusSeconds(REFRESH_TOKEN_TIME_TO_LIVE)), TEST_APPLICATION_NAME, null);
+    when(tenantRefreshTokenSerializer.deserialize(anyObject(), anyObject())).thenReturn(deserialized);
 
     when(hashGenerator.isEqual(any(), any(), any(), any(), anyInt(), anyInt())).thenReturn(true);
   }
@@ -174,28 +182,28 @@
   @Test
   public void correctDeterminationOfPasswordExpiration()
   {
-    final LocalDate passwordExpirationFromToday = LocalDate.now(ZoneId.of("UTC"));
-    Assert.assertTrue(AuthenticationCommandHandler.pastExpiration(passwordExpirationFromToday));
+    final LocalDateTime passwordExpirationFromToday = LocalDateTime.now(ZoneId.of("UTC"));
+    Assert.assertTrue(AuthenticationCommandHandler.pastExpiration(Optional.of(passwordExpirationFromToday)));
 
-    final LocalDate passwordExpirationFromYesterday = passwordExpirationFromToday.minusDays(1);
-    Assert.assertTrue(AuthenticationCommandHandler.pastExpiration(passwordExpirationFromYesterday));
+    final LocalDateTime passwordExpirationFromYesterday = passwordExpirationFromToday.minusDays(1);
+    Assert.assertTrue(AuthenticationCommandHandler.pastExpiration(Optional.of(passwordExpirationFromYesterday)));
 
-    final LocalDate passwordExpirationFromTommorrow = passwordExpirationFromToday.plusDays(1);
-    Assert.assertFalse(AuthenticationCommandHandler.pastExpiration(passwordExpirationFromTommorrow));
+    final LocalDateTime passwordExpirationFromTommorrow = passwordExpirationFromToday.plusDays(1);
+    Assert.assertFalse(AuthenticationCommandHandler.pastExpiration(Optional.of(passwordExpirationFromTommorrow)));
 
   }
 
   @Test
   public void correctDeterminationOfPasswordGracePeriod()
   {
-    final LocalDate passwordExpirationFromToday = LocalDate.now(ZoneId.of("UTC"));
-    Assert.assertFalse(AuthenticationCommandHandler.pastGracePeriod(passwordExpirationFromToday, GRACE_PERIOD));
+    final LocalDateTime passwordExpirationFromToday = LocalDateTime.now(ZoneId.of("UTC"));
+    Assert.assertFalse(AuthenticationCommandHandler.pastGracePeriod(Optional.of(passwordExpirationFromToday), GRACE_PERIOD));
 
-    final LocalDate nowJustWithinPasswordExpirationAndGracePeriod = passwordExpirationFromToday.minusDays(GRACE_PERIOD - 1);
-    Assert.assertFalse(AuthenticationCommandHandler.pastGracePeriod(nowJustWithinPasswordExpirationAndGracePeriod, GRACE_PERIOD));
+    final LocalDateTime nowJustWithinPasswordExpirationAndGracePeriod = passwordExpirationFromToday.minusDays(GRACE_PERIOD - 1);
+    Assert.assertFalse(AuthenticationCommandHandler.pastGracePeriod(Optional.of(nowJustWithinPasswordExpirationAndGracePeriod), GRACE_PERIOD));
 
-    final LocalDate nowJustOutsideOfPasswordExpirationAndGracePeriod = passwordExpirationFromToday.minusDays(GRACE_PERIOD);
-    Assert.assertTrue(AuthenticationCommandHandler.pastGracePeriod(nowJustOutsideOfPasswordExpirationAndGracePeriod, GRACE_PERIOD));
+    final LocalDateTime nowJustOutsideOfPasswordExpirationAndGracePeriod = passwordExpirationFromToday.minusDays(GRACE_PERIOD);
+    Assert.assertTrue(AuthenticationCommandHandler.pastGracePeriod(Optional.of(nowJustOutsideOfPasswordExpirationAndGracePeriod), GRACE_PERIOD));
   }
 
   @Test
@@ -212,4 +220,46 @@
     Assert.assertEquals(dateString, localDateTimeString);
     Assert.assertTrue(localDateTimeString.startsWith(localDateString.substring(0, localDateString.length()-1))); //(removing Z)
   }
+
+  @Test
+  public void intersectSets() {
+    final Set<AllowedOperationType> intersectionWithNull
+            = AuthenticationCommandHandler.intersectSets(new HashSet<>(), null);
+    Assert.assertTrue(intersectionWithNull.isEmpty());
+
+    final Set<AllowedOperationType> intersectionWithEqualSet
+            = AuthenticationCommandHandler.intersectSets(Collections.singleton(AllowedOperationType.CHANGE),
+            Collections.singleton(AllowedOperationType.CHANGE));
+    Assert.assertEquals(Collections.singleton(AllowedOperationType.CHANGE), intersectionWithEqualSet);
+
+    final Set<AllowedOperationType> intersectionWithAll
+            = AuthenticationCommandHandler.intersectSets(AllowedOperationType.ALL,
+            AllowedOperationType.ALL);
+    Assert.assertEquals(AllowedOperationType.ALL, intersectionWithAll);
+
+    final Set<AllowedOperationType> intersectionWithNonOverlappingSet
+            = AuthenticationCommandHandler.intersectSets(Collections.singleton(AllowedOperationType.DELETE),
+            Collections.singleton(AllowedOperationType.CHANGE));
+    Assert.assertTrue(intersectionWithNonOverlappingSet.isEmpty());
+
+    final Set<AllowedOperationType> intersectionWithSubSet
+            = AuthenticationCommandHandler.intersectSets(AllowedOperationType.ALL,
+            Collections.singleton(AllowedOperationType.CHANGE));
+    Assert.assertEquals(Collections.singleton(AllowedOperationType.CHANGE), intersectionWithSubSet);
+
+    final Set<AllowedOperationType> intersectionWithPartiallyOverlapping = AuthenticationCommandHandler.intersectSets(
+            Stream.of(AllowedOperationType.CHANGE, AllowedOperationType.DELETE).collect(Collectors.toSet()),
+            Stream.of(AllowedOperationType.CHANGE, AllowedOperationType.READ).collect(Collectors.toSet()));
+    Assert.assertEquals(Collections.singleton(AllowedOperationType.CHANGE), intersectionWithPartiallyOverlapping);
+  }
+
+  @Test
+  public void transformToSearchablePermissions()
+  {
+    Map<String, Set<AllowedOperationType>> x = AuthenticationCommandHandler.transformToSearchablePermissions(Arrays.asList(
+            new PermissionType("x", new HashSet<>(AllowedOperationType.ALL)),
+            new PermissionType("y", new HashSet<>(Collections.singletonList(AllowedOperationType.CHANGE)))));
+
+    Assert.assertEquals(AllowedOperationType.ALL, x.get("x"));
+  }
 }