CVE-2018-11047
Description
Cloud Foundry UAA, versions 4.19 prior to 4.19.2 and 4.12 prior to 4.12.4 and 4.10 prior to 4.10.2 and 4.7 prior to 4.7.6 and 4.5 prior to 4.5.7, incorrectly authorizes requests to admin endpoints by accepting a valid refresh token in lieu of an access token. Refresh tokens by design have a longer expiration time than access tokens, allowing the possessor of a refresh token to authenticate longer than expected. This affects the administrative endpoints of the UAA. i.e. /Users, /Groups, etc. However, if the user has been deleted or had groups removed, or the client was deleted, the refresh token will no longer be valid.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Cloud Foundry UAA incorrectly accepts refresh tokens for admin endpoints, extending authentication beyond intended expiration.
Vulnerability
Cloud Foundry UAA versions 4.19 prior to 4.19.2, 4.12 prior to 4.12.4, 4.10 prior to 4.10.2, 4.7 prior to 4.7.6, and 4.5 prior to 4.5.7 incorrectly authorize requests to administrative endpoints (such as /Users, /Groups, etc.) by accepting a valid refresh token in place of an access token. The bug lies in the token validation logic, which does not distinguish between token types when authorizing requests to these admin endpoints [1][2].
Exploitation
An attacker who possesses a valid refresh token (e.g., obtained through OAuth2 flows or by compromising a client that holds such tokens) can use that token to authenticate to administrative endpoints. Because refresh tokens have inherently longer expiration times than access tokens, this allows the attacker to maintain authenticated access to admin endpoints for an extended period. The attacker does not need any additional authentication step beyond presenting the refresh token to the vulnerable endpoint [1][2]. However, if the user has been deleted, groups removed, or the client deleted, the refresh token becomes invalid and exploitation is no longer possible [2].
Impact
Successful exploitation enables an attacker to perform administrative operations on the UAA, such as managing user accounts, groups, or OAuth2 clients, with the privileges associated with the token's scope. This can lead to unauthorized access to sensitive data (information disclosure), modification of user or group configurations, or potential privilege escalation, depending on the attacker's granted scopes [1][2].
Mitigation
The vulnerability is fixed in UAA versions 4.19.2, 4.12.4, 4.10.2, 4.7.6, and 4.5.7 [1][2]. The fix includes a patch that adds validation to reject refresh tokens when an access token is required for admin endpoints, as seen in the commit aba1fb5 [3] and bbbba5a [4]. Organizations should upgrade to the fixed versions immediately. No workaround is described for unpatched versions; if an upgrade is not possible, restricting network access to admin endpoints and monitoring for unauthorized use of refresh tokens may reduce risk.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | < 4.5.7 | 4.5.7 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.6.0, < 4.7.6 | 4.7.6 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.8.0, < 4.10.2 | 4.10.2 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.11.0, < 4.12.4 | 4.12.4 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 4.13.0, < 4.19.2 | 4.19.2 |
Affected products
2- Cloud Foundry/Cloud Foundry UAAv5Range: 4.19
Patches
1081aeb7a3aa04Refactor UAA token service to use new validation methods
4 files changed · +123 −16
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+8 −14 modified@@ -1027,20 +1027,11 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen throw new InvalidTokenException("Invalid access token value, must be at least 30 characters"); } - 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(); @@ -1103,7 +1094,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();
server/src/main/java/org/cloudfoundry/identity/uaa/util/TokenValidation.java+17 −0 modified@@ -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 { @@ -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; + } + }
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+50 −2 modified@@ -1680,6 +1680,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); @@ -1920,7 +1968,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); } @@ -1950,7 +1998,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); }
server/src/test/java/org/cloudfoundry/identity/uaa/util/TokenValidationTest.java+48 −0 modified@@ -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; @@ -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; @@ -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)))
a1d523c7f150Refactor UAA token service to use new validation methods
4 files changed · +123 −16
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+8 −14 modified@@ -1033,20 +1033,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(); @@ -1110,7 +1101,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();
server/src/main/java/org/cloudfoundry/identity/uaa/util/TokenValidation.java+17 −0 modified@@ -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 { @@ -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; + } + }
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+50 −2 modified@@ -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); @@ -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); } @@ -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); }
server/src/test/java/org/cloudfoundry/identity/uaa/util/TokenValidationTest.java+48 −0 modified@@ -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; @@ -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; @@ -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)))
aba1fb5f18e0Refactor UAA token service to use new validation methods
4 files changed · +123 −16
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+8 −14 modified@@ -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(); @@ -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();
server/src/main/java/org/cloudfoundry/identity/uaa/util/TokenValidation.java+17 −0 modified@@ -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 { @@ -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; + } + }
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+50 −2 modified@@ -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); @@ -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); } @@ -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); }
server/src/test/java/org/cloudfoundry/identity/uaa/util/TokenValidationTest.java+48 −0 modified@@ -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; @@ -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; @@ -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)))
2906057dae99Refactor UAA token service to use new validation methods
4 files changed · +123 −16
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+8 −14 modified@@ -938,20 +938,11 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen throw new InvalidTokenException("Invalid access token value, must be at least 30 characters"); } - 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(); @@ -1014,7 +1005,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();
server/src/main/java/org/cloudfoundry/identity/uaa/util/TokenValidation.java+17 −0 modified@@ -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 { @@ -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; + } + }
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+50 −2 modified@@ -1756,6 +1756,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); @@ -1996,7 +2044,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); } @@ -2026,7 +2074,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); }
server/src/test/java/org/cloudfoundry/identity/uaa/util/TokenValidationTest.java+48 −0 modified@@ -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; @@ -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; @@ -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)))
bbbba5aec514Refactor UAA token service to use new validation methods
4 files changed · +123 −16
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+8 −14 modified@@ -938,20 +938,11 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen throw new InvalidTokenException("Invalid access token value, must be at least 30 characters"); } - 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(); @@ -1014,7 +1005,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();
server/src/main/java/org/cloudfoundry/identity/uaa/util/TokenValidation.java+17 −0 modified@@ -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 { @@ -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; + } + }
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+50 −2 modified@@ -1756,6 +1756,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); @@ -1996,7 +2044,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); } @@ -2026,7 +2074,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); }
server/src/test/java/org/cloudfoundry/identity/uaa/util/TokenValidationTest.java+48 −0 modified@@ -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; @@ -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; @@ -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)))
0cd3c6fdd962Add additional jti claim logic when loading auth object
2 files changed · +67 −0
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+13 −0 modified@@ -940,6 +940,19 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen TokenValidation tokenValidation = validateToken(accessToken); Map<String, Object> claims = tokenValidation.getClaims(); + + Object jtiClaim = claims.get(JTI); + + 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." + ); + } + } + accessToken = tokenValidation.getJwt().getEncoded(); // Check token expiry
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+54 −0 modified@@ -33,6 +33,7 @@ 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.util.TokenValidation; import org.cloudfoundry.identity.uaa.zone.ClientServicesExtension; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; @@ -1975,6 +1976,59 @@ public void testLoad_Opaque_AuthenticationForAUser() { System.out.println("newAccessToken = " + newAccessToken); } + @Test + public void loadAuthentication_when_given_an_opaque_refreshToken_should_throw_exception() { + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.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 = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + + String refreshTokenValue = tokenProvisioning.retrieve(compositeToken.getRefreshToken().getValue(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + + tokenServices.loadAuthentication(refreshTokenValue); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void loadAuthentication_when_given_an_refresh_jwt_should_throw_exception() { + IdentityZoneHolder.get().getConfig().getTokenPolicy().setJwtRevocable(true); + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.resourceIds)); + Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters()); + azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE); + + azParameters.put(REQUEST_TOKEN_FORMAT, JWT.getStringValue()); + + authorizationRequest.setRequestParameters(azParameters); + Authentication userAuthentication = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + TokenValidation refreshToken = tokenServices.validateToken(compositeToken.getRefreshToken().getValue()); + + String refreshTokenValue = tokenProvisioning.retrieve(refreshToken.getClaims().get("jti").toString(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + tokenServices.loadAuthentication(refreshTokenValue); + } @Test public void testLoadAuthenticationForAClient() {
b37552d2bf08Add additional jti claim logic when loading auth object
2 files changed · +67 −0
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+13 −0 modified@@ -1031,6 +1031,19 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen TokenValidation tokenValidation = validateToken(accessToken); Map<String, Object> claims = tokenValidation.getClaims(); + + Object jtiClaim = claims.get(JTI); + + 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." + ); + } + } + accessToken = tokenValidation.getJwt().getEncoded(); // Check token expiry
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+54 −0 modified@@ -31,6 +31,7 @@ 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.util.TokenValidation; import org.cloudfoundry.identity.uaa.zone.ClientServicesExtension; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; @@ -1886,6 +1887,59 @@ public void testLoad_Opaque_AuthenticationForAUser() { System.out.println("newAccessToken = " + newAccessToken); } + @Test + public void loadAuthentication_when_given_an_opaque_refreshToken_should_throw_exception() { + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.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 = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + + String refreshTokenValue = tokenProvisioning.retrieve(compositeToken.getRefreshToken().getValue(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + + tokenServices.loadAuthentication(refreshTokenValue); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void loadAuthentication_when_given_an_refresh_jwt_should_throw_exception() { + IdentityZoneHolder.get().getConfig().getTokenPolicy().setJwtRevocable(true); + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.resourceIds)); + Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters()); + azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE); + + azParameters.put(REQUEST_TOKEN_FORMAT, JWT.getStringValue()); + + authorizationRequest.setRequestParameters(azParameters); + Authentication userAuthentication = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + TokenValidation refreshToken = tokenServices.validateToken(compositeToken.getRefreshToken().getValue()); + + String refreshTokenValue = tokenProvisioning.retrieve(refreshToken.getClaims().get("jti").toString(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + tokenServices.loadAuthentication(refreshTokenValue); + } @Test public void testLoadAuthenticationForAClient() {
4fa3e351ec0bAdd additional jti claim logic when loading auth object
2 files changed · +67 −0
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+13 −0 modified@@ -1029,6 +1029,19 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen TokenValidation tokenValidation = validateToken(accessToken); Map<String, Object> claims = tokenValidation.getClaims(); + + Object jtiClaim = claims.get(JTI); + + 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." + ); + } + } + accessToken = tokenValidation.getJwt().getEncoded(); // Check token expiry
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+54 −0 modified@@ -31,6 +31,7 @@ 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.util.TokenValidation; import org.cloudfoundry.identity.uaa.zone.ClientServicesExtension; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; @@ -1899,6 +1900,59 @@ public void testLoad_Opaque_AuthenticationForAUser() { System.out.println("newAccessToken = " + newAccessToken); } + @Test + public void loadAuthentication_when_given_an_opaque_refreshToken_should_throw_exception() { + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.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 = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + + String refreshTokenValue = tokenProvisioning.retrieve(compositeToken.getRefreshToken().getValue(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + + tokenServices.loadAuthentication(refreshTokenValue); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void loadAuthentication_when_given_an_refresh_jwt_should_throw_exception() { + IdentityZoneHolder.get().getConfig().getTokenPolicy().setJwtRevocable(true); + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.resourceIds)); + Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters()); + azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE); + + azParameters.put(REQUEST_TOKEN_FORMAT, JWT.getStringValue()); + + authorizationRequest.setRequestParameters(azParameters); + Authentication userAuthentication = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + TokenValidation refreshToken = tokenServices.validateToken(compositeToken.getRefreshToken().getValue()); + + String refreshTokenValue = tokenProvisioning.retrieve(refreshToken.getClaims().get("jti").toString(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + tokenServices.loadAuthentication(refreshTokenValue); + } @Test public void testLoadAuthenticationForAClient() {
5d021e83ef14Add additional jti claim logic when loading auth object
2 files changed · +67 −0
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+13 −0 modified@@ -940,6 +940,19 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen TokenValidation tokenValidation = validateToken(accessToken); Map<String, Object> claims = tokenValidation.getClaims(); + + Object jtiClaim = claims.get(JTI); + + 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." + ); + } + } + accessToken = tokenValidation.getJwt().getEncoded(); // Check token expiry
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+54 −0 modified@@ -33,6 +33,7 @@ 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.util.TokenValidation; import org.cloudfoundry.identity.uaa.zone.ClientServicesExtension; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; @@ -1975,6 +1976,59 @@ public void testLoad_Opaque_AuthenticationForAUser() { System.out.println("newAccessToken = " + newAccessToken); } + @Test + public void loadAuthentication_when_given_an_opaque_refreshToken_should_throw_exception() { + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.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 = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + + String refreshTokenValue = tokenProvisioning.retrieve(compositeToken.getRefreshToken().getValue(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + + tokenServices.loadAuthentication(refreshTokenValue); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void loadAuthentication_when_given_an_refresh_jwt_should_throw_exception() { + IdentityZoneHolder.get().getConfig().getTokenPolicy().setJwtRevocable(true); + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.resourceIds)); + Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters()); + azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE); + + azParameters.put(REQUEST_TOKEN_FORMAT, JWT.getStringValue()); + + authorizationRequest.setRequestParameters(azParameters); + Authentication userAuthentication = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + TokenValidation refreshToken = tokenServices.validateToken(compositeToken.getRefreshToken().getValue()); + + String refreshTokenValue = tokenProvisioning.retrieve(refreshToken.getClaims().get("jti").toString(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + tokenServices.loadAuthentication(refreshTokenValue); + } @Test public void testLoadAuthenticationForAClient() {
4cb1be404cf4Add additional jti claim logic when loading auth object
2 files changed · +67 −0
server/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServices.java+13 −0 modified@@ -1035,6 +1035,19 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen TokenValidation tokenValidation = validateToken(accessToken); Map<String, Object> claims = tokenValidation.getClaims(); + + Object jtiClaim = claims.get(JTI); + + 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." + ); + } + } + accessToken = tokenValidation.getJwt().getEncoded(); // Check token expiry
server/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaTokenServicesTests.java+54 −0 modified@@ -31,6 +31,7 @@ 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.util.TokenValidation; import org.cloudfoundry.identity.uaa.zone.ClientServicesExtension; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration; @@ -1886,6 +1887,59 @@ public void testLoad_Opaque_AuthenticationForAUser() { System.out.println("newAccessToken = " + newAccessToken); } + @Test + public void loadAuthentication_when_given_an_opaque_refreshToken_should_throw_exception() { + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.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 = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + + String refreshTokenValue = tokenProvisioning.retrieve(compositeToken.getRefreshToken().getValue(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + + tokenServices.loadAuthentication(refreshTokenValue); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void loadAuthentication_when_given_an_refresh_jwt_should_throw_exception() { + IdentityZoneHolder.get().getConfig().getTokenPolicy().setJwtRevocable(true); + tokenSupport.defaultClient.setAutoApproveScopes(singleton("true")); + AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,tokenSupport.requestedAuthScopes); + authorizationRequest.setResponseTypes(new HashSet(Arrays.asList("token"))); + authorizationRequest.setResourceIds(new HashSet<>(tokenSupport.resourceIds)); + Map<String, String> azParameters = new HashMap<>(authorizationRequest.getRequestParameters()); + azParameters.put(GRANT_TYPE, AUTHORIZATION_CODE); + + azParameters.put(REQUEST_TOKEN_FORMAT, JWT.getStringValue()); + + authorizationRequest.setRequestParameters(azParameters); + Authentication userAuthentication = tokenSupport.defaultUserAuthentication; + + OAuth2Authentication authentication = new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication); + OAuth2AccessToken compositeToken = tokenServices.createAccessToken(authentication); + TokenValidation refreshToken = tokenServices.validateToken(compositeToken.getRefreshToken().getValue()); + + String refreshTokenValue = tokenProvisioning.retrieve(refreshToken.getClaims().get("jti").toString(), IdentityZoneHolder.get().getId()).getValue(); + + expectedException.expect(InvalidTokenException.class); + expectedException.expectMessage("Invalid access token was provided."); + tokenServices.loadAuthentication(refreshTokenValue); + } @Test public void testLoadAuthenticationForAClient() {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
14- github.com/advisories/GHSA-r4v8-9hgx-vm6mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-11047ghsaADVISORY
- github.com/cloudfoundry/uaa/commit/0cd3c6fdd96206a1d6a376ac62e21e59e16cdcb1ghsaWEB
- github.com/cloudfoundry/uaa/commit/2906057dae995024576ce6afdc20abd85569514ghsaWEB
- github.com/cloudfoundry/uaa/commit/4cb1be404cf4a82e39cf2a6357aa17af8b33f2a1ghsaWEB
- github.com/cloudfoundry/uaa/commit/4fa3e351ec0bface3b693810605905e29a9a8569ghsaWEB
- github.com/cloudfoundry/uaa/commit/5d021e83ef143c64179d0da015aa76321ee40988ghsaWEB
- github.com/cloudfoundry/uaa/commit/81aeb7a3aa048ea086c494f725d643e48dd9266ghsaWEB
- github.com/cloudfoundry/uaa/commit/a1d523c7f150e56bf06df8b83ed1d416d6c1d3bghsaWEB
- github.com/cloudfoundry/uaa/commit/aba1fb5f18e0d628628b2d960fc6d0cc62d86f5ghsaWEB
- github.com/cloudfoundry/uaa/commit/b37552d2bf084de059bc965b5ef5a45e64883904ghsaWEB
- github.com/cloudfoundry/uaa/commit/bbbba5aec514ad88e7d1e168a2519c80229f02fghsaWEB
- www.cloudfoundry.org/blog/cve-2018-11047ghsaWEB
- www.cloudfoundry.org/blog/cve-2018-11047/mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.