org.cloudfoundry.identity.uaa.oauth.UaaTokenServicesTests.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.identity.uaa.oauth.UaaTokenServicesTests.java

Source

/*******************************************************************************
*     Cloud Foundry
*     Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
*
*     This product is licensed to you under the Apache License, Version 2.0 (the "License").
*     You may not use this product except in compliance with the License.
*
*     This product includes a number of subcomponents with
*     separate copyright notices and license terms. Your use of these
*     subcomponents is subject to the terms and conditions of the
*     subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.oauth;

import org.apache.commons.collections.map.HashedMap;
import org.cloudfoundry.identity.uaa.approval.Approval;
import org.cloudfoundry.identity.uaa.approval.Approval.ApprovalStatus;
import org.cloudfoundry.identity.uaa.approval.ApprovalStore;
import org.cloudfoundry.identity.uaa.audit.AuditEvent;
import org.cloudfoundry.identity.uaa.audit.AuditEventType;
import org.cloudfoundry.identity.uaa.audit.event.TokenIssuedEvent;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;
import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
import org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.oauth.approval.InMemoryApprovalStore;
import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt;
import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper;
import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants;
import org.cloudfoundry.identity.uaa.oauth.token.CompositeAccessToken;
import org.cloudfoundry.identity.uaa.oauth.token.RevocableToken;
import org.cloudfoundry.identity.uaa.oauth.token.RevocableTokenProvisioning;
import org.cloudfoundry.identity.uaa.oauth.token.TokenConstants;
import org.cloudfoundry.identity.uaa.oauth.token.matchers.AbstractOAuth2AccessTokenMatchers;
import org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2RefreshTokenMatchers;
import org.cloudfoundry.identity.uaa.test.MockAuthentication;
import org.cloudfoundry.identity.uaa.test.TestApplicationEventPublisher;
import org.cloudfoundry.identity.uaa.user.InMemoryUaaUserDatabase;
import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.user.UaaUserPrototype;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning;
import org.cloudfoundry.identity.uaa.zone.TokenPolicy;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import org.opensaml.saml2.core.AuthnContext;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.EMPTY_SET;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singleton;
import static org.cloudfoundry.identity.uaa.oauth.UaaTokenServices.UAA_REFRESH_TOKEN;
import static org.cloudfoundry.identity.uaa.oauth.client.ClientDetailsModification.SECRET;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.OPAQUE;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REQUEST_TOKEN_FORMAT;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.JWT;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.audience;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.cid;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.clientId;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.email;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.expiry;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.issuedAt;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.issuerUri;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.jwtId;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.origin;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.revocationSignature;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.scope;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.subject;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.userId;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.username;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.validFor;
import static org.cloudfoundry.identity.uaa.oauth.token.matchers.OAuth2AccessTokenMatchers.zoneId;
import static org.cloudfoundry.identity.uaa.user.UaaAuthority.USER_AUTHORITIES;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.core.AllOf.allOf;
import static org.hamcrest.number.OrderingComparison.greaterThan;
import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo;
import static org.hamcrest.text.IsEmptyString.isEmptyString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(Parameterized.class)
public class UaaTokenServicesTests {

    public static final String CLIENT_ID = "client";
    public static final String GRANT_TYPE = "grant_type";
    public static final String PASSWORD = "password";
    public static final String CLIENT_CREDENTIALS = "client_credentials";
    public static final String AUTHORIZATION_CODE = "authorization_code";
    public static final String REFRESH_TOKEN = "refresh_token";
    public static final String IMPLICIT = "implicit";
    public static final String CLIENT_AUTHORITIES = "read,update,write,openid";
    public static final String ISSUER_URI = "http://localhost:8080/uaa/oauth/token";
    public static final String READ = "read";
    public static final String WRITE = "write";
    public static final String DELETE = "delete";
    public static final String ALL_GRANTS_CSV = "authorization_code,password,implicit,client_credentials";
    public static final String CLIENTS = "clients";
    public static final String SCIM = "scim";
    public static final String OPENID = "openid";
    public static final String ROLES = "roles";
    public static final String PROFILE = "profile";

    private TestApplicationEventPublisher<TokenIssuedEvent> publisher;
    private UaaTokenServices tokenServices = new UaaTokenServices();

    private final int accessTokenValidity = 60 * 60 * 12;
    private final int refreshTokenValidity = 60 * 60 * 24 * 30;

    private List<GrantedAuthority> defaultUserAuthorities = Arrays.asList(
            UaaAuthority.authority("space.123.developer"), UaaAuthority.authority("uaa.user"),
            UaaAuthority.authority("space.345.developer"), UaaAuthority.authority("space.123.admin"),
            UaaAuthority.authority(OPENID), UaaAuthority.authority(READ), UaaAuthority.authority(WRITE),
            UaaAuthority.authority(UAA_REFRESH_TOKEN));

    private String userId = "12345";
    private String username = "jdsa";
    private String email = "jdsa@vmware.com";
    private final String externalId = "externalId";
    private UaaUser defaultUser = new UaaUser(
            new UaaUserPrototype().withId(userId).withUsername(username).withPassword(PASSWORD).withEmail(email)
                    .withAuthorities(defaultUserAuthorities).withGivenName("Marissa").withFamilyName("Bloggs")
                    .withPhoneNumber("1234567890").withCreated(new Date(System.currentTimeMillis() - 15000))
                    .withModified(new Date(System.currentTimeMillis() - 15000)).withOrigin(OriginKeys.UAA)
                    .withExternalId(externalId).withVerified(false).withZoneId(IdentityZoneHolder.get().getId())
                    .withSalt(userId).withPasswordLastModified(new Date(System.currentTimeMillis() - 15000))
                    .withLastLogonSuccess(12345L).withPreviousLogonSuccess(12365L));

    // Need to create a user with a modified time slightly in the past because
    // the token IAT is in seconds and the token
    // expiry
    // skew will not be long enough
    private InMemoryUaaUserDatabase userDatabase = new InMemoryUaaUserDatabase(singleton(defaultUser));

    private Authentication defaultUserAuthentication = new UsernamePasswordAuthenticationToken(
            new UaaPrincipal(defaultUser), "n/a", null);

    private InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService();

    private ApprovalStore approvalStore = new InMemoryApprovalStore();
    private MockAuthentication mockAuthentication;
    private List<String> requestedAuthScopes;
    private List<String> clientScopes;
    private List<String> readScope;
    private List<String> writeScope;
    private List<String> expandedScopes;
    public List<String> resourceIds;
    private String expectedJson;
    private BaseClientDetails defaultClient;
    private OAuth2RequestFactory requestFactory;
    private TokenPolicy tokenPolicy;
    private RevocableTokenProvisioning tokenProvisioning;
    private final Map<String, RevocableToken> tokens = new HashMap<>();
    private Calendar expiresAt = Calendar.getInstance();
    private Calendar updatedAt = Calendar.getInstance();

    @Rule
    public ExpectedException expectedEx = ExpectedException.none();

    private TestTokenEnhancer tokenEnhancer;
    private Set<String> thousandScopes;
    private CompositeAccessToken persistToken;
    private Date expiration;

    public UaaTokenServicesTests(TestTokenEnhancer enhancer, String testname) {
        publisher = TestApplicationEventPublisher.forEventClass(TokenIssuedEvent.class);
        this.tokenEnhancer = enhancer;
    }

    @Parameterized.Parameters(name = "{index}: testname[{1}")
    public static Collection<Object[]> data() {
        return Arrays
                .asList(new Object[][] { { null, "old behavior" }, { new TestTokenEnhancer(), "using enhancer" } });
    }

    @Before
    public void setUp() throws Exception {
        IdentityZoneHolder.clear();
        IdentityZoneProvisioning provisioning = mock(IdentityZoneProvisioning.class);
        IdentityZoneHolder.setProvisioning(provisioning);
        IdentityZone zone = IdentityZone.getUaa();
        IdentityZoneConfiguration config = new IdentityZoneConfiguration();
        tokenPolicy = new TokenPolicy(accessTokenValidity, refreshTokenValidity);
        Map<String, String> keys = new HashMap<>();
        keys.put("testKey", "9c247h8yt978w3nv45y978w45hntv6");
        keys.put("otherKey", "unc0uf98gv89egh4v98749978hv");
        tokenPolicy.setKeys(keys);
        tokenPolicy.setActiveKeyId("testKey");
        config.setTokenPolicy(tokenPolicy);
        zone.setConfig(config);
        when(provisioning.retrieve("uaa")).thenReturn(zone);

        mockAuthentication = new MockAuthentication();
        SecurityContextHolder.getContext().setAuthentication(mockAuthentication);
        requestedAuthScopes = Arrays.asList(READ, WRITE, OPENID);
        clientScopes = Arrays.asList(READ, WRITE, OPENID);
        readScope = Arrays.asList(READ);
        writeScope = Arrays.asList(WRITE);
        expandedScopes = Arrays.asList(READ, WRITE, DELETE, OPENID);
        resourceIds = Arrays.asList(SCIM, CLIENTS);
        expectedJson = "[\"" + READ + "\",\"" + WRITE + "\",\"" + OPENID + "\"]";

        defaultClient = new BaseClientDetails(CLIENT_ID, SCIM + "," + CLIENTS,
                READ + "," + WRITE + "," + OPENID + "," + UAA_REFRESH_TOKEN, ALL_GRANTS_CSV, CLIENT_AUTHORITIES);

        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, defaultClient));

        tokenProvisioning = mock(RevocableTokenProvisioning.class);
        when(tokenProvisioning.create(anyObject())).thenAnswer((Answer<RevocableToken>) invocation -> {
            RevocableToken arg = (RevocableToken) invocation.getArguments()[0];
            tokens.put(arg.getTokenId(), arg);
            return arg;
        });
        when(tokenProvisioning.update(anyString(), anyObject())).thenAnswer((Answer<RevocableToken>) invocation -> {
            String id = (String) invocation.getArguments()[0];
            RevocableToken arg = (RevocableToken) invocation.getArguments()[1];
            arg.setTokenId(id);
            tokens.put(arg.getTokenId(), arg);
            return arg;
        });
        when(tokenProvisioning.retrieve(anyString())).thenAnswer((Answer<RevocableToken>) invocation -> {
            String id = (String) invocation.getArguments()[0];
            RevocableToken result = tokens.get(id);
            if (result == null) {
                throw new EmptyResultDataAccessException(1);
            }
            return result;

        });

        AbstractOAuth2AccessTokenMatchers.revocableTokens.set(tokens);

        requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
        tokenServices.setClientDetailsService(clientDetailsService);
        tokenServices.setTokenPolicy(tokenPolicy);
        tokenServices.setDefaultUserAuthorities(AuthorityUtils.authorityListToSet(USER_AUTHORITIES));
        tokenServices.setIssuer("http://localhost:8080/uaa");
        tokenServices.setUserDatabase(userDatabase);
        tokenServices.setApprovalStore(approvalStore);
        tokenServices.setApplicationEventPublisher(publisher);
        tokenServices.setTokenProvisioning(tokenProvisioning);
        tokenServices.setUaaTokenEnhancer(tokenEnhancer);
        tokenServices.afterPropertiesSet();

        thousandScopes = new HashSet<>();
        for (int i = 0; i < 1000; i++) {
            thousandScopes.add(String.valueOf(i));
        }
        persistToken = new CompositeAccessToken("token-value");
        expiration = new Date(System.currentTimeMillis() + 10000);
        persistToken.setScope(thousandScopes);
        persistToken.setExpiration(expiration);
    }

    @After
    public void teardown() {
        AbstractOAuth2AccessTokenMatchers.revocableTokens.remove();
        IdentityZoneHolder.clear();
        tokens.clear();
    }

    @Test
    public void test_persist_scope_is_longer_than_1000_chars() throws Exception {
        tokenServices.persistRevocableToken("id", "rid", persistToken,
                new DefaultExpiringOAuth2RefreshToken("refresh-token-value", expiration), "clientId", "userId",
                true, true);

        ArgumentCaptor<RevocableToken> rt = ArgumentCaptor.forClass(RevocableToken.class);
        verify(tokenProvisioning, atLeast(1)).create(rt.capture());
        assertNotNull(rt.getAllValues());
        assertThat(rt.getAllValues().size(), greaterThanOrEqualTo(1));
        assertNotNull(rt.getAllValues().get(0));
        assertEquals(1000, rt.getAllValues().get(0).getScope().length());
    }

    @Test
    public void test_opaque_tokens_are_persisted() throws Exception {
        IdentityZoneHolder.get().getConfig().getTokenPolicy().setJwtRevocable(false);
        IdentityZoneHolder.get().getConfig().getTokenPolicy().setRefreshTokenFormat(JWT.getStringValue());
        CompositeAccessToken result = tokenServices.persistRevocableToken("id", "rid", persistToken,
                new DefaultExpiringOAuth2RefreshToken("refresh-token-value", expiration), "clientId", "userId",
                true, true);

        ArgumentCaptor<RevocableToken> rt = ArgumentCaptor.forClass(RevocableToken.class);
        verify(tokenProvisioning, times(2)).create(rt.capture());
        assertNotNull(rt.getAllValues());
        assertThat(rt.getAllValues().size(), equalTo(2));
        assertNotNull(rt.getAllValues().get(0));
        assertEquals(RevocableToken.TokenType.ACCESS_TOKEN, rt.getAllValues().get(0).getResponseType());
        assertEquals(RevocableToken.TokenFormat.OPAQUE.name(), rt.getAllValues().get(0).getFormat());
        assertEquals("id", result.getValue());
        assertEquals(RevocableToken.TokenType.REFRESH_TOKEN, rt.getAllValues().get(1).getResponseType());
        assertEquals(RevocableToken.TokenFormat.OPAQUE.name(), rt.getAllValues().get(1).getFormat());
        assertEquals("rid", result.getRefreshToken().getValue());
    }

    @Test
    public void test_refresh_tokens_are_uniquely_persisted() {
        IdentityZoneHolder.get().getConfig().getTokenPolicy().setRefreshTokenUnique(true);
        IdentityZoneHolder.get().getConfig().getTokenPolicy()
                .setRefreshTokenFormat(TokenConstants.TokenFormat.OPAQUE.getStringValue());
        tokenServices.persistRevocableToken("id", "rid", persistToken,
                new DefaultExpiringOAuth2RefreshToken("refresh-token-value", expiration), "clientId", "userId",
                true, true);
        ArgumentCaptor<RevocableToken> rt = ArgumentCaptor.forClass(RevocableToken.class);
        verify(tokenProvisioning, times(1)).deleteRefreshTokensForClientAndUserId("clientId", "userId");
        verify(tokenProvisioning, times(2)).create(rt.capture());
        RevocableToken refreshToken = rt.getAllValues().get(1);
        assertEquals(RevocableToken.TokenType.REFRESH_TOKEN, refreshToken.getResponseType());
    }

    @Test
    public void test_refresh_token_not_unique_when_set_to_false() {
        IdentityZoneHolder.get().getConfig().getTokenPolicy().setRefreshTokenUnique(false);
        tokenServices.persistRevocableToken("id", "rid", persistToken,
                new DefaultExpiringOAuth2RefreshToken("refresh-token-value", expiration), "clientId", "userId",
                true, true);
        ArgumentCaptor<RevocableToken> rt = ArgumentCaptor.forClass(RevocableToken.class);
        verify(tokenProvisioning, times(0)).deleteRefreshTokensForClientAndUserId(anyString(), anyString());
        verify(tokenProvisioning, times(2)).create(rt.capture());
        RevocableToken refreshToken = rt.getAllValues().get(1);
        assertEquals(RevocableToken.TokenType.REFRESH_TOKEN, refreshToken.getResponseType());
    }

    @Test
    public void test_jwt_no_token_is_not_persisted() throws Exception {
        IdentityZoneHolder.get().getConfig().getTokenPolicy().setRefreshTokenFormat(JWT.getStringValue());
        CompositeAccessToken result = tokenServices.persistRevocableToken("id", "rid", persistToken,
                new DefaultExpiringOAuth2RefreshToken("refresh-token-value", expiration), "clientId", "userId",
                false, false);

        ArgumentCaptor<RevocableToken> rt = ArgumentCaptor.forClass(RevocableToken.class);
        verify(tokenProvisioning, never()).create(rt.capture());
        assertEquals(persistToken.getValue(), result.getValue());
        assertEquals("refresh-token-value", result.getRefreshToken().getValue());
    }

    @Test
    public void test_opaque_refresh_token_is_persisted() throws Exception {
        IdentityZoneHolder.get().getConfig().getTokenPolicy()
                .setRefreshTokenFormat(TokenConstants.TokenFormat.OPAQUE.getStringValue());
        CompositeAccessToken result = tokenServices.persistRevocableToken("id", "rid", persistToken,
                new DefaultExpiringOAuth2RefreshToken("refresh-token-value", expiration), "clientId", "userId",
                false, false);

        ArgumentCaptor<RevocableToken> rt = ArgumentCaptor.forClass(RevocableToken.class);
        verify(tokenProvisioning, times(1)).create(rt.capture());
        assertNotNull(rt.getAllValues());
        assertEquals(1, rt.getAllValues().size());
        assertEquals(RevocableToken.TokenType.REFRESH_TOKEN, rt.getAllValues().get(0).getResponseType());
        assertEquals(RevocableToken.TokenFormat.OPAQUE.name(), rt.getAllValues().get(0).getFormat());
        assertEquals("refresh-token-value", rt.getAllValues().get(0).getValue());
        assertNotEquals("refresh-token-value", result.getRefreshToken().getValue());
    }

    @Test
    public void null_issuer_should_fail() throws URISyntaxException {
        tokenServices = new UaaTokenServices();
        tokenServices.setClientDetailsService(mock(ClientDetailsService.class));
        try {
            tokenServices.afterPropertiesSet();
            fail();
        } catch (IllegalArgumentException x) {
            assertTrue(x.getMessage().contains("issuer must be set"));
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void do_not_allow_set_of_null_issuer() throws URISyntaxException {
        tokenServices.setIssuer(null);
    }

    @Test(expected = URISyntaxException.class)
    public void do_not_allow_set_of_non_url_issuer() throws URISyntaxException {
        tokenServices.setIssuer("some bla bla bla");
    }

    @Test(expected = IllegalArgumentException.class)
    public void getTokenEndpoint_Fails_If_Issuer_Is_Wrong() throws Exception {
        Field field = UaaTokenServices.class.getDeclaredField("issuer");
        field.setAccessible(true);
        ReflectionUtils.setField(field, tokenServices, "adasdas");
        tokenServices.getTokenEndpoint();
    }

    @Test
    public void is_opaque_token_required() {
        defaultClient.setAutoApproveScopes(singleton("true"));
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResponseTypes(new HashSet(Arrays.asList(CompositeAccessToken.ID_TOKEN, "token")));
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, TokenConstants.GRANT_TYPE_USER_TOKEN);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;
        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        assertTrue(tokenServices.opaqueTokenRequired(authentication));
    }

    @Test(expected = InvalidTokenException.class)
    public void testNullRefreshTokenString() {
        tokenServices.refreshAccessToken(null, null);
    }

    @Test(expected = InvalidGrantException.class)
    public void testInvalidGrantType() {
        AuthorizationRequest ar = mock(AuthorizationRequest.class);
        tokenServices.refreshAccessToken("", requestFactory.createTokenRequest(ar, "dsdada"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testInvalidRefreshToken() {
        Map<String, String> map = new HashMap<>();
        map.put("grant_type", "refresh_token");
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(map, null, null, null, null, null,
                false, null, null, null);
        tokenServices.refreshAccessToken("dasdasdasdasdas",
                requestFactory.createTokenRequest(authorizationRequest, "refresh_token"));
    }

    @Test
    public void testCreateAccessTokenForAClient() {

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, clientScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, CLIENT_CREDENTIALS);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                null);

        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        assertCommonClientAccessTokenProperties(accessToken);
        assertThat(accessToken, validFor(is(accessTokenValidity)));
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, zoneId(is(IdentityZoneHolder.get().getId())));
        assertThat(accessToken.getRefreshToken(), is(nullValue()));
        validateExternalAttributes(accessToken);

        assertCommonEventProperties(accessToken, CLIENT_ID, expectedJson);
    }

    @Test
    public void test_refresh_token_is_opaque_when_requested() {
        OAuth2AccessToken accessToken = performPasswordGrant(TokenConstants.TokenFormat.OPAQUE.getStringValue());
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();

        String refreshTokenValue = accessToken.getRefreshToken().getValue();
        assertThat("Token value should be equal to or lesser than 36 characters", refreshTokenValue.length(),
                lessThanOrEqualTo(36));
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));
        TokenRequest refreshTokenRequest = getRefreshTokenRequest();

        //validate both opaque and JWT refresh tokens
        for (String s : Arrays.asList(refreshTokenValue, tokens.get(refreshTokenValue).getValue())) {
            OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(s, refreshTokenRequest);
            assertCommonUserAccessTokenProperties(refreshedAccessToken);
        }
    }

    @Test
    public void test_using_opaque_parameter_on_refresh_grant() {
        OAuth2AccessToken accessToken = performPasswordGrant(TokenConstants.TokenFormat.OPAQUE.getStringValue());
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        String refreshTokenValue = refreshToken.getValue();

        Map<String, String> parameters = new HashMap<>();
        parameters.put(REQUEST_TOKEN_FORMAT, OPAQUE);
        TokenRequest refreshTokenRequest = getRefreshTokenRequest(parameters);

        //validate both opaque and JWT refresh tokens
        for (String s : Arrays.asList(refreshTokenValue, tokens.get(refreshTokenValue).getValue())) {
            OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(s, refreshTokenRequest);
            assertThat("Token value should be equal to or lesser than 36 characters",
                    refreshedAccessToken.getValue().length(), lessThanOrEqualTo(36));
            assertCommonUserAccessTokenProperties(
                    new DefaultOAuth2AccessToken(tokens.get(refreshedAccessToken).getValue()));
        }
    }

    protected OAuth2AccessToken performPasswordGrant() {
        return performPasswordGrant(JWT.getStringValue());
    }

    protected OAuth2AccessToken performPasswordGrant(String tokenFormat) {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, PASSWORD);
        azParameters.put(REQUEST_TOKEN_FORMAT, tokenFormat);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        return tokenServices.createAccessToken(authentication);
    }

    @Test
    public void testCreateOpaqueAccessTokenForAClient() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, clientScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(REQUEST_TOKEN_FORMAT, TokenConstants.OPAQUE);
        azParameters.put(GRANT_TYPE, CLIENT_CREDENTIALS);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                null);

        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        assertTrue("Token is not a composite token", accessToken instanceof CompositeAccessToken);
        assertThat("Token value should be equal to or lesser than 36 characters", accessToken.getValue().length(),
                lessThanOrEqualTo(36));
        assertThat(accessToken.getRefreshToken(), is(nullValue()));
    }

    @Test
    public void testCreateAccessTokenForAClientInAnotherIdentityZone() {
        String subdomain = "test-zone-subdomain";
        IdentityZone identityZone = getIdentityZone(subdomain);
        identityZone.setConfig(JsonUtils.readValue(
                "{\"tokenPolicy\":{\"accessTokenValidity\":3600,\"refreshTokenValidity\":7200}}",
                IdentityZoneConfiguration.class));
        IdentityZoneHolder.set(identityZone);
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, clientScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, CLIENT_CREDENTIALS);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                null);

        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonClientAccessTokenProperties(accessToken);
        assertThat(accessToken, validFor(is(3600)));
        assertThat(accessToken, issuerUri(is("http://" + subdomain + ".localhost:8080/uaa/oauth/token")));
        assertThat(accessToken.getRefreshToken(), is(nullValue()));
        validateExternalAttributes(accessToken);

        Assert.assertEquals(1, publisher.getEventCount());

        this.assertCommonEventProperties(accessToken, CLIENT_ID, expectedJson);
    }

    private IdentityZone getIdentityZone(String subdomain) {
        IdentityZone identityZone = new IdentityZone();
        identityZone.setSubdomain(subdomain);
        identityZone.setName("The Twiglet Zone");
        identityZone.setDescription("Like the Twilight Zone but tastier.");
        return identityZone;
    }

    @Test
    public void testCreateAccessTokenAuthcodeGrant() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        validateAccessAndRefreshToken(accessToken);
    }

    @Test
    public void testCreateAccessTokenAuthcodeGrantSwitchedPrimaryKey() {
        String originalPrimaryKeyId = tokenPolicy.getActiveKeyId();
        try {
            tokenPolicy.setActiveKeyId("otherKey");

            AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
            authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
            Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
            azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
            authorizationRequest.setRequestParameters(azParameters);
            Authentication userAuthentication = defaultUserAuthentication;

            OAuth2Authentication authentication = new OAuth2Authentication(
                    authorizationRequest.createOAuth2Request(), userAuthentication);
            OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

            validateAccessAndRefreshToken(accessToken);
        } finally {
            tokenPolicy.setActiveKeyId(originalPrimaryKeyId);
        }
    }

    @Test
    public void testCreateAccessTokenPasswordGrant() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, PASSWORD);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        validateAccessAndRefreshToken(accessToken);
        tokenServices.loadAuthentication(accessToken.getValue());

        //ensure that we can load without user_name claim
        tokenServices.setExcludedClaims(new HashSet(
                Arrays.asList(ClaimConstants.AUTHORITIES, ClaimConstants.USER_NAME, ClaimConstants.EMAIL)));
        accessToken = tokenServices.createAccessToken(authentication);
        assertNotNull(tokenServices.loadAuthentication(accessToken.getValue()).getUserAuthentication());

    }

    @Test
    public void testClientSecret_Added_Token_Validation_Still_Works() {

        defaultClient.setClientSecret(SECRET);

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, PASSWORD);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        //normal token validation
        tokenServices.loadAuthentication(accessToken.getValue());

        //add a 2nd secret
        defaultClient.setClientSecret(defaultClient.getClientSecret() + " newsecret");
        tokenServices.loadAuthentication(accessToken.getValue());

        //generate a token when we have two secrets
        OAuth2AccessToken accessToken2 = tokenServices.createAccessToken(authentication);

        //remove the 1st secret
        defaultClient.setClientSecret("newsecret");
        try {
            tokenServices.loadAuthentication(accessToken.getValue());
            fail("Token should fail to validate on the revocation signature");
        } catch (InvalidTokenException e) {
            assertTrue(e.getMessage().contains("revocable signature mismatch"));
        }
        tokenServices.loadAuthentication(accessToken2.getValue());

        OAuth2AccessToken accessToken3 = tokenServices.createAccessToken(authentication);
        tokenServices.loadAuthentication(accessToken3.getValue());
    }

    @Test
    public void testCreateRevocableAccessTokenPasswordGrant() {
        OAuth2AccessToken accessToken = performPasswordGrant();

        validateAccessAndRefreshToken(accessToken);
    }

    protected void validateAccessAndRefreshToken(OAuth2AccessToken accessToken) {
        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));
        validateExternalAttributes(accessToken);

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));
    }

    protected void validateExternalAttributes(OAuth2AccessToken accessToken) {
        Map<String, String> extendedAttributes = (Map<String, String>) accessToken.getAdditionalInformation()
                .get(ClaimConstants.EXTERNAL_ATTR);
        if (tokenEnhancer != null) {
            Assert.assertEquals("test", extendedAttributes.get("purpose"));
        } else {
            assertNull("External attributes should not exist", extendedAttributes);
        }
    }

    @Test
    public void testCreateAccessTokenRefreshGrant() throws InterruptedException {
        OAuth2AccessToken accessToken = getOAuth2AccessToken();

        TokenRequest refreshTokenRequest = getRefreshTokenRequest();

        OAuth2AccessToken refreshedAccessToken = tokenServices
                .refreshAccessToken(accessToken.getRefreshToken().getValue(), refreshTokenRequest);

        assertEquals(refreshedAccessToken.getRefreshToken().getValue(), accessToken.getRefreshToken().getValue());

        this.assertCommonUserAccessTokenProperties(refreshedAccessToken);
        assertThat(refreshedAccessToken, issuerUri(is(ISSUER_URI)));
        assertThat(refreshedAccessToken, scope(is(requestedAuthScopes)));
        assertThat(refreshedAccessToken, validFor(is(60 * 60 * 12)));
        validateExternalAttributes(accessToken);
    }

    protected TokenRequest getRefreshTokenRequest() {
        return getRefreshTokenRequest(emptyMap());
    }

    protected TokenRequest getRefreshTokenRequest(Map<String, String> requestParameters) {
        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        refreshAuthorizationRequest.setRequestParameters(requestParameters);
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);
        return requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token");
    }

    @Test
    public void createAccessToken_usingRefreshGrant_inOtherZone() throws Exception {
        String subdomain = "test-zone-subdomain";
        IdentityZone identityZone = getIdentityZone(subdomain);
        identityZone.setConfig(JsonUtils.readValue(
                "{\"tokenPolicy\":{\"accessTokenValidity\":3600,\"refreshTokenValidity\":9600}}",
                IdentityZoneConfiguration.class));
        IdentityZoneHolder.set(identityZone);

        OAuth2AccessToken accessToken = getOAuth2AccessToken();

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(
                accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
        assertEquals(refreshedAccessToken.getRefreshToken().getValue(), accessToken.getRefreshToken().getValue());

        this.assertCommonUserAccessTokenProperties(refreshedAccessToken);
        assertThat(refreshedAccessToken,
                issuerUri(is("http://test-zone-subdomain.localhost:8080/uaa/oauth/token")));
        assertThat(refreshedAccessToken, scope(is(requestedAuthScopes)));
        assertThat(refreshedAccessToken, validFor(is(3600)));
        validateExternalAttributes(accessToken);
    }

    private OAuth2AccessToken getOAuth2AccessToken() {
        expiresAt.add(Calendar.MILLISECOND, 300000);
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(OPENID)
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        return tokenServices.createAccessToken(authentication);
    }

    @Test
    public void testCreateAccessTokenRefreshGrantAllScopesAutoApproved() throws InterruptedException {
        BaseClientDetails clientDetails = cloneClient(defaultClient);
        clientDetails.setAutoApproveScopes(singleton("true"));
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        // NO APPROVALS REQUIRED

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(
                accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));

        assertEquals(refreshedAccessToken.getRefreshToken().getValue(), accessToken.getRefreshToken().getValue());

        this.assertCommonUserAccessTokenProperties(refreshedAccessToken);
        assertThat(refreshedAccessToken, issuerUri(is(ISSUER_URI)));
        assertThat(refreshedAccessToken, scope(is(requestedAuthScopes)));
        assertThat(refreshedAccessToken, validFor(is(60 * 60 * 12)));
        assertThat(accessToken.getRefreshToken(), is(not(nullValue())));
    }

    @Test
    public void testCreateAccessTokenRefreshGrantSomeScopesAutoApprovedDowngradedRequest()
            throws InterruptedException {
        BaseClientDetails clientDetails = cloneClient(defaultClient);
        clientDetails.setAutoApproveScopes(singleton("true"));
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        // NO APPROVALS REQUIRED

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);

        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, readScope);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(
                accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));

        assertEquals(refreshedAccessToken.getRefreshToken().getValue(), accessToken.getRefreshToken().getValue());

        this.assertCommonUserAccessTokenProperties(refreshedAccessToken);
        assertThat(refreshedAccessToken, issuerUri(is(ISSUER_URI)));
        assertThat(refreshedAccessToken, validFor(is(60 * 60 * 12)));
        assertThat(accessToken.getRefreshToken(), is(not(nullValue())));
    }

    @Test
    public void testCreateAccessTokenRefreshGrantSomeScopesAutoApproved() throws InterruptedException {
        BaseClientDetails clientDetails = cloneClient(defaultClient);
        clientDetails.setAutoApproveScopes(readScope);
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        Calendar updatedAt = Calendar.getInstance();
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(OPENID)
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(
                accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));

        assertEquals(refreshedAccessToken.getRefreshToken().getValue(), accessToken.getRefreshToken().getValue());

        this.assertCommonUserAccessTokenProperties(refreshedAccessToken);
        assertThat(refreshedAccessToken, issuerUri(is(ISSUER_URI)));
        assertThat(refreshedAccessToken, validFor(is(60 * 60 * 12)));
        assertThat(accessToken.getRefreshToken(), is(not(nullValue())));
    }

    @Test(expected = InvalidTokenException.class)
    public void testCreateAccessTokenRefreshGrantNoScopesAutoApprovedIncompleteApprovals()
            throws InterruptedException {
        BaseClientDetails clientDetails = cloneClient(defaultClient);
        clientDetails.setAutoApproveScopes(Arrays.asList());
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        Calendar updatedAt = Calendar.getInstance();
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test
    public void testCreateAccessTokenRefreshGrantAllScopesAutoApprovedButApprovalDenied()
            throws InterruptedException {
        BaseClientDetails clientDetails = cloneClient(defaultClient);
        clientDetails.setAutoApproveScopes(requestedAuthScopes);
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        Calendar updatedAt = Calendar.getInstance();
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.DENIED)
                .setLastUpdatedAt(updatedAt.getTime()));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2AccessToken refreshedAccessToken = tokenServices.refreshAccessToken(
                accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
        assertNotNull(refreshedAccessToken);
    }

    @Test
    public void refreshTokenNotCreatedIfGrantTypeRestricted() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                defaultUserAuthentication);
        tokenServices.setRestrictRefreshGrant(true);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        assertThat(accessToken.getRefreshToken(), is(nullValue()));
    }

    @Test
    public void testCreateAccessTokenImplicitGrant() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, IMPLICIT);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));
        assertThat(accessToken.getRefreshToken(), is(nullValue()));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));
    }

    @Test
    public void create_id_token_with_roles_scope() {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID));
        assertTrue(idTokenJwt.getClaims().contains("\"amr\":[\"ext\",\"rba\",\"mfa\"]"));
    }

    @Test
    public void create_id_token_with_amr_claim() throws Exception {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID, ROLES));
        assertTrue(idTokenJwt.getClaims().contains("\"amr\":[\"ext\",\"rba\",\"mfa\"]"));
    }

    @Test
    public void create_id_token_with_acr_claim() throws Exception {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID, ROLES));
        assertTrue(idTokenJwt.getClaims().contains("\"" + ClaimConstants.ACR + "\":{\"values\":[\""));
    }

    @Test
    public void create_id_token_without_roles_scope() {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID));
        assertFalse(idTokenJwt.getClaims().contains("\"roles\""));
    }

    @Test
    public void create_id_token_with_profile_scope() throws Exception {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID, PROFILE));
        assertTrue(idTokenJwt.getClaims().contains("\"given_name\":\"" + defaultUser.getGivenName() + "\""));
        assertTrue(idTokenJwt.getClaims().contains("\"family_name\":\"" + defaultUser.getFamilyName() + "\""));
        assertTrue(idTokenJwt.getClaims().contains("\"phone_number\":\"" + defaultUser.getPhoneNumber() + "\""));
    }

    @Test
    public void create_id_token_without_profile_scope() throws Exception {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID));
        assertFalse(idTokenJwt.getClaims().contains("\"given_name\":"));
        assertFalse(idTokenJwt.getClaims().contains("\"family_name\":"));
        assertFalse(idTokenJwt.getClaims().contains("\"phone_number\":"));
    }

    @Test
    public void create_id_token_with_last_logon_time_claim() {
        Jwt idTokenJwt = getIdToken(Arrays.asList(OPENID));
        assertTrue(idTokenJwt.getClaims().contains("\"previous_logon_time\":12365"));
    }

    private Jwt getIdToken(List<String> scopes) {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, scopes);

        authorizationRequest.setResponseTypes(new HashSet<>(Arrays.asList(CompositeAccessToken.ID_TOKEN)));

        UaaPrincipal uaaPrincipal = new UaaPrincipal(defaultUser.getId(), defaultUser.getUsername(),
                defaultUser.getEmail(), defaultUser.getOrigin(), defaultUser.getExternalId(),
                defaultUser.getZoneId());
        UaaAuthentication userAuthentication = new UaaAuthentication(uaaPrincipal, null, defaultUserAuthorities,
                new HashSet<>(Arrays.asList("group1", "group2")), Collections.EMPTY_MAP, null, true,
                System.currentTimeMillis(), System.currentTimeMillis() + 1000l * 60l);
        Set<String> amr = new HashSet<>();
        amr.addAll(Arrays.asList("ext", "mfa", "rba"));
        userAuthentication.setAuthenticationMethods(amr);
        userAuthentication.setAuthContextClassRef(new HashSet<>(Arrays.asList(AuthnContext.PASSWORD_AUTHN_CTX)));
        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);

        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        Jwt tokenJwt = JwtHelper.decode(accessToken.getValue());
        SignatureVerifier verifier = KeyInfo.getKey(tokenJwt.getHeader().getKid()).getVerifier();
        tokenJwt.verifySignature(verifier);
        assertNotNull(tokenJwt);

        Jwt idToken = JwtHelper.decode(((CompositeAccessToken) accessToken).getIdTokenValue());
        idToken.verifySignature(verifier);
        return idToken;
    }

    @Test
    public void testCreateAccessWithNonExistingScopes() {
        List<String> scopesThatDontExist = Arrays.asList("scope1", "scope2");
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, scopesThatDontExist);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, IMPLICIT);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is(ISSUER_URI)));
        assertThat(accessToken, scope(is(scopesThatDontExist)));
        assertThat(accessToken, validFor(is(60 * 60 * 12)));
        assertThat(accessToken.getRefreshToken(), is(nullValue()));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(scopesThatDontExist));
    }

    @Test
    public void createAccessToken_forUser_inanotherzone() {
        String subdomain = "test-zone-subdomain";
        IdentityZone identityZone = getIdentityZone(subdomain);
        identityZone.setConfig(JsonUtils.readValue(
                "{\"tokenPolicy\":{\"accessTokenValidity\":3600,\"refreshTokenValidity\":9600}}",
                IdentityZoneConfiguration.class));
        IdentityZoneHolder.set(identityZone);

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(accessToken);
        assertThat(accessToken, issuerUri(is("http://test-zone-subdomain.localhost:8080/uaa/oauth/token")));
        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken, validFor(is(3600)));
        assertThat(accessToken.getRefreshToken(), is(not(nullValue())));

        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers
                .issuerUri(is("http://test-zone-subdomain.localhost:8080/uaa/oauth/token")));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(9600)));

        this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));
    }

    private String buildJsonString(List<String> list) {
        StringBuffer buf = new StringBuffer("[");
        int count = list.size();
        for (String s : list) {
            buf.append("\"");
            buf.append(s);
            buf.append("\"");
            if (--count > 0) {
                buf.append(",");
            }
        }
        buf.append("]");
        return buf.toString();
    }

    @Test
    public void testCreateAccessTokenAuthcodeGrantNarrowerScopes() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        Calendar updatedAt = Calendar.getInstance();
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        // First Request
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        assertThat(accessToken, scope(is(requestedAuthScopes)));
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        assertThat(refreshToken, is(not(nullValue())));

        assertThat(refreshToken, OAuth2RefreshTokenMatchers.scope(is(requestedAuthScopes)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.audience(is(resourceIds)));

        // Second request with reduced scopes
        AuthorizationRequest reducedScopeAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, readScope);
        reducedScopeAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(
                reducedScopeAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        reducedScopeAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2Authentication reducedScopeAuthentication = new OAuth2Authentication(
                reducedScopeAuthorizationRequest.createOAuth2Request(), userAuthentication);
        OAuth2AccessToken reducedScopeAccessToken = tokenServices.refreshAccessToken(
                accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(reducedScopeAuthorizationRequest, "refresh_token"));

        // AT should have the new scopes, RT should be the same
        assertThat(reducedScopeAccessToken, scope(is(readScope)));
        assertEquals(reducedScopeAccessToken.getRefreshToken(), accessToken.getRefreshToken());
    }

    @Test(expected = InvalidScopeException.class)
    public void testCreateAccessTokenAuthcodeGrantExpandedScopes() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));
        // First Request
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        assertThat(accessToken, scope(is(requestedAuthScopes)));
        assertThat(accessToken.getRefreshToken(), is(not(nullValue())));

        assertThat(accessToken.getRefreshToken(), OAuth2RefreshTokenMatchers.scope(is(requestedAuthScopes)));
        assertThat(accessToken.getRefreshToken(), OAuth2RefreshTokenMatchers.audience(is(resourceIds)));

        // Second request with expanded scopes
        AuthorizationRequest expandedScopeAuthorizationRequest = new AuthorizationRequest(CLIENT_ID,
                expandedScopes);
        expandedScopeAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(
                expandedScopeAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        expandedScopeAuthorizationRequest.setRequestParameters(refreshAzParameters);

        OAuth2Authentication expandedScopeAuthentication = new OAuth2Authentication(
                expandedScopeAuthorizationRequest.createOAuth2Request(), userAuthentication);
        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(expandedScopeAuthorizationRequest, "refresh_token"));
    }

    @Test
    public void testChangedExpiryForTokens() {
        BaseClientDetails clientDetails = cloneClient(defaultClient);
        clientDetails.setAccessTokenValiditySeconds(3600);
        clientDetails.setRefreshTokenValiditySeconds(36000);
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        assertThat(accessToken, validFor(is(3600)));
        assertThat(accessToken.getRefreshToken(), is(not(nullValue())));

        assertThat(accessToken.getRefreshToken(), OAuth2RefreshTokenMatchers.validFor(is(36000)));
    }

    @Test(expected = TokenRevokedException.class)
    public void testUserUpdatedAfterRefreshTokenIssued() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        UaaUser user = userDatabase.retrieveUserByName(username, OriginKeys.UAA);
        UaaUser newUser = new UaaUser(new UaaUserPrototype().withId(userId).withUsername(user.getUsername())
                .withPassword("blah").withEmail(user.getEmail()).withAuthorities(user.getAuthorities()));
        userDatabase.updateUser(userId, newUser);

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testRefreshTokenExpiry() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));

        BaseClientDetails clientDetails = cloneClient(defaultClient);
        // Back date the refresh token. Crude way to do this but i'm not sure of
        // another
        clientDetails.setRefreshTokenValiditySeconds(-36000);
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, clientDetails));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testRefreshTokenAfterApprovalsRevoked() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));

        // Other scope is left unapproved

        for (Approval approval : approvalStore.getApprovals(userId, CLIENT_ID)) {
            approvalStore.revokeApproval(approval);
        }

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testRefreshTokenAfterApprovalsExpired() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, -3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testRefreshTokenAfterApprovalsDenied() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, -3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.DENIED));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testRefreshTokenAfterApprovalsMissing() {
        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, -3000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.DENIED));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test(expected = InvalidTokenException.class)
    public void testRefreshTokenAfterApprovalsMissing2() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest refreshAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        refreshAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(refreshAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        refreshAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(refreshAuthorizationRequest, "refresh_token"));
    }

    @Test
    public void refreshAccessTokenWithGrantTypeRestricted() {
        expectedEx.expect(InsufficientScopeException.class);
        expectedEx.expectMessage("Expected scope " + UAA_REFRESH_TOKEN + " is missing");

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                defaultUserAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest reducedScopeAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, readScope);
        reducedScopeAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(
                reducedScopeAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        reducedScopeAuthorizationRequest.setRequestParameters(refreshAzParameters);

        tokenServices.setRestrictRefreshGrant(true);
        tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(reducedScopeAuthorizationRequest, "refresh_token"));
    }

    @Test
    public void refreshAccessTokenWithGrantTypeRestricted_butRefreshScopePresent() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,
                Arrays.asList(UAA_REFRESH_TOKEN));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                defaultUserAuthentication);
        tokenServices.setRestrictRefreshGrant(true);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        AuthorizationRequest reducedScopeAuthorizationRequest = new AuthorizationRequest(CLIENT_ID, null);
        reducedScopeAuthorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> refreshAzParameters = new HashMap<>(
                reducedScopeAuthorizationRequest.getRequestParameters());
        refreshAzParameters.put(GRANT_TYPE, REFRESH_TOKEN);
        reducedScopeAuthorizationRequest.setRequestParameters(refreshAzParameters);

        expiresAt.add(Calendar.MILLISECOND, 300000);
        updatedAt.add(Calendar.MILLISECOND, -1000);
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(UAA_REFRESH_TOKEN).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        tokenServices.setRestrictRefreshGrant(true);
        OAuth2AccessToken refresh_token = tokenServices.refreshAccessToken(accessToken.getRefreshToken().getValue(),
                requestFactory.createTokenRequest(reducedScopeAuthorizationRequest, "refresh_token"));
        assertNotNull(refresh_token);
    }

    @Test
    public void testReadAccessToken() {
        readAccessToken(EMPTY_SET);
    }

    @Test
    public void testReadAccessToken_No_PII() {
        readAccessToken(new HashSet<>(Arrays.asList(ClaimConstants.EMAIL, ClaimConstants.USER_NAME)));
    }

    public void readAccessToken(Set<String> excludedClaims) {
        tokenServices.setExcludedClaims(excludedClaims);
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);
        Calendar updatedAt = Calendar.getInstance();
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        Approval approval = new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(OPENID)
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime());
        approvalStore.addApproval(approval);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        assertEquals(accessToken, tokenServices.readAccessToken(accessToken.getValue()));

        approvalStore.revokeApproval(approval);
        try {
            tokenServices.readAccessToken(accessToken.getValue());
            fail("Approval has been revoked");
        } catch (InvalidTokenException x) {
            assertThat("Exception should be about approvals",
                    x.getMessage().contains("some requested scopes are not approved"));
        }
    }

    @Test(expected = InvalidTokenException.class)
    public void testReadAccessTokenForDeletedUserId() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        Calendar expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.MILLISECOND, 3000);
        Calendar updatedAt = Calendar.getInstance();
        updatedAt.add(Calendar.MILLISECOND, -1000);

        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID).setScope(readScope.get(0))
                .setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));
        approvalStore.addApproval(new Approval().setUserId(userId).setClientId(CLIENT_ID)
                .setScope(writeScope.get(0)).setExpiresAt(expiresAt.getTime()).setStatus(ApprovalStatus.APPROVED)
                .setLastUpdatedAt(updatedAt.getTime()));

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);

        this.userDatabase.clear();
        assertEquals(accessToken, tokenServices.readAccessToken(accessToken.getValue()));
    }

    @Test
    public void testLoadAuthenticationForAUser() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        OAuth2Authentication loadedAuthentication = tokenServices.loadAuthentication(accessToken.getValue());

        assertEquals(USER_AUTHORITIES, loadedAuthentication.getAuthorities());
        assertEquals(username, loadedAuthentication.getName());
        UaaPrincipal uaaPrincipal = (UaaPrincipal) defaultUserAuthentication.getPrincipal();
        assertEquals(uaaPrincipal, loadedAuthentication.getPrincipal());
        assertNull(loadedAuthentication.getDetails());

        Authentication userAuth = loadedAuthentication.getUserAuthentication();
        assertEquals(username, userAuth.getName());
        assertEquals(uaaPrincipal, userAuth.getPrincipal());
        assertTrue(userAuth.isAuthenticated());
    }

    @Test
    public void opaque_tokens_validate_signature() throws Exception {
        defaultClient.setAutoApproveScopes(singleton("true"));
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResponseTypes(new HashSet(Arrays.asList(CompositeAccessToken.ID_TOKEN, "token")));
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        azParameters.put(REQUEST_TOKEN_FORMAT, TokenConstants.OPAQUE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        assertNotNull(accessToken);
        assertTrue("Token should be composite token", accessToken instanceof CompositeAccessToken);
        CompositeAccessToken composite = (CompositeAccessToken) accessToken;
        assertThat("id_token should be JWT, thus longer than 36 characters", composite.getIdTokenValue().length(),
                greaterThan(36));
        assertThat("Opaque access token must be shorter than 37 characters", accessToken.getValue().length(),
                lessThanOrEqualTo(36));
        assertThat("Opaque refresh token must be shorter than 37 characters",
                accessToken.getRefreshToken().getValue().length(), lessThanOrEqualTo(36));

        Map<String, String> keys = new HashMap<>();
        keys.put("otherKey", "unc0uf98gv89egh4v98749978hv");
        tokenPolicy.setKeys(keys);
        tokenPolicy.setActiveKeyId("otherKey");
        IdentityZoneHolder.get().getConfig().setTokenPolicy(tokenPolicy);

        expectedEx.expect(InvalidTokenException.class);
        expectedEx.expectMessage("Invalid key ID: testKey");
        tokenServices.validateToken(accessToken.getValue());
    }

    @Test
    public void testLoad_Opaque_AuthenticationForAUser() {
        defaultClient.setAutoApproveScopes(singleton("true"));
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResponseTypes(new HashSet(Arrays.asList(CompositeAccessToken.ID_TOKEN, "token")));
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        azParameters.put(REQUEST_TOKEN_FORMAT, TokenConstants.OPAQUE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        assertNotNull(accessToken);
        assertTrue("Token should be composite token", accessToken instanceof CompositeAccessToken);
        CompositeAccessToken composite = (CompositeAccessToken) accessToken;
        assertThat("id_token should be JWT, thus longer than 36 characters", composite.getIdTokenValue().length(),
                greaterThan(36));
        assertThat("Opaque access token must be shorter than 37 characters", accessToken.getValue().length(),
                lessThanOrEqualTo(36));
        assertThat("Opaque refresh token must be shorter than 37 characters",
                accessToken.getRefreshToken().getValue().length(), lessThanOrEqualTo(36));

        String accessTokenValue = tokenProvisioning.retrieve(composite.getValue()).getValue();
        Map<String, Object> accessTokenClaims = tokenServices.validateToken(accessTokenValue).getClaims();
        assertEquals(true, accessTokenClaims.get(ClaimConstants.REVOCABLE));

        String refreshTokenValue = tokenProvisioning.retrieve(composite.getRefreshToken().getValue()).getValue();
        Map<String, Object> refreshTokenClaims = tokenServices.validateToken(refreshTokenValue).getClaims();
        assertEquals(true, refreshTokenClaims.get(ClaimConstants.REVOCABLE));

        OAuth2Authentication loadedAuthentication = tokenServices.loadAuthentication(accessToken.getValue());

        assertEquals(USER_AUTHORITIES, loadedAuthentication.getAuthorities());
        assertEquals(username, loadedAuthentication.getName());
        UaaPrincipal uaaPrincipal = (UaaPrincipal) defaultUserAuthentication.getPrincipal();
        assertEquals(uaaPrincipal, loadedAuthentication.getPrincipal());
        assertNull(loadedAuthentication.getDetails());

        Authentication userAuth = loadedAuthentication.getUserAuthentication();
        assertEquals(username, userAuth.getName());
        assertEquals(uaaPrincipal, userAuth.getPrincipal());
        assertTrue(userAuth.isAuthenticated());

        Map<String, String> params = new HashedMap();
        params.put("grant_type", "refresh_token");
        params.put("client_id", CLIENT_ID);
        OAuth2AccessToken newAccessToken = tokenServices.refreshAccessToken(composite.getRefreshToken().getValue(),
                new TokenRequest(params, CLIENT_ID, Collections.EMPTY_SET, "refresh_token"));
        System.out.println("newAccessToken = " + newAccessToken);
    }

    @Test
    public void testLoadAuthenticationForAClient() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, CLIENT_CREDENTIALS);
        authorizationRequest.setRequestParameters(azParameters);

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                null);

        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        OAuth2Authentication loadedAuthentication = tokenServices.loadAuthentication(accessToken.getValue());

        assertThat("Client authorities match.", loadedAuthentication.getAuthorities(), containsInAnyOrder(
                AuthorityUtils.commaSeparatedStringToAuthorityList(CLIENT_AUTHORITIES).toArray()));
        assertEquals(CLIENT_ID, loadedAuthentication.getName());
        assertEquals(CLIENT_ID, loadedAuthentication.getPrincipal());
        assertNull(loadedAuthentication.getDetails());

        assertNull(loadedAuthentication.getUserAuthentication());
    }

    @Test(expected = InvalidTokenException.class)
    public void testLoadAuthenticationWithAnExpiredToken() throws InterruptedException {
        BaseClientDetails shortExpiryClient = defaultClient;
        shortExpiryClient.setAccessTokenValiditySeconds(1);
        clientDetailsService.setClientDetailsStore(Collections.singletonMap(CLIENT_ID, shortExpiryClient));

        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken accessToken = tokenServices.createAccessToken(authentication);
        assertThat(accessToken, validFor(is(1)));

        Thread.sleep(1000l);
        tokenServices.loadAuthentication(accessToken.getValue());
    }

    @Test
    public void testCreateAccessTokenAuthcodeGrantAdditionalAuthorizationAttributes() {
        AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, requestedAuthScopes);
        authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
        Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
        azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
        azParameters.put("authorities",
                "{\"az_attr\":{\"external_group\":\"domain\\\\group1\", \"external_id\":\"abcd1234\"}}");
        authorizationRequest.setRequestParameters(azParameters);
        Authentication userAuthentication = defaultUserAuthentication;

        OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(),
                userAuthentication);
        OAuth2AccessToken token = tokenServices.createAccessToken(authentication);

        this.assertCommonUserAccessTokenProperties(token);
        assertThat(token, issuerUri(is(ISSUER_URI)));
        assertThat(token, scope(is(requestedAuthScopes)));
        assertThat(token, validFor(is(60 * 60 * 12)));

        OAuth2RefreshToken refreshToken = token.getRefreshToken();
        this.assertCommonUserRefreshTokenProperties(refreshToken);
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.issuerUri(is(ISSUER_URI)));
        assertThat(refreshToken, OAuth2RefreshTokenMatchers.validFor(is(60 * 60 * 24 * 30)));

        this.assertCommonEventProperties(token, userId, buildJsonString(requestedAuthScopes));

        Map<String, String> azMap = new LinkedHashMap<>();
        azMap.put("external_group", "domain\\group1");
        azMap.put("external_id", "abcd1234");
        assertEquals(azMap, token.getAdditionalInformation().get("az_attr"));
    }

    private BaseClientDetails cloneClient(ClientDetails client) {
        return new BaseClientDetails(client);
    }

    @SuppressWarnings("unchecked")
    private void assertCommonClientAccessTokenProperties(OAuth2AccessToken accessToken) {
        assertThat(accessToken, allOf(clientId(is(CLIENT_ID)), userId(is(nullValue())), subject(is(CLIENT_ID)),
                username(is(nullValue())), cid(is(CLIENT_ID)), scope(is(clientScopes)), audience(is(resourceIds)),
                jwtId(not(isEmptyString())), issuedAt(is(greaterThan(0))), expiry(is(greaterThan(0)))));
    }

    @SuppressWarnings({ "unused", "unchecked" })
    private void assertCommonUserAccessTokenProperties(OAuth2AccessToken accessToken) {
        assertThat(accessToken, allOf(username(is(username)), clientId(is(CLIENT_ID)), subject(is(userId)),
                audience(is(resourceIds)), origin(is(OriginKeys.UAA)), revocationSignature(is(not(nullValue()))),
                cid(is(CLIENT_ID)), userId(is(userId)), email(is(email)), jwtId(not(isEmptyString())),
                issuedAt(is(greaterThan(0))), expiry(is(greaterThan(0)))));
    }

    @SuppressWarnings("unchecked")
    private void assertCommonUserRefreshTokenProperties(OAuth2RefreshToken refreshToken) {
        assertThat(refreshToken, allOf(/*issuer(is(issuerUri)),*/
                OAuth2RefreshTokenMatchers.username(is(username)),
                OAuth2RefreshTokenMatchers.clientId(is(CLIENT_ID)),
                OAuth2RefreshTokenMatchers.subject(is(not(nullValue()))),
                OAuth2RefreshTokenMatchers.audience(is(resourceIds)),
                OAuth2RefreshTokenMatchers.origin(is(OriginKeys.UAA)),
                OAuth2RefreshTokenMatchers.revocationSignature(is(not(nullValue()))),
                OAuth2RefreshTokenMatchers.jwtId(not(isEmptyString())),
                OAuth2RefreshTokenMatchers.issuedAt(is(greaterThan(0))),
                OAuth2RefreshTokenMatchers.expiry(is(greaterThan(0)))));
    }

    private void assertCommonEventProperties(OAuth2AccessToken accessToken, String expectedPrincipalId,
            String expectedData) {
        Assert.assertEquals(1, publisher.getEventCount());

        TokenIssuedEvent event = publisher.getLatestEvent();
        Assert.assertEquals(accessToken, event.getSource());
        Assert.assertEquals(mockAuthentication, event.getAuthentication());
        AuditEvent auditEvent = event.getAuditEvent();
        Assert.assertEquals(expectedPrincipalId, auditEvent.getPrincipalId());
        Assert.assertEquals(expectedData, auditEvent.getData());
        Assert.assertEquals(AuditEventType.TokenIssuedEvent, auditEvent.getType());
    }
}