Skip to content

Commit

Permalink
Refactor UAA token service to use new validation methods
Browse files Browse the repository at this point in the history
[#158846330] https://www.pivotaltracker.com/story/show/158846330

Signed-off-by: Bruce Ricard <bruce.ricard@gmail.com>
  • Loading branch information
DennisDenuto authored and cf-identity committed Jul 11, 2018
1 parent b37552d commit aba1fb5
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 16 deletions.
Expand Up @@ -1029,20 +1029,11 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen
throw new InvalidTokenException("Invalid access token value, must be at least 30 characters:"+accessToken);
}

TokenValidation tokenValidation = validateToken(accessToken);
Map<String, Object> claims = tokenValidation.getClaims();

Object jtiClaim = claims.get(JTI);
TokenValidation tokenValidation = validateToken(accessToken)
.checkAccessToken()
.throwIfInvalid();

if (jtiClaim == null) {
throw new InvalidTokenException("The token must contain a jti claim.");
} else {
if (jtiClaim.toString().endsWith(REFRESH_TOKEN_SUFFIX)) {
throw new InvalidTokenException(
"Invalid access token was provided."
);
}
}
Map<String, Object> claims = tokenValidation.getClaims();

accessToken = tokenValidation.getJwt().getEncoded();

Expand Down Expand Up @@ -1106,7 +1097,10 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen
*/
@Override
public OAuth2AccessToken readAccessToken(String accessToken) {
TokenValidation tokenValidation = validateToken(accessToken);
TokenValidation tokenValidation = validateToken(accessToken)
.checkAccessToken()
.throwIfInvalid();

Map<String, Object> claims = tokenValidation.getClaims();
accessToken = tokenValidation.getJwt().getEncoded();

Expand Down
Expand Up @@ -58,9 +58,11 @@
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.CLIENT_ID;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.EXP;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.ISS;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.JTI;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.REVOCATION_SIGNATURE;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.SCOPE;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.USER_ID;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REFRESH_TOKEN_SUFFIX;
import static org.cloudfoundry.identity.uaa.util.UaaTokenUtils.isUserToken;

public class TokenValidation {
Expand Down Expand Up @@ -492,4 +494,19 @@ public Jwt getJwt() {
public Map<String, Object> getClaims() {
return claims;
}

public TokenValidation checkAccessToken() {
Object jti = this.getClaims().get(JTI);
if (jti == null) {
addError("The token must contain a jti claim.");
return this;
}

if (jti.toString().endsWith(REFRESH_TOKEN_SUFFIX)) {
addError("Invalid access token.");
}

return this;
}

}
Expand Up @@ -1667,6 +1667,54 @@ public void testReadAccessToken_No_PII() {
readAccessToken(new HashSet<>(Arrays.asList(ClaimConstants.EMAIL, ClaimConstants.USER_NAME)));
}

@Test
public void testReadAccessToken_When_Given_Refresh_token_should_throw_exception() {
tokenServices.setExcludedClaims(new HashSet<>(Arrays.asList(ClaimConstants.EMAIL, ClaimConstants.USER_NAME)));
AuthorizationRequest authorizationRequest =new AuthorizationRequest(CLIENT_ID, tokenSupport.requestedAuthScopes);
authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.resourceIds));
Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters());
azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE);
authorizationRequest.setRequestParameters(azParameters);
Authentication userAuthentication = tokenSupport.defaultUserAuthentication;

Calendar expiresAt1 = Calendar.getInstance();
expiresAt1.add(Calendar.MILLISECOND, 3000);
Calendar updatedAt1 = Calendar.getInstance();
updatedAt1.add(Calendar.MILLISECOND, -1000);

tokenSupport.approvalStore.addApproval(new Approval()
.setUserId(tokenSupport.userId)
.setClientId(CLIENT_ID)
.setScope(tokenSupport.readScope.get(0))
.setExpiresAt(expiresAt1.getTime())
.setStatus(ApprovalStatus.APPROVED)
.setLastUpdatedAt(updatedAt1.getTime()), IdentityZoneHolder.get().getId());
tokenSupport.approvalStore.addApproval(new Approval()
.setUserId(tokenSupport.userId)
.setClientId(CLIENT_ID)
.setScope(tokenSupport.writeScope.get(0))
.setExpiresAt(expiresAt1.getTime())
.setStatus(ApprovalStatus.APPROVED)
.setLastUpdatedAt(updatedAt1.getTime()), IdentityZoneHolder.get().getId());
Approval approval = new Approval()
.setUserId(tokenSupport.userId)
.setClientId(CLIENT_ID)
.setScope(OPENID)
.setExpiresAt(expiresAt1.getTime())
.setStatus(ApprovalStatus.APPROVED)
.setLastUpdatedAt(updatedAt1.getTime());
tokenSupport.approvalStore.addApproval(
approval, IdentityZoneHolder.get().getId());

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


expectedException.expectMessage("Invalid access token.");
tokenServices.readAccessToken(accessToken.getRefreshToken().getValue());
}


public void readAccessToken(Set<String> excludedClaims) {
tokenServices.setExcludedClaims(excludedClaims);
AuthorizationRequest authorizationRequest =new AuthorizationRequest(CLIENT_ID, tokenSupport.requestedAuthScopes);
Expand Down Expand Up @@ -1907,7 +1955,7 @@ public void loadAuthentication_when_given_an_opaque_refreshToken_should_throw_ex
String refreshTokenValue = tokenProvisioning.retrieve(compositeToken.getRefreshToken().getValue(), IdentityZoneHolder.get().getId()).getValue();

expectedException.expect(InvalidTokenException.class);
expectedException.expectMessage("Invalid access token was provided.");
expectedException.expectMessage("Invalid access token.");

tokenServices.loadAuthentication(refreshTokenValue);
}
Expand Down Expand Up @@ -1937,7 +1985,7 @@ public void loadAuthentication_when_given_an_refresh_jwt_should_throw_exception(
String refreshTokenValue = tokenProvisioning.retrieve(refreshToken.getClaims().get("jti").toString(), IdentityZoneHolder.get().getId()).getValue();

expectedException.expect(InvalidTokenException.class);
expectedException.expectMessage("Invalid access token was provided.");
expectedException.expectMessage("Invalid access token.");
tokenServices.loadAuthentication(refreshTokenValue);
}

Expand Down
Expand Up @@ -20,6 +20,7 @@
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.user.UaaUserDatabase;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -46,13 +47,19 @@
import static java.util.Collections.EMPTY_LIST;
import static org.cloudfoundry.identity.uaa.oauth.client.ClientConstants.REQUIRED_USER_GROUPS;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.EMAIL;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.JTI;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.USER_NAME;
import static org.cloudfoundry.identity.uaa.util.TokenValidation.validate;
import static org.cloudfoundry.identity.uaa.util.UaaMapUtils.entry;
import static org.cloudfoundry.identity.uaa.util.UaaMapUtils.map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
Expand Down Expand Up @@ -210,12 +217,53 @@ public void validateToken() throws Exception {
.checkRevocationSignature(Collections.singletonList("fa1c787d"))
.checkAudience("acme", "app")
.checkRevocableTokenStore(revocableTokenProvisioning)
.checkAccessToken()
;

assertThat(validation.getValidationErrors(), empty());
assertTrue(validation.isValid());
}

@Test
public void validateAccessToken() throws Exception {
content.put(JTI, "8b14f193-8212-4af2-9927-e3ae903f94a6-r");

TokenValidation validation = validate(getToken())
.checkAccessToken();

assertThat(validation.getValidationErrors(), not(empty()));
assertThat(validation.getValidationErrors(), hasSize(1));
assertThat(validation.getValidationErrors().get(0), instanceOf(InvalidTokenException.class));
assertThat(validation.getValidationErrors().get(0).getMessage(), is("Invalid access token."));

assertThat(validation.isValid(), is(false));
}

@Test
public void validateAccessToken_with_dashR_in_JTI_should_fail_validation() throws Exception {
content.put(JTI, "8b14f193-r-8212-4af2-9927-e3ae903f94a6");

TokenValidation validation = validate(getToken())
.checkAccessToken();

assertThat(validation.getValidationErrors(), empty());
assertThat(validation.isValid(), is(true));
}

@Test
public void validateAccessToken_without_jti_should_fail_validation() throws Exception {
content.put(JTI, null);

TokenValidation validation = validate(getToken())
.checkAccessToken();

assertThat(validation.getValidationErrors(), hasSize(1));
assertThat(validation.getValidationErrors().get(0), instanceOf(InvalidTokenException.class));
assertThat(validation.getValidationErrors().get(0).getMessage(), is("The token must contain a jti claim."));

assertThat(validation.isValid(), is(false));
}

@Test
public void validateToken_Without_Email_And_Username() throws Exception {
TokenValidation validation = validate(getToken(Arrays.asList(EMAIL, USER_NAME)))
Expand Down

0 comments on commit aba1fb5

Please sign in to comment.