Skip to content

Commit

Permalink
Add ensure id token with opaque refresh token
Browse files Browse the repository at this point in the history
  • Loading branch information
jhamon authored and Pivotal committed Jul 30, 2018
1 parent a1febf3 commit c98530c
Showing 1 changed file with 71 additions and 34 deletions.
Expand Up @@ -34,6 +34,7 @@
import org.cloudfoundry.identity.uaa.util.TimeServiceImpl; import org.cloudfoundry.identity.uaa.util.TimeServiceImpl;
import org.cloudfoundry.identity.uaa.util.UaaTokenUtils; import org.cloudfoundry.identity.uaa.util.UaaTokenUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityZone; 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.IdentityZoneHolder;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
Expand Down Expand Up @@ -129,10 +130,10 @@ private void createClientAndUserInRandomZone() throws Exception {
zone.getConfig().getTokenPolicy().setActiveKeyId("key1"); zone.getConfig().getTokenPolicy().setActiveKeyId("key1");
zone = identityZoneProvisioning.update(zone); zone = identityZoneProvisioning.update(zone);


String clientId = "refresh-"+new RandomValueStringGenerator().generate(); String clientId = "refresh-" + new RandomValueStringGenerator().generate();
client = setUpClients(clientId, "uaa.resource", "uaa.user,openid", "client_credentials,password,refresh_token", true, TEST_REDIRECT_URI, Arrays.asList(OriginKeys.UAA), 30*60, zone); client = setUpClients(clientId, "uaa.resource", "uaa.user,openid", "client_credentials,password,refresh_token", true, TEST_REDIRECT_URI, Arrays.asList(OriginKeys.UAA), 30 * 60, zone);


String username = "testuser"+new RandomValueStringGenerator().generate(); String username = "testuser" + new RandomValueStringGenerator().generate();
user = setUpUser(username, "", OriginKeys.UAA, zone.getId()); user = setUpUser(username, "", OriginKeys.UAA, zone.getId());


refreshToken = getRefreshToken(client.getClientId(), SECRET, user.getUserName(), SECRET, zone.getSubdomain() + ".localhost"); refreshToken = getRefreshToken(client.getClientId(), SECRET, user.getUserName(), SECRET, zone.getSubdomain() + ".localhost");
Expand Down Expand Up @@ -205,7 +206,8 @@ public void test_opaque_refresh_tokens_sets_revocable_claim() throws Exception {
IdentityZoneHolder.set(zone); IdentityZoneHolder.set(zone);
String token = revocableTokenProvisioning.retrieve(tokenId, IdentityZoneHolder.get().getId()).getValue(); String token = revocableTokenProvisioning.retrieve(tokenId, IdentityZoneHolder.get().getId()).getValue();
Jwt jwt = JwtHelper.decode(token); Jwt jwt = JwtHelper.decode(token);
Map<String, Object> claims = JsonUtils.readValue(jwt.getClaims(), new TypeReference<Map<String, Object>>() {}); Map<String, Object> claims = JsonUtils.readValue(jwt.getClaims(), new TypeReference<Map<String, Object>>() {
});
assertNotNull(claims.get(ClaimConstants.REVOCABLE)); assertNotNull(claims.get(ClaimConstants.REVOCABLE));
assertTrue((Boolean) claims.get(ClaimConstants.REVOCABLE)); assertTrue((Boolean) claims.get(ClaimConstants.REVOCABLE));
} }
Expand All @@ -223,26 +225,10 @@ public void test_opaque_refresh_unique_tokens_count() throws Exception {
assertEquals(1, countTokens(client.getClientId(), user.getId())); assertEquals(1, countTokens(client.getClientId(), user.getId()));
} }


@Test private void assertRefreshIdTokenCorrect(String originalIdTokenJwt, String idTokenJwtFromRefreshGrant) {
public void refreshTokenGrantType_returnsIdToken_toOpenIdClients() throws Exception { assertNotNull(idTokenJwtFromRefreshGrant);
when(timeService.getCurrentTimeMillis()).thenReturn(1000L); Map<String, Object> originalIdClaims = UaaTokenUtils.getClaims(originalIdTokenJwt);
BaseClientDetails openIdClient = setUpClients("openidclient", "", "openid", "password,refresh_token", true); Map<String, Object> idClaims = UaaTokenUtils.getClaims(idTokenJwtFromRefreshGrant);
ScimUser user = setUpUser("openiduser", "", OriginKeys.UAA, "uaa");
Map<String, Object> tokenResponse = getTokens(openIdClient.getClientId(), SECRET, user.getUserName(), SECRET, "localhost");
String refreshToken = (String) tokenResponse.get(REFRESH_TOKEN);
String originalIdToken = (String) tokenResponse.get(ID_TOKEN);

when(timeService.getCurrentTimeMillis()).thenReturn(5000L);
MockHttpServletResponse refreshResponse = useRefreshToken(refreshToken, openIdClient.getClientId(), SECRET, "localhost");

assertEquals(HttpStatus.SC_OK, refreshResponse.getStatus());
CompositeAccessToken compositeAccessToken = JsonUtils.readValue(refreshResponse.getContentAsString(), CompositeAccessToken.class);
String idTokenJwt = compositeAccessToken.getIdTokenValue();

assertNotNull(idTokenJwt);
Map<String, Object> refreshClaims = UaaTokenUtils.getClaims(refreshToken);
Map<String, Object> originalIdClaims = UaaTokenUtils.getClaims(originalIdToken);
Map<String, Object> idClaims = UaaTokenUtils.getClaims(idTokenJwt);


// These claims should be the same in the old and new id tokens: auth_time, iss, sub, azp // These claims should be the same in the old and new id tokens: auth_time, iss, sub, azp
// http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse // http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse
Expand All @@ -252,7 +238,6 @@ public void refreshTokenGrantType_returnsIdToken_toOpenIdClients() throws Except
assertThat(idClaims.get("iss"), not(nullValue())); assertThat(idClaims.get("iss"), not(nullValue()));
assertEquals(originalIdClaims.get("iss"), idClaims.get("iss")); assertEquals(originalIdClaims.get("iss"), idClaims.get("iss"));


assertThat(refreshClaims.get("sub"), not(nullValue()));
assertThat(originalIdClaims.get("sub"), not(nullValue())); assertThat(originalIdClaims.get("sub"), not(nullValue()));
assertEquals(originalIdClaims.get("sub"), idClaims.get("sub")); assertEquals(originalIdClaims.get("sub"), idClaims.get("sub"));


Expand All @@ -261,7 +246,6 @@ public void refreshTokenGrantType_returnsIdToken_toOpenIdClients() throws Except


// These claims should be different in the old and new id token: iat // These claims should be different in the old and new id token: iat
// http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse // http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse
assertThat(refreshClaims.get("iat"), not(nullValue()));
assertThat(originalIdClaims.get("iat"), not(nullValue())); assertThat(originalIdClaims.get("iat"), not(nullValue()));
assertThat(idClaims.get("iat"), not(nullValue())); assertThat(idClaims.get("iat"), not(nullValue()));
assertNotEquals(originalIdClaims.get("iat"), idClaims.get("iat")); assertNotEquals(originalIdClaims.get("iat"), idClaims.get("iat"));
Expand All @@ -284,8 +268,58 @@ public void refreshTokenGrantType_returnsIdToken_toOpenIdClients() throws Except
assertNotEquals(originalIdClaims.get("jti"), idClaims.get("jti")); assertNotEquals(originalIdClaims.get("jti"), idClaims.get("jti"));
} }


// TODO: should test with opaque refresh token @Test
// TODO: iss should not change in id_token even if admin updates issuer config public void refreshTokenGrantType_returnsIdToken_toOpenIdClients() throws Exception {
when(timeService.getCurrentTimeMillis()).thenReturn(1000L);
BaseClientDetails openIdClient = setUpClients("openidclient", "", "openid", "password,refresh_token", true);
ScimUser user = setUpUser("openiduser", "", OriginKeys.UAA, "uaa");
Map<String, Object> tokenResponse = getTokens(openIdClient.getClientId(), SECRET, user.getUserName(), SECRET, "localhost", "jwt");
String refreshToken = (String) tokenResponse.get(REFRESH_TOKEN);
String originalIdTokenJwt = (String) tokenResponse.get(ID_TOKEN);
when(timeService.getCurrentTimeMillis()).thenReturn(5000L);

MockHttpServletResponse refreshResponse = useRefreshToken(refreshToken, openIdClient.getClientId(), SECRET, "localhost");

assertEquals(HttpStatus.SC_OK, refreshResponse.getStatus());
CompositeAccessToken compositeAccessToken = JsonUtils.readValue(refreshResponse.getContentAsString(), CompositeAccessToken.class);
String idTokenJwt = compositeAccessToken.getIdTokenValue();
assertRefreshIdTokenCorrect(originalIdTokenJwt, idTokenJwt);
}

@Test
public void refreshTokenGrantType_returnsIdToken_toOpenIdClients_withOpaqueRefreshToken() throws Exception {
when(timeService.getCurrentTimeMillis()).thenReturn(1000L);
BaseClientDetails openIdClient = setUpClients("openidclient", "", "openid", "password,refresh_token", true);
ScimUser user = setUpUser("openiduser", "", OriginKeys.UAA, "uaa");
Map<String, Object> tokenResponse = getTokens(openIdClient.getClientId(), SECRET, user.getUserName(), SECRET, "localhost", "opaque");
String refreshToken = (String) tokenResponse.get(REFRESH_TOKEN);
String originalIdTokenJwt = (String) tokenResponse.get(ID_TOKEN);
when(timeService.getCurrentTimeMillis()).thenReturn(5000L);

MockHttpServletResponse refreshResponse = useRefreshToken(refreshToken, openIdClient.getClientId(), SECRET, "localhost");

assertEquals(HttpStatus.SC_OK, refreshResponse.getStatus());
CompositeAccessToken compositeAccessToken = JsonUtils.readValue(refreshResponse.getContentAsString(), CompositeAccessToken.class);
String idTokenJwt = compositeAccessToken.getIdTokenValue();
assertRefreshIdTokenCorrect(originalIdTokenJwt, idTokenJwt);
}

@Test
public void refreshTokenGrantType_rejectsRefreshTokensIfIssuerHasChanged() throws Exception {
createClientAndUserInRandomZone();
zone.getConfig().setIssuer("http://fancyissuer.com");
identityZoneProvisioning.update(zone);
when(timeService.getCurrentTimeMillis()).thenReturn(1000L);
Map<String, Object> tokenResponse = getTokens(client.getClientId(), SECRET, user.getUserName(), SECRET, zone.getSubdomain() + ".localhost", "jwt");
String refreshToken = (String) tokenResponse.get(REFRESH_TOKEN);
when(timeService.getCurrentTimeMillis()).thenReturn(5000L);
zone.getConfig().setIssuer("http://a.new.issuer.url.com");
identityZoneProvisioning.update(zone);

MockHttpServletResponse refreshResponse = useRefreshToken(refreshToken, client.getClientId(), SECRET, zone.getSubdomain() + ".localhost");

assertEquals(HttpStatus.SC_UNAUTHORIZED, refreshResponse.getStatus());
}


@Test @Test
public void refreshTokenGrantType_doesNotReturnIdToken_toNonOpenIdClients() throws Exception { public void refreshTokenGrantType_doesNotReturnIdToken_toNonOpenIdClients() throws Exception {
Expand All @@ -301,7 +335,7 @@ public void refreshTokenGrantType_doesNotReturnIdToken_toNonOpenIdClients() thro
} }


protected int countTokens(String clientId, String userId) { protected int countTokens(String clientId, String userId) {
return template.queryForObject("select count(*) from revocable_tokens where client_id=? and user_id=?", new String[] {clientId, userId}, Integer.class); return template.queryForObject("select count(*) from revocable_tokens where client_id=? and user_id=?", new String[]{clientId, userId}, Integer.class);
} }


protected MockHttpServletResponse useRefreshToken(String refreshToken, String clientId, String clientSecret, String host) throws Exception { protected MockHttpServletResponse useRefreshToken(String refreshToken, String clientId, String clientSecret, String host) throws Exception {
Expand All @@ -319,17 +353,18 @@ protected MockHttpServletResponse useRefreshToken(String refreshToken, String cl
} }


private void validateAccessTokenExists(String refreshResponse) { private void validateAccessTokenExists(String refreshResponse) {
Map<String,Object> result = JsonUtils.readValue(refreshResponse, new TypeReference<Map<String, Object>>() {}); Map<String, Object> result = JsonUtils.readValue(refreshResponse, new TypeReference<Map<String, Object>>() {
});
assertNotNull(result.get(ACCESS_TOKEN)); assertNotNull(result.get(ACCESS_TOKEN));
} }


protected String getRefreshToken(String clientId, String clientSecret, String userName, String password, String host) throws Exception { protected String getRefreshToken(String clientId, String clientSecret, String userName, String password, String host) throws Exception {
Map<String, Object> result = getTokens(clientId, clientSecret, userName, password, host); Map<String, Object> result = getTokens(clientId, clientSecret, userName, password, host, "jwt");
assertNotNull(result.get(REFRESH_TOKEN)); assertNotNull(result.get(REFRESH_TOKEN));
return (String)result.get(REFRESH_TOKEN); return (String) result.get(REFRESH_TOKEN);
} }


private Map<String, Object> getTokens(String clientId, String clientSecret, String userName, String password, String host) throws Exception { private Map<String, Object> getTokens(String clientId, String clientSecret, String userName, String password, String host, String tokenFormat) throws Exception {
String response = getMockMvc().perform( String response = getMockMvc().perform(
post("/oauth/token") post("/oauth/token")
.header("Host", host) .header("Host", host)
Expand All @@ -340,11 +375,13 @@ private Map<String, Object> getTokens(String clientId, String clientSecret, Stri
.param("username", userName) .param("username", userName)
.param("password", password) .param("password", password)
.param("client_secret", clientSecret) .param("client_secret", clientSecret)
.param("token_format", tokenFormat)
.param(OAuth2Utils.CLIENT_ID, clientId) .param(OAuth2Utils.CLIENT_ID, clientId)
) )
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(); .andReturn().getResponse().getContentAsString();


return JsonUtils.readValue(response, new TypeReference<Map<String, Object>>() {}); return JsonUtils.readValue(response, new TypeReference<Map<String, Object>>() {
});
} }
} }

0 comments on commit c98530c

Please sign in to comment.