Skip to content

Commit

Permalink
Rename the "primaryKey" to "activeKey"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy Coffield authored and pivotal committed Mar 14, 2016
1 parent 6972c26 commit 53a296c
Show file tree
Hide file tree
Showing 15 changed files with 46 additions and 51 deletions.
6 changes: 3 additions & 3 deletions docs/UAA-APIs.rst
Expand Up @@ -851,7 +851,7 @@ Request body *example* ::
"tokenPolicy": {
"accessTokenValidity": 4800,
"refreshTokenValidity": 9600,
"primaryKeyId": "key-id-1",
"activeKeyId": "key-id-1",
"keys": {
"key-id-1": {
"signingKey": "pr1V4T3_keY_t3xT"
Expand Down Expand Up @@ -926,7 +926,7 @@ Fields *Available Fields* ::
accessTokenValidity int Optional How long the access token is valid for in seconds.
refreshTokenValidity int Optional How long the refresh token is valid for seconds.
keys Map Optional A map specifying current and historical JWT signing keys, with unique IDs for referring to them. For an explanation of key rotation, see ``/token_key``.
primaryKeyId String Dependent The ID of the signing key that should be used for signing tokens. Optional if only one signing key is specified. For an explanation of key rotation, see ``/token_key``.
activeKeyId String Dependent The ID of the signing key that should be used for signing tokens. Optional if only one signing key is specified. For an explanation of key rotation, see ``/token_key``.

SAML Identity Provider Configuration ``SamlConfig`` (part of Identity Zone Configuration - See class org.cloudfoundry.identity.uaa.zone.SamlConfig)
============================== ==================== ========= ========================================================================================================================================================================
Expand Down Expand Up @@ -2763,7 +2763,7 @@ Get the Token Signing Key: ``GET /token_key``
An endpoint which returns the JSON Web Token (JWT) key, used by the UAA to sign JWT access tokens, and to be used by authorized clients to verify that a token came from the UAA. The key is in JSON Web Key format. For complete information about JSON Web Keys, see RFC 7517 (https://tools.ietf.org/html/rfc7517).
In the case when the token key is symmetric, signer key and verifier key are the same, then this call is authenticated with client credentials using the HTTP Basic method.

JWT signing keys are specified via the identity zone configuration (see ``/identity-zones``). An identity zone token policy can be configured with multiple keys for purposes of key rotation. When adding a new key, set its ID as the ``primaryKeyId`` to use it to sign all new tokens. ``/check_token`` will continue to verify tokens signed with the previous signing key for as long as it is present in the ``keys`` of the identity zone's token policy. Remove it to invalidate all those tokens.
JWT signing keys are specified via the identity zone configuration (see ``/identity-zones``). An identity zone token policy can be configured with multiple keys for purposes of key rotation. When adding a new key, set its ID as the ``activeKeyId`` to use it to sign all new tokens. ``/check_token`` will continue to verify tokens signed with the previous signing key for as long as it is present in the ``keys`` of the identity zone's token policy. Remove it to invalidate all those tokens.

JWT tokens issued by the UAA contain a ``kid`` field, indicating which key should be used for verification of the token. In the case that this is not the primary key, use ``GET /token_keys`` to retrieve all currently valid keys, and select the key that matches the token's ``kid``.

Expand Down
Expand Up @@ -21,8 +21,6 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -50,7 +48,7 @@ private void setKeysLegacy(Map<String, KeyInformation> keys) {
}

private Map<String, String> keys;
private String primaryKeyId;
private String activeKeyId;

public TokenPolicy() {
accessTokenValidity = refreshTokenValidity = -1;
Expand Down Expand Up @@ -113,9 +111,9 @@ public void setSigningKey(String signingKey) {
}
}

public String getPrimaryKeyId() {
if(primaryKeyId != null) {
return primaryKeyId;
public String getActiveKeyId() {
if(activeKeyId != null) {
return activeKeyId;
}

if(keys != null && keys.size() == 1) {
Expand All @@ -125,8 +123,8 @@ public String getPrimaryKeyId() {
return null;
}

public void setPrimaryKeyId(String primaryKeyId) {
this.primaryKeyId = primaryKeyId;
public void setActiveKeyId(String activeKeyId) {
this.activeKeyId = activeKeyId;
}

}
Expand Up @@ -15,7 +15,6 @@
import org.bouncycastle.asn1.ASN1Sequence;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneConfiguration;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.cloudfoundry.identity.uaa.zone.TokenPolicy;
import org.springframework.util.StringUtils;

import java.security.KeyFactory;
Expand Down Expand Up @@ -56,15 +55,15 @@ public KeyInfo getKey(String keyId) {
return getKeys().get(keyId);
}

public KeyInfo getPrimaryKey() {
return getKeys().get(getZonePrimaryKeyId());
public KeyInfo getActiveKey() {
return getKeys().get(getActiveKeyId());
}

private String getZonePrimaryKeyId() {
private String getActiveKeyId() {
IdentityZoneConfiguration config = IdentityZoneHolder.get().getConfig();
if(config == null) return IdentityZoneHolder.getUaaZone().getConfig().getTokenPolicy().getPrimaryKeyId();
String primaryKeyId = config.getTokenPolicy().getPrimaryKeyId();
if(!StringUtils.hasText(primaryKeyId)) return IdentityZoneHolder.getUaaZone().getConfig().getTokenPolicy().getPrimaryKeyId();
if(config == null) return IdentityZoneHolder.getUaaZone().getConfig().getTokenPolicy().getActiveKeyId();
String primaryKeyId = config.getTokenPolicy().getActiveKeyId();
if(!StringUtils.hasText(primaryKeyId)) return IdentityZoneHolder.getUaaZone().getConfig().getTokenPolicy().getActiveKeyId();
return primaryKeyId;
}

Expand Down
Expand Up @@ -69,7 +69,7 @@ public void setSignerProvider(SignerProvider signerProvider) {
@RequestMapping(value = "/token_key", method = RequestMethod.GET)
@ResponseBody
public VerificationKeyResponse getKey(Principal principal) {
KeyInfo key = signerProvider.getPrimaryKey();
KeyInfo key = signerProvider.getActiveKey();
if (!includeSymmetricalKeys(principal) && !key.isPublic()) {
throw new AccessDeniedException("You need to authenticate to see a shared key");
}
Expand Down
Expand Up @@ -369,7 +369,7 @@ private OAuth2AccessToken createAccessToken(String userId,
} catch (JsonUtils.JsonUtilException e) {
throw new IllegalStateException("Cannot convert access token to JSON", e);
}
String token = JwtHelper.encode(content, signerProvider.getPrimaryKey().getSigner()).getEncoded();
String token = JwtHelper.encode(content, signerProvider.getActiveKey().getSigner()).getEncoded();
// This setter copies the value and returns. Don't change.
accessToken.setValue(token);
populateIdToken(accessToken, jwtAccessToken, requestedScopes, responseTypes, clientId, forceIdTokenCreation, externalGroupsForIdToken, user, userAttributesForIdToken);
Expand Down Expand Up @@ -420,7 +420,7 @@ private void populateIdToken(CompositeAccessToken token,
}

String content = JsonUtils.writeValueAsString(clone);
String encoded = JwtHelper.encode(content, signerProvider.getPrimaryKey().getSigner()).getEncoded();
String encoded = JwtHelper.encode(content, signerProvider.getActiveKey().getSigner()).getEncoded();
token.setIdTokenValue(encoded);
} catch (JsonUtils.JsonUtilException e) {
throw new IllegalStateException("Cannot convert ID token to JSON", e);
Expand Down Expand Up @@ -495,7 +495,7 @@ private void populateIdToken(CompositeAccessToken token,
// them up
response.put(AUD, resourceIds);

response.put(KID, signerProvider.getPrimaryKey().getKeyId());
response.put(KID, signerProvider.getActiveKey().getKeyId());

for (String excludedClaim : getExcludedClaims()) {
response.remove(excludedClaim);
Expand Down Expand Up @@ -669,7 +669,7 @@ private ExpiringOAuth2RefreshToken createRefreshToken(OAuth2Authentication authe
} catch (JsonUtils.JsonUtilException e) {
throw new IllegalStateException("Cannot convert access token to JSON", e);
}
String jwtToken = JwtHelper.encode(content, signerProvider.getPrimaryKey().getSigner()).getEncoded();
String jwtToken = JwtHelper.encode(content, signerProvider.getActiveKey().getSigner()).getEncoded();

ExpiringOAuth2RefreshToken refreshToken = new DefaultExpiringOAuth2RefreshToken(jwtToken, token.getExpiration());

Expand Down Expand Up @@ -744,7 +744,7 @@ protected String getUserId(OAuth2Authentication authentication) {
response.put(REVOCATION_SIGNATURE, revocableSignature);
}

response.put(KID, signerProvider.getPrimaryKey().getKeyId());
response.put(KID, signerProvider.getActiveKey().getKeyId());

response.put(AUD, resourceIds);

Expand Down Expand Up @@ -1011,7 +1011,7 @@ private Map<String, Object> getClaimsForToken(String token) {
if(keyId!=null) {
key = signerProvider.getKey(keyId);
} else {
key = signerProvider.getPrimaryKey();
key = signerProvider.getActiveKey();
}

if(key == null) {
Expand Down
Expand Up @@ -38,19 +38,19 @@ public IdentityZoneConfiguration validate(IdentityZoneConfiguration config, Mode

TokenPolicy tokenPolicy = config.getTokenPolicy();
if(tokenPolicy != null) {
String primaryKeyId = tokenPolicy.getPrimaryKeyId();
String activeKeyId = tokenPolicy.getActiveKeyId();
Map<String, String> jwtKeys = tokenPolicy.getKeys();
if(jwtKeys != null) {
if(StringUtils.hasText(primaryKeyId)) {
if(!jwtKeys.containsKey(primaryKeyId)) {
throw new InvalidIdentityZoneConfigurationException("The specified primary key ID is not present in the configured keys: " + primaryKeyId, null);
if(StringUtils.hasText(activeKeyId)) {
if(!jwtKeys.containsKey(activeKeyId)) {
throw new InvalidIdentityZoneConfigurationException("The specified primary key ID is not present in the configured keys: " + activeKeyId, null);
}
} else {
if(jwtKeys.size() > 1) {
throw new InvalidIdentityZoneConfigurationException("Multiple token signing keys are specified, but none is specified to be the primary key.", null);
}
}
} else if(StringUtils.hasText(primaryKeyId)) {
} else if(StringUtils.hasText(activeKeyId)) {
throw new InvalidIdentityZoneConfigurationException("Identity zone cannot specify primary key ID with no zone keys configured.", null);
}
}
Expand Down
Expand Up @@ -598,7 +598,7 @@ public void testZoneValidatesTokenSignedWithOwnKey() throws Exception {
}

@Test
public void testZoneValidatesTokenSignedWithOldPrimaryKey() throws Exception {
public void testZoneValidatesTokenSignedWithInactiveKey() throws Exception {
HashMap<String, String> keys = new HashMap<>();
keys.put("oldKey", "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIBOgIBAAJAcEJMJ3ZT4GgdxipJe4uXvRQFfSpOneGjHfFTLjECMd0OkNtIWoIU\n" +
Expand All @@ -614,9 +614,9 @@ public void testZoneValidatesTokenSignedWithOldPrimaryKey() throws Exception {
tokenServices.afterPropertiesSet();
accessToken = tokenServices.createAccessToken(authentication);

keys.put("newPrimaryKey", "nc978y78o3cg5i7env587geehn89mcehgc46");
keys.put("newKey", "nc978y78o3cg5i7env587geehn89mcehgc46");
configureDefaultZoneKeys(keys);
IdentityZoneHolder.get().getConfig().getTokenPolicy().setPrimaryKeyId("newPrimaryKey");
IdentityZoneHolder.get().getConfig().getTokenPolicy().setActiveKeyId("newKey");

Claims result = endpoint.checkToken(accessToken.getValue(), Collections.emptyList());
}
Expand All @@ -639,9 +639,9 @@ public void testZoneValidatesTokenSignedWithRemovedKey() throws Exception {
accessToken = tokenServices.createAccessToken(authentication);

keys.remove("oldKey");
keys.put("newPrimaryKey", "nc978y78o3cg5i7env587geehn89mcehgc46");
keys.put("newKey", "nc978y78o3cg5i7env587geehn89mcehgc46");
configureDefaultZoneKeys(keys);
IdentityZoneHolder.get().getConfig().getTokenPolicy().setPrimaryKeyId("newPrimaryKey");
IdentityZoneHolder.get().getConfig().getTokenPolicy().setActiveKeyId("newKey");

Claims result = endpoint.checkToken(accessToken.getValue(), Collections.emptyList());
}
Expand Down
Expand Up @@ -218,7 +218,7 @@ public void setUp() throws Exception {
keys.put("testKey", "9c247h8yt978w3nv45y978w45hntv6");
keys.put("otherKey", "unc0uf98gv89egh4v98749978hv");
tokenPolicy.setKeys(keys);
tokenPolicy.setPrimaryKeyId("testKey");
tokenPolicy.setActiveKeyId("testKey");
config.setTokenPolicy(tokenPolicy);
zone.setConfig(config);
when(provisioning.retrieve("uaa")).thenReturn(zone);
Expand Down Expand Up @@ -375,9 +375,9 @@ public void testCreateAccessTokenAuthcodeGrant() {

@Test
public void testCreateAccessTokenAuthcodeGrantSwitchedPrimaryKey() {
String originalPrimaryKeyId = tokenPolicy.getPrimaryKeyId();
String originalPrimaryKeyId = tokenPolicy.getActiveKeyId();
try {
tokenPolicy.setPrimaryKeyId("otherKey");
tokenPolicy.setActiveKeyId("otherKey");

AuthorizationRequest authorizationRequest = new AuthorizationRequest(CLIENT_ID,requestedAuthScopes);
authorizationRequest.setResourceIds(new HashSet<>(resourceIds));
Expand All @@ -403,7 +403,7 @@ public void testCreateAccessTokenAuthcodeGrantSwitchedPrimaryKey() {

this.assertCommonEventProperties(accessToken, userId, buildJsonString(requestedAuthScopes));
} finally {
tokenPolicy.setPrimaryKeyId(originalPrimaryKeyId);
tokenPolicy.setActiveKeyId(originalPrimaryKeyId);
}
}

Expand Down
Expand Up @@ -48,7 +48,7 @@ else if (token instanceof OAuth2RefreshToken)
} catch (Exception e) {
throw new IllegalArgumentException("Unable to decode token", e);
}
tokenJwt.verifySignature(signer.getKey((String) claims.getOrDefault("kid", signer.getPrimaryKey())).getVerifier());
tokenJwt.verifySignature(signer.getKey((String) claims.getOrDefault("kid", signer.getActiveKey())).getVerifier());
return claims;
}
}
2 changes: 1 addition & 1 deletion uaa/src/main/resources/uaa.yml
Expand Up @@ -237,7 +237,7 @@ oauth:
# accessTokenValiditySeconds: 3600
# refreshTokenValiditySeconds: 3600
# keys:
# primaryKey: key-id-1
# activeKey: key-id-1
# key-id-1:
# signingKey: |
# -----BEGIN RSA PRIVATE KEY-----
Expand Down
2 changes: 1 addition & 1 deletion uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml
Expand Up @@ -325,7 +325,7 @@
<constructor-arg name="accessTokenValidity" value="${jwt.token.policy.accessTokenValiditySeconds:#{globalTokenPolicy.getAccessTokenValidity()}}" />
<constructor-arg name="refreshTokenValidity" value="${jwt.token.policy.refreshTokenValiditySeconds:#{globalTokenPolicy.getRefreshTokenValidity()}}" />
<constructor-arg name="signingKeysMap" ref="signingKeysMap" />
<property name="primaryKeyId" value="${jwt.token.policy.primaryKey:#{null}}" />
<property name="activeKeyId" value="${jwt.token.policy.activeKey:#{null}}" />
</bean>

<bean id="signingKeysMap" class="org.cloudfoundry.identity.uaa.zone.SigningKeysMap" factory-bean="keyPairsFactory" factory-method="getKeyPairsMap" />
Expand Down
Expand Up @@ -574,7 +574,7 @@ public void bootstrap_map_of_signing_and_verification_keys_in_default_zone() thr
assertTrue(keys.keySet().contains("key-id-1"));
String signingKey = keys.get("key-id-1");
assertThat(signingKey, containsString("test-signing-key"));
assertThat(uaaTokenPolicy.getPrimaryKeyId(), is("key-id-2"));
assertThat(uaaTokenPolicy.getActiveKeyId(), is("key-id-2"));
}

@Test
Expand Down
Expand Up @@ -20,9 +20,7 @@
import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils;
import org.cloudfoundry.identity.uaa.oauth.DisableIdTokenResponseTypeFilter;
import org.cloudfoundry.identity.uaa.oauth.KeyInfo;
import org.cloudfoundry.identity.uaa.oauth.SignerProvider;
import org.cloudfoundry.identity.uaa.oauth.UaaAuthorizationEndpoint;
import org.cloudfoundry.identity.uaa.oauth.UaaTokenServices;
import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants;
import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants;
Expand Down Expand Up @@ -1323,7 +1321,7 @@ private Map<String, Object> getClaimsForToken(String token) {
throw new IllegalStateException("Cannot read token claims", e);
}

String kid = (String) claims.getOrDefault("kid", signerProvider.getPrimaryKey());
String kid = (String) claims.getOrDefault("kid", signerProvider.getActiveKey());
assertNotNull("Token should have a key ID.", kid);
tokenJwt.verifySignature(signerProvider.getKey(kid).getVerifier());

Expand Down
Expand Up @@ -532,7 +532,7 @@ public void testCreateZoneAndIdentityProvider() throws Exception {
jwtKeys.put("key_id_1", "secret_key_1");
jwtKeys.put("key_id_2", "secret_key_2");
tokenPolicy.setKeys(jwtKeys);
tokenPolicy.setPrimaryKeyId("key_id_1");
tokenPolicy.setActiveKeyId("key_id_1");

SamlConfig samlConfig = new SamlConfig();

Expand Down Expand Up @@ -624,7 +624,7 @@ public void testCreateZoneWithInvalidPrimarySigningKeyId() throws Exception {
jwtKeys.put("key_id_1", "secret_key_1");
jwtKeys.put("key_id_2", "secret_key_2");
tokenPolicy.setKeys(jwtKeys);
tokenPolicy.setPrimaryKeyId("nonexistent_key");
tokenPolicy.setActiveKeyId("nonexistent_key");

getMockMvc().perform(
post("/identity-zones")
Expand Down Expand Up @@ -679,7 +679,7 @@ public void testCreateZoneWithInvalidSamlKeyCertPair() throws Exception {
jwtKeys.put("key_id_1", "secret_key_1");
jwtKeys.put("key_id_2", "secret_key_2");
tokenPolicy.setKeys(jwtKeys);
tokenPolicy.setPrimaryKeyId("key_id_1");
tokenPolicy.setActiveKeyId("key_id_1");

SamlConfig samlConfig = new SamlConfig();

Expand Down Expand Up @@ -1333,4 +1333,4 @@ private List<ScimUser> getUsersInZone(String subdomain, String token) throws Exc
return JsonUtils.readValue(root.get("resources").toString(), new TypeReference<List<ScimUser>>() {
});
}
}
}
2 changes: 1 addition & 1 deletion uaa/src/test/resources/test/bootstrap/uaa.yml
Expand Up @@ -14,7 +14,7 @@ jwt:
exclude:
- authorities
policy:
primaryKey: key-id-2
activeKey: key-id-2
keys:
key-id-1:
signingKey: |
Expand Down

0 comments on commit 53a296c

Please sign in to comment.