Proposal
Rather than using subdomains, for example somedomain.login.system.com, this issue proposes using a path /z/{subdomain}/oauth/token
What this actually means
Implementation is 100% backwards compatible.
Paths are used to express an identity zone, they are not a new definition of an identity zone.
Tokens issued via subdomains (Host header) and tokens issued via path based identity zones have the same issuer, ie tokens do not adjust claim values.
Requirements
- Zone path will only work when used against the default domain. If a zone path is added to a subdomain of the UAA, it will return a 400 error.
- subdomain based zones will continue to work
- There is no zone path for the default zone, unless the path is zone-id rather than subdomain
- Cookies must have the path set correctly
- Cookie flapping can occur, two cookies with JSESSIONID being sent, so cookie filtering must occur on the server
Example Test
public class ZonePathTokenMockMvcTests extends AbstractTokenMockMvcTests {
private enum ZoneResolutionMode {
SUBDOMAIN(subdomain -> get("/oauth/token")
.with(new SetServerNameRequestPostProcessor(subdomain + ".localhost"))
),
ZONE_PATH(subdomain -> get("/z/{subdomain}/oauth/token", subdomain));
private final Function<String, MockHttpServletRequestBuilder> requestBuilder;
ZoneResolutionMode(Function<String, MockHttpServletRequestBuilder> requestBuilder) {
this.requestBuilder = requestBuilder;
}
MockHttpServletRequestBuilder createRequestBuilder(String subdomain) {
return requestBuilder.apply(subdomain);
}
}
@ParameterizedTest
@EnumSource(ZoneResolutionMode.class)
void clientCredentialsGrant(ZoneResolutionMode mode) throws Exception {
String subdomain = "testzone" + generator.generate().toLowerCase();
IdentityZone testZone = setupIdentityZone(subdomain);
IdentityZoneHolder.set(testZone);
setupIdentityProvider(OriginKeys.UAA);
String scopes = "uaa.admin";
String clientId = "testclient" + generator.generate();
setUpClients(clientId, scopes, scopes, "client_credentials", true, null, null);
IdentityZoneHolder.clear();
String tokenResult = mockMvc.perform(mode.createRequestBuilder(subdomain)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_FORM_URLENCODED)
.param(OAuth2Utils.GRANT_TYPE, GRANT_TYPE_CLIENT_CREDENTIALS)
.param(OAuth2Utils.RESPONSE_TYPE, "token")
.param(OAuth2Utils.CLIENT_ID, clientId)
.param(CLIENT_SECRET, SECRET)
.param(OAuth2Utils.REDIRECT_URI, TEST_REDIRECT_URI))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andReturn().getResponse().getContentAsString();
Map<String, Object> response = JsonUtils.readValueAsMap(tokenResult);
Jwt tokenClaims = JwtHelper.decode((String) response.get("access_token"));
Assertions.assertThat(tokenClaims.getClaimSet().getStringClaim(ISS))
.isEqualTo("http://%s.localhost:8080/uaa/oauth/token".formatted(testZone.getSubdomain()));
}
}
Decisions
- Should the path use subdomain or zone-id?
/z/{subdomain}/oauth/token versus /z/{zone-id}/oauth/token? Or /zid/{zone-id}/ and /zs/{subdomain}/ to support both?
- Do we support
/z/{subdomain}/ everywhere, or should we only support it for UI (cookie based) and simply rewrite the URLs for all other places?
- Should the JSESSIONID cookie name be different based on domain? example JSESSIONID-foobar for the foobar domain? (special characters, encoding)
Proposal
Rather than using subdomains, for example
somedomain.login.system.com, this issue proposes using a path/z/{subdomain}/oauth/tokenWhat this actually means
Implementation is 100% backwards compatible.
Paths are used to express an identity zone, they are not a new definition of an identity zone.
Tokens issued via subdomains (Host header) and tokens issued via path based identity zones have the same issuer, ie tokens do not adjust claim values.
Requirements
Example Test
Decisions
/z/{subdomain}/oauth/tokenversus/z/{zone-id}/oauth/token? Or/zid/{zone-id}/and/zs/{subdomain}/to support both?/z/{subdomain}/everywhere, or should we only support it for UI (cookie based) and simply rewrite the URLs for all other places?