Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change default of refresh token format #2406

Merged
merged 4 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import java.util.stream.Collector;
import java.util.stream.Collectors;

import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.JWT;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.OPAQUE;

@JsonIgnoreProperties(ignoreUnknown = true)
public class TokenPolicy {
Expand All @@ -43,7 +43,7 @@ public class TokenPolicy {
private boolean jwtRevocable = false;
private boolean refreshTokenUnique = false;
private boolean refreshTokenRotate = false;
private String refreshTokenFormat = JWT.getStringValue();
private String refreshTokenFormat = OPAQUE.getStringValue();

@JsonGetter("keys")
@JsonInclude(JsonInclude.Include.NON_NULL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void test_default_values() {
assertFalse(policy.isRefreshTokenUnique());
assertFalse(policy.isJwtRevocable());
assertFalse(policy.isRefreshTokenRotate());
assertEquals(TokenConstants.TokenFormat.JWT.getStringValue(), policy.getRefreshTokenFormat());
assertEquals(TokenConstants.TokenFormat.OPAQUE.getStringValue(), policy.getRefreshTokenFormat());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ public void testCreateAccessTokenRefreshGrant() {
@Test
public void testCreateAccessTokenRefreshGrant_with_an_old_refresh_token_format_containing_scopes_claim() {
//Given
IdentityZoneHolder.get().getConfig().getTokenPolicy().setRefreshTokenFormat(JWT.getStringValue());
OAuth2AccessToken accessToken = getOAuth2AccessToken();
String refreshTokenJwt = accessToken.getRefreshToken().getValue();

Expand Down Expand Up @@ -1879,6 +1880,7 @@ public void testWrongClientDoesNotLeakToken() {

@Test
public void createRefreshToken_JwtDoesNotContainScopeClaim() {
IdentityZoneHolder.get().getConfig().getTokenPolicy().setRefreshTokenFormat(JWT.getStringValue());
AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID, tokenSupport.requestedAuthScopes);
Map<String, String> authzParameters = new HashMap<>(authorizationRequest.getRequestParameters());
authzParameters.put(GRANT_TYPE, GRANT_TYPE_PASSWORD);
Expand Down
2 changes: 1 addition & 1 deletion uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@
<property name="activeKeyId" value="${jwt.token.policy.activeKeyId:#{null}}"/>
<property name="jwtRevocable" value="${jwt.token.revocable:false}"/>
<property name="refreshTokenFormat"
value="${jwt.token.refresh.format:#{T(org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat).JWT.getStringValue()}}"/>
value="${jwt.token.refresh.format:#{T(org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat).OPAQUE.getStringValue()}}"/>
<property name="refreshTokenUnique" value="${jwt.token.refresh.unique:false}"/>
<property name="refreshTokenRotate" value="${jwt.token.refresh.rotate:false}"/>
</bean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ void reset() {
}

private void createClientAndUserInRandomZone() throws Exception {
createClientAndUserInRandomZone(null);
}

private void createClientAndUserInRandomZone(String refreshTokenFormat) throws Exception {
RandomValueStringGenerator generator = new RandomValueStringGenerator();
zone = setupIdentityZone(generator.generate());
IdentityZoneHolder.set(zone);
Expand All @@ -151,6 +155,9 @@ private void createClientAndUserInRandomZone() throws Exception {
keys.put("key2", signingKey2);
zone.getConfig().getTokenPolicy().setKeys(keys);
zone.getConfig().getTokenPolicy().setActiveKeyId("key1");
if (refreshTokenFormat != null) {
zone.getConfig().getTokenPolicy().setRefreshTokenFormat(refreshTokenFormat);
}
zone = identityZoneProvisioning.update(zone);

String clientId = "refreshclient";
Expand Down Expand Up @@ -284,7 +291,7 @@ void test_refresh_token_after_key_rotation() throws Exception {

@Test
void test_default_refresh_tokens_count() throws Exception {
createClientAndUserInRandomZone();
createClientAndUserInRandomZone("jwt");
template.update("delete from revocable_tokens");
assertEquals(0, countTokens(client.getClientId(), user.getId()));
getJwtRefreshToken(client.getClientId(), SECRET, user.getUserName(), SECRET, getZoneHostUrl(zone));
Expand Down Expand Up @@ -411,7 +418,7 @@ void refreshTokenGrantType_returnsIdToken_toOpenIdClients_withOpaqueRefreshToken

@Test
void refreshTokenGrantType_withJwtTokens_preservesRefreshTokenExpiryClaim() throws Exception {
createClientAndUserInRandomZone();
createClientAndUserInRandomZone("jwt");
when(timeService.getCurrentTimeMillis()).thenReturn(1000L);
CompositeToken tokenResponse = getTokensWithPasswordGrant(client.getClientId(), SECRET, user.getUserName(), SECRET, getZoneHostUrl(zone), "jwt");
String refreshToken = tokenResponse.getRefreshToken().getValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@TestPropertySource(properties = {"uaa.url=https://localhost:8080/uaa"})
@TestPropertySource(properties = {"uaa.url=https://localhost:8080/uaa", "jwt.token.refresh.format=jwt"})
public class TokenMvcMockTests extends AbstractTokenMockMvcTests {
private String BADSECRET = "badsecret";
protected RandomValueStringGenerator generator = new RandomValueStringGenerator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class IdentityZoneEndpointDocs extends EndpointDocs {
private static final String KEYS_CERT_DESC = "PEM encoded X.509 to be used in x5c, e.g. [RFC7517](https://tools.ietf.org/html/rfc7517#section-4.7)";
private static final String ACCESS_TOKEN_VALIDITY_DESC = "Time in seconds between when a access token is issued and when it expires. Defaults to global `accessTokenValidity`";
private static final String REFRESH_TOKEN_VALIDITY_DESC = "Time in seconds between when a refresh token is issued and when it expires. Defaults to global `refreshTokenValidity`";
private static final String REFRESH_TOKEN_FORMAT = "The format for the refresh token. Allowed values are `jwt`, `opaque`. Defaults to `jwt`.";
private static final String REFRESH_TOKEN_FORMAT = "The format for the refresh token. Allowed values are `jwt`, `opaque`. Defaults to `opaque`.";
private static final String REFRESH_TOKEN_UNIQUE = "If true, uaa will only issue one refresh token per client_id/user_id combination. Defaults to `false`.";
private static final String REFRESH_TOKEN_ROTATE = "If true, uaa will issue a new refresh token value in grant type refresh_token. Defaults to `false`.";
private static final String JWT_REVOCABLE_DESC = "Set to true if JWT tokens should be stored in the token store, and thus made individually revocable. Opaque tokens are always stored and revocable.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA;
import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_AUTHORIZATION_CODE;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.JWT;
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.OPAQUE;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -2258,7 +2257,7 @@ private IdentityZone createZoneReturn() throws Exception {
assertEquals(id.toLowerCase(), zone.getSubdomain());
assertFalse(zone.getConfig().getTokenPolicy().isRefreshTokenUnique());
assertFalse(zone.getConfig().getTokenPolicy().isRefreshTokenRotate());
assertEquals(JWT.getStringValue(), zone.getConfig().getTokenPolicy().getRefreshTokenFormat());
assertEquals(OPAQUE.getStringValue(), zone.getConfig().getTokenPolicy().getRefreshTokenFormat());
checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent, zoneModifiedEventListener, IdentityZone.getUaaZoneId(), "http://localhost:8080/uaa/oauth/token", "identity");

//validate that default groups got created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@

@DisplayName("Uaa Token Services Tests")
@DefaultTestContext
@TestPropertySource(properties = {"uaa.url=https://uaa.some.test.domain.com:555/uaa"})
@TestPropertySource(properties = {"uaa.url=https://uaa.some.test.domain.com:555/uaa", "jwt.token.refresh.format=jwt"})
class UaaTokenServicesTests {
@Autowired
private UaaTokenServices tokenServices;
Expand Down
6 changes: 5 additions & 1 deletion uaa/src/test/resources/integration_test_properties.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ jwt:
HYeZSkMuQDYHcaO9xtYP3QdhD+nLXbNrCxaSSaSX8tS4BjdcSH1yMyLFg5OqiJYg
wYFiptyKFm5QqFhFTY+20aE=
-----END PRIVATE KEY-----

revocable: false
refresh:
format: opaque
rotate: false
unique: false
login:
serviceProviderKey: |
-----BEGIN RSA PRIVATE KEY-----
Expand Down