Skip to content

Commit

Permalink
feat(jwt): Extract the client_id from the aud claim. Fallback to azp …
Browse files Browse the repository at this point in the history
…and then to client_id claim

Closes gravitee-io/issues#1235
  • Loading branch information
brasseld committed May 30, 2018
1 parent de0d52b commit c1a5047
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 18 deletions.
37 changes: 35 additions & 2 deletions src/main/java/io/gravitee/policy/jwt/JWTPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -70,6 +71,8 @@ public class JWTPolicy {
static final String CONTEXT_ATTRIBUTE_JWT_CLAIMS = CONTEXT_ATTRIBUTE_PREFIX + "claims";
static final String CONTEXT_ATTRIBUTE_JWT_TOKEN = CONTEXT_ATTRIBUTE_PREFIX + "token";
static final String CONTEXT_ATTRIBUTE_CLIENT_ID = "client_id";
static final String CONTEXT_ATTRIBUTE_AUDIENCE = "aud";
static final String CONTEXT_ATTRIBUTE_AUTHORIZED_PARTY = "azp";

static final String CONTEXT_ATTRIBUTE_OAUTH_PREFIX = "oauth.";
static final String CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID = CONTEXT_ATTRIBUTE_OAUTH_PREFIX + CONTEXT_ATTRIBUTE_CLIENT_ID;
Expand Down Expand Up @@ -100,7 +103,7 @@ public void onRequest(Request request, Response response, ExecutionContext execu
//3rd set access_token in context
executionContext.setAttribute(CONTEXT_ATTRIBUTE_JWT_TOKEN, jwt);

String clientId = claims.get(CONTEXT_ATTRIBUTE_CLIENT_ID, String.class);
String clientId = getClientId(claims);
executionContext.setAttribute(CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, clientId);

if (configuration.isExtractClaims()) {
Expand All @@ -109,14 +112,44 @@ public void onRequest(Request request, Response response, ExecutionContext execu

//Finally continue the process...
policyChain.doNext(request, response);

}
catch (ExpiredJwtException | MalformedJwtException | SignatureException | IllegalArgumentException| AuthSchemeException e ) {
LOGGER.error(e.getMessage(),e.getCause());
policyChain.failWith(PolicyResult.failure(HttpStatusCode.UNAUTHORIZED_401, "Unauthorized"));
}
}

private String getClientId(DefaultClaims claims) {
String clientId = null;

// Look for the OAuth2 client_id of the Relying Party from the Authorized party claim
String authorizedParty = claims.get(CONTEXT_ATTRIBUTE_AUTHORIZED_PARTY, String.class);
if (authorizedParty != null && ! authorizedParty.isEmpty()) {
clientId = authorizedParty;
}

if (clientId == null) {
// Look for the OAuth2 client_id of the Relying Party from the audience claim
Object audClaim = claims.get(CONTEXT_ATTRIBUTE_AUDIENCE);
if (audClaim != null) {
if (audClaim instanceof List) {
List<String> audiences = (List<String>) audClaim;
// For the moment, we took only the first value of the array
clientId = audiences.get(0);
} else {
clientId = (String) audClaim;
}
}
}

// Is there any client_id claim in JWT claims ?
if (clientId == null) {
clientId = claims.get(CONTEXT_ATTRIBUTE_CLIENT_ID, String.class);
}

return clientId;
}

/**
* Extract JWT from the request.
* First attempt to extract if from standard Authorization header.
Expand Down
175 changes: 159 additions & 16 deletions src/test/java/io/gravitee/policy/jwt/JWTPolicyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void init() {
@Test
public void test_with_cache_disabled_and_gateway_keys_and_valid_authorization_header() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -95,11 +95,153 @@ public void test_with_cache_disabled_and_gateway_keys_and_valid_authorization_he

verify(policyChain,Mockito.times(1)).doNext(request, response);
}


@Test
public void test_get_client_with_client_id_claim() throws Exception {
when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());

JwtBuilder builder = getJsonWebToken(7200);
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_CLIENT_ID, "my-client-id");

String jwt = builder.compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+jwt);
when(request.headers()).thenReturn(headers);
when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);

new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext,Mockito.times(1))
.setAttribute(JWTPolicy.CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, "my-client-id");

verify(policyChain,Mockito.times(1)).doNext(request, response);
}

@Test
public void test_get_client_with_aud_claim() throws Exception {
when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());

JwtBuilder builder = getJsonWebToken(7200);
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_CLIENT_ID, "my-client-id");
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_AUDIENCE, "my-client-id-from-aud");

String jwt = builder.compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+jwt);
when(request.headers()).thenReturn(headers);
when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);

new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext,Mockito.times(1))
.setAttribute(JWTPolicy.CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, "my-client-id-from-aud");

verify(policyChain,Mockito.times(1)).doNext(request, response);
}

@Test
public void test_get_client_with_aud_array_claim() throws Exception {
when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());

JwtBuilder builder = getJsonWebToken(7200);
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_CLIENT_ID, "my-client-id");
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_AUDIENCE, new String [] {"my-client-id-from-aud"});

String jwt = builder.compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+jwt);
when(request.headers()).thenReturn(headers);
when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);

new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext,Mockito.times(1))
.setAttribute(JWTPolicy.CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, "my-client-id-from-aud");

verify(policyChain,Mockito.times(1)).doNext(request, response);
}

@Test
public void test_get_client_with_azp_claim() throws Exception {
when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());

JwtBuilder builder = getJsonWebToken(7200);
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_CLIENT_ID, "my-client-id");
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_AUTHORIZED_PARTY, "my-client-id-from-azp");

String jwt = builder.compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+jwt);
when(request.headers()).thenReturn(headers);
when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);

new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext,Mockito.times(1))
.setAttribute(JWTPolicy.CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, "my-client-id-from-azp");

verify(policyChain,Mockito.times(1)).doNext(request, response);
}

@Test
public void test_get_client_with_multiple_client_claims() throws Exception {
when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());

JwtBuilder builder = getJsonWebToken(7200);
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_CLIENT_ID, "my-client-id");
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_AUDIENCE, new String [] {"my-client-id-from-aud"});
builder.claim(JWTPolicy.CONTEXT_ATTRIBUTE_AUTHORIZED_PARTY, "my-client-id-from-azp");

String jwt = builder.compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+jwt);
when(request.headers()).thenReturn(headers);
when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);

new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext,Mockito.times(1))
.setAttribute(JWTPolicy.CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, "my-client-id-from-azp");

verify(policyChain,Mockito.times(1)).doNext(request, response);
}

@Test
public void test_get_client_without_client_claim() throws Exception {
when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());

JwtBuilder builder = getJsonWebToken(7200);

String jwt = builder.compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+jwt);
when(request.headers()).thenReturn(headers);
when(configuration.getPublicKeyResolver()).thenReturn(PublicKeyResolver.GATEWAY_KEYS);

new JWTPolicy(configuration).onRequest(request, response, executionContext, policyChain);

verify(executionContext,Mockito.times(1))
.setAttribute(JWTPolicy.CONTEXT_ATTRIBUTE_OAUTH_CLIENT_ID, null);

verify(policyChain,Mockito.times(1)).doNext(request, response);
}

@Test
public void test_with_cache_disabled_and_given_key_and_valid_authorization_header() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -120,7 +262,7 @@ public void test_with_cache_disabled_and_given_key_and_valid_authorization_heade
@Test
public void test_with_cache_disabled_and_given_key_using_EL_and_valid_authorization_header() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();
final String property = "prop['key']";

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
Expand All @@ -142,7 +284,7 @@ public void test_with_cache_disabled_and_given_key_using_EL_and_valid_authorizat
@Test
public void test_with_cache_disabled_and_given_key_but_not_provided() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -162,7 +304,7 @@ public void test_with_cache_disabled_and_given_key_but_not_provided() throws Exc
@Test
public void test_with_cache_disabled_and_gateway_keys_and_valid_access_token() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -183,7 +325,7 @@ public void test_with_cache_disabled_and_gateway_keys_and_valid_access_token() t
@Test
public void test_with_cache_disabled_and_gateway_keys_and_expired_header_token() throws Exception {

String jwt = getJsonWebToken(0);
String jwt = getJsonWebToken(0).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -202,7 +344,7 @@ public void test_with_cache_disabled_and_gateway_keys_and_expired_header_token()
@Test
public void test_with_cache_disabled_and_gateway_keys_and_unknonw_issuer() throws Exception {

String jwt = getJsonWebToken(7200,"unknown",null);
String jwt = getJsonWebToken(7200,"unknown",null).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -219,7 +361,7 @@ public void test_with_cache_disabled_and_gateway_keys_and_unknonw_issuer() throw

@Test
public void test_with_cache_disabled_and_given_issuer_and_valid_authorization_header() throws Exception {
String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();
final String resolverParameter = "validIss1|"+ISS+"|validIss3";

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
Expand All @@ -242,7 +384,7 @@ public void test_with_cache_disabled_and_given_issuer_and_valid_authorization_he
@Test
public void test_with_cache_disabled_and_given_issuer_using_EL_and_valid_authorization_header() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();
final String property = "prop['key']";

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
Expand All @@ -265,7 +407,7 @@ public void test_with_cache_disabled_and_given_issuer_using_EL_and_valid_authori
@Test
public void test_with_cache_disabled_and_given_issuer_but_not_provided() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

when(executionContext.getComponent(Environment.class)).thenReturn(environment);
when(environment.getProperty("policy.jwt.issuer.gravitee.authorization.server.MAIN")).thenReturn(getSshRsaKey());
Expand All @@ -285,7 +427,7 @@ public void test_with_cache_disabled_and_given_issuer_but_not_provided() throws
@Test
public void test_not_authentifiction_scheme() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", jwt);
Expand All @@ -299,7 +441,7 @@ public void test_not_authentifiction_scheme() throws Exception {
@Test
public void test_not_authentifiction_scheme_supported() throws Exception {

String jwt = getJsonWebToken(7200);
String jwt = getJsonWebToken(7200).compact();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + jwt);
Expand All @@ -316,7 +458,7 @@ public void test_not_authentifiction_scheme_supported() throws Exception {
* @return String
* @throws Exception
*/
private String getJsonWebToken(long secondsToAdd) throws Exception {
private JwtBuilder getJsonWebToken(long secondsToAdd) throws Exception {
return getJsonWebToken(secondsToAdd,null,null);
}

Expand All @@ -325,7 +467,7 @@ private String getJsonWebToken(long secondsToAdd) throws Exception {
* @return String
* @throws Exception
*/
private String getJsonWebToken(long secondsToAdd, String iss, String kid) throws Exception{
private JwtBuilder getJsonWebToken(long secondsToAdd, String iss, String kid) throws Exception{

Map<String,Object> header = new HashMap<String,Object>(2);
header.put("alg", "RS256");
Expand All @@ -338,7 +480,8 @@ private String getJsonWebToken(long secondsToAdd, String iss, String kid) throws
jwtBuilder.setExpiration(Date.from(Instant.now().plusSeconds(secondsToAdd)));

jwtBuilder.signWith(SignatureAlgorithm.RS256, getPrivateKey());
return jwtBuilder.compact();

return jwtBuilder;
}

/**
Expand Down

0 comments on commit c1a5047

Please sign in to comment.