Skip to content

Commit

Permalink
Move token-store retrieval out of TokenValidation
Browse files Browse the repository at this point in the history
[#117580865] https://www.pivotaltracker.com/story/show/117580865

Signed-off-by: Jeremy Coffield <jcoffield@pivotal.io>
  • Loading branch information
jlo authored and cf-identity committed Apr 15, 2016
1 parent bc5d4a0 commit 4b6dba4
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 248 deletions.
Expand Up @@ -43,6 +43,7 @@
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
Expand Down Expand Up @@ -88,6 +89,7 @@
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern;


import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.ADDITIONAL_AZ_ATTR; import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.ADDITIONAL_AZ_ATTR;
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.AUD; import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.AUD;
Expand Down Expand Up @@ -1019,24 +1021,44 @@ private Set<String> getAutoApprovedScopes(Object grantType, Collection<String> t
} }


protected TokenValidation validateToken(String token) { protected TokenValidation validateToken(String token) {
TokenValidation tokenValidation = validate(tokenProvisioning, token).throwIfInvalid(); TokenValidation tokenValidation;
Jwt tokenJwt = tokenValidation.getJwt(); Pattern jwtPattern = Pattern.compile("[a-zA-Z0-9_\\-\\\\=]*\\.[a-zA-Z0-9_\\-\\\\=]*\\.[a-zA-Z0-9_\\-\\\\=]*");
Map<String, Object> claims = tokenValidation.getClaims(); if(jwtPattern.matcher(token).matches()) {
tokenValidation = validate(token)
.checkRevocableTokenStore(tokenProvisioning)
.throwIfInvalid();
Jwt tokenJwt = tokenValidation.getJwt();

String keyId = tokenJwt.getHeader().getKid();
KeyInfo key;
if(keyId!=null) {
key = KeyInfo.getKey(keyId);
} else {
key = KeyInfo.getActiveKey();
}


String keyId = tokenJwt.getHeader().getKid(); if(key == null) {
KeyInfo key; throw new InvalidTokenException("Invalid key ID: " + keyId);
if(keyId!=null) { }
key = KeyInfo.getKey(keyId); SignatureVerifier verifier = key.getVerifier();
tokenValidation
.checkSignature(verifier)
.throwIfInvalid()
;
} else { } else {
key = KeyInfo.getActiveKey(); RevocableToken revocableToken;
try {
revocableToken = tokenProvisioning.retrieve(token);
} catch(EmptyResultDataAccessException ex) {
throw new TokenRevokedException("The token expired, was revoked, or the token ID is incorrect: " + token);
}
token = revocableToken.getValue();
tokenValidation = validate(token).throwIfInvalid();
} }


if(key == null) { Map<String, Object> claims = tokenValidation.getClaims();
throw new InvalidTokenException("Invalid key ID: " + keyId);
}
SignatureVerifier verifier = key.getVerifier();
tokenValidation tokenValidation
.checkSignature(verifier)
.checkIssuer(getTokenEndpoint()) .checkIssuer(getTokenEndpoint())
.throwIfInvalid() .throwIfInvalid()
; ;
Expand Down
Expand Up @@ -67,23 +67,12 @@ public class TokenValidation {
private final String token; private final String token;
private final boolean decoded; // this is used to avoid checking claims on tokens that had errors when decoding private final boolean decoded; // this is used to avoid checking claims on tokens that had errors when decoding
private final List<RuntimeException> validationErrors = new ArrayList<>(); private final List<RuntimeException> validationErrors = new ArrayList<>();
private final boolean serverSide;


public static TokenValidation validate(String token) { public static TokenValidation validate(String tokenJwtValue) {
return new TokenValidation(token); return new TokenValidation(tokenJwtValue);
}

public static TokenValidation validate(RevocableTokenProvisioning tokenProvisioning, String token) {
Pattern jwtPattern = Pattern.compile("[a-zA-Z0-9_\\-\\\\=]*\\.[a-zA-Z0-9_\\-\\\\=]*\\.[a-zA-Z0-9_\\-\\\\=]*");
if(jwtPattern.matcher(token).matches()) {
return new TokenValidation(token);
} else {
return new TokenValidation(tokenProvisioning, token);
}
} }


private TokenValidation(String token) { private TokenValidation(String token) {
this.serverSide = false;
this.token = token; this.token = token;


Jwt tokenJwt; Jwt tokenJwt;
Expand Down Expand Up @@ -113,29 +102,6 @@ private TokenValidation(String token) {
this.decoded = isValid(); this.decoded = isValid();
} }


private TokenValidation(RevocableTokenProvisioning tokenProvisioning, String tokenId) {
this.serverSide = true;

String token;
try {
token = tokenProvisioning.retrieve(tokenId).getValue();
} catch (EmptyResultDataAccessException x) {
token = null;
validationErrors.add(new TokenRevokedException("The token was revoked or the token ID is incorrect: " + tokenId));
}
this.token = token;

if(token != null) {
tokenJwt = JwtHelper.decode(token);
claims = JsonUtils.readValue(tokenJwt.getClaims(), new TypeReference<Map<String, Object>>() {});
this.decoded = true;
} else {
tokenJwt = null;
claims = null;
this.decoded = false;
}
}

public boolean isValid() { public boolean isValid() {
return validationErrors.size() == 0; return validationErrors.size() == 0;
} }
Expand All @@ -156,17 +122,11 @@ private TokenValidation(TokenValidation source) {
this.tokenJwt = source.tokenJwt; this.tokenJwt = source.tokenJwt;
this.token = source.token; this.token = source.token;
this.decoded = source.decoded; this.decoded = source.decoded;
this.serverSide = source.serverSide;
this.scopes = source.scopes; this.scopes = source.scopes;
} }




public TokenValidation checkSignature(SignatureVerifier verifier) { public TokenValidation checkSignature(SignatureVerifier verifier) {
if(serverSide) {
// serverSide tokens are not JWT and we should not validate the JWT signature
return this;
}

if(!decoded) { return this; } if(!decoded) { return this; }
try { try {
tokenJwt.verifySignature(verifier); tokenJwt.verifySignature(verifier);
Expand Down Expand Up @@ -419,11 +379,6 @@ else if(audClaim == null) {
} }


public TokenValidation checkRevocableTokenStore(RevocableTokenProvisioning revocableTokenProvisioning) { public TokenValidation checkRevocableTokenStore(RevocableTokenProvisioning revocableTokenProvisioning) {
if(serverSide) {
// serverSide tokens are inherently present in the token store
return this;
}

if(!decoded) { if(!decoded) {
addError("The token could not be checked for revocation."); addError("The token could not be checked for revocation.");
return this; return this;
Expand Down

0 comments on commit 4b6dba4

Please sign in to comment.