Skip to content

Commit

Permalink
Add OpenID Connect standard claims in ATs for WLCG JWT profile (#651)
Browse files Browse the repository at this point in the history
With IAM_ACCESS_TOKEN_INCLUDE_AUTHN_INFO=true and in the context of a WLCG JTW profile: if email scope is requested, the email claim is included in the AT; if profile scope is requested, name and preferred_username claims are included in the AT.
  • Loading branch information
rmiccoli committed Oct 5, 2023
1 parent 169c5c4 commit 3f3cb3e
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,31 +61,62 @@ public JWTClaimsSet buildAccessToken(OAuth2AccessTokenEntity token,

builder.notBeforeTime(Date.from(issueTime));

addScopeClaim(builder, token);
addWlcgVerClaim(builder);

if (!isNull(userInfo)) {
Set<String> groupNames =
groupHelper.resolveGroupNames(token, ((UserInfoAdapter) userInfo).getUserinfo());
addWlcgGroupsScopeClaim(builder, groupNames);

if (token.getScope().contains(ATTR_SCOPE)) {
addAttributeScopeClaim(builder, userInfo);
}
if (properties.getAccessToken().isIncludeAuthnInfo()) {
addAuthnInfoClaims(builder, token.getScope(), userInfo);
}
}

addAudience(builder, authentication);

return builder.build();
}

private void addScopeClaim(Builder builder, OAuth2AccessTokenEntity token) {
if (!token.getScope().isEmpty()) {
builder.claim(SCOPE_CLAIM_NAME, token.getScope().stream().collect(joining(SPACE)));
}
}

private void addWlcgVerClaim(Builder builder) {
builder.claim(WLCG_VER_CLAIM, PROFILE_VERSION);
}

if (!isNull(userInfo)) {
Set<String> groupNames =
groupHelper.resolveGroupNames(token, ((UserInfoAdapter) userInfo).getUserinfo());
private void addWlcgGroupsScopeClaim(Builder builder, Set<String> groupNames) {
if (!groupNames.isEmpty()) {
builder.claim(WLCGGroupHelper.WLCG_GROUPS_SCOPE, groupNames);
}
}

if (!groupNames.isEmpty()) {
builder.claim(WLCGGroupHelper.WLCG_GROUPS_SCOPE, groupNames);
}
private void addAttributeScopeClaim(Builder builder, UserInfo userInfo) {
builder.claim(ATTR_SCOPE,
attributeHelper.getAttributeMapFromUserInfo(((UserInfoAdapter) userInfo).getUserinfo()));
}

if (token.getScope().contains(ATTR_SCOPE)) {
builder.claim(ATTR_SCOPE, attributeHelper
.getAttributeMapFromUserInfo(((UserInfoAdapter) userInfo).getUserinfo()));
}
private void addAuthnInfoClaims(Builder builder, Set<String> scopes, UserInfo userInfo) {
if (scopes.contains("email")) {
builder.claim("email", userInfo.getEmail());
}
if (scopes.contains("profile")) {
builder.claim("preferred_username", userInfo.getPreferredUsername());
builder.claim("name", userInfo.getName());
}
}

private void addAudience(Builder builder, OAuth2Authentication authentication) {
if (!hasAudienceRequest(authentication) && !hasRefreshTokenAudienceRequest(authentication)) {
builder.audience(ALL_AUDIENCES_VALUE);
}

return builder.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
@TestPropertySource(properties = {
// @formatter:off
"iam.jwt-profile.default-profile=wlcg",
"iam.access_token.include_authn_info=true",
"scope.matchers[0].name=storage.read",
"scope.matchers[0].type=path",
"scope.matchers[0].prefix=storage.read",
Expand Down Expand Up @@ -747,7 +748,7 @@ public void attributesAreNotIncludedInAccessTokenWhenNotRequested() throws Excep
}

@Test
public void attributesAreIncludedInAccessTokenWhenNotRequested() throws Exception {
public void attributesAreIncludedInAccessTokenWhenRequested() throws Exception {
IamAccount testAccount =
repo.findByUsername(TEST_USER).orElseThrow(assertionError(EXPECTED_USER_NOT_FOUND));

Expand All @@ -773,4 +774,55 @@ public void attributesAreIncludedInAccessTokenWhenNotRequested() throws Exceptio
assertThat(claims.getJSONObjectClaim("attr").get("test"), is("test"));
}

@Test
public void additionalClaimsAreIncludedInAccessTokenWhenRequested() throws Exception {

String tokenResponseJson = mvc
.perform(post("/token").param("grant_type", "password")
.param("client_id", CLIENT_ID)
.param("client_secret", CLIENT_SECRET)
.param("username", "test")
.param("password", "password")
.param("scope", "openid profile email"))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();

JWTClaimsSet claims =
JWTParser.parse(mapper.readTree(tokenResponseJson).get("access_token").asText())
.getJWTClaimsSet();

assertThat(claims.getClaim("email"), notNullValue());
assertThat(claims.getClaim("email"), is("test@iam.test"));
assertThat(claims.getClaim("name"), notNullValue());
assertThat(claims.getClaim("name"), is("Test User"));
assertThat(claims.getClaim("preferred_username"), notNullValue());
assertThat(claims.getClaim("preferred_username"), is("test"));
}

@Test
public void additionalClaimsAreNotIncludedInAccessTokenWhenRNotequested() throws Exception {

String tokenResponseJson = mvc
.perform(post("/token").param("grant_type", "password")
.param("client_id", CLIENT_ID)
.param("client_secret", CLIENT_SECRET)
.param("username", "test")
.param("password", "password")
.param("scope", "openid address"))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();

JWTClaimsSet claims =
JWTParser.parse(mapper.readTree(tokenResponseJson).get("access_token").asText())
.getJWTClaimsSet();

assertThat(claims.getClaim("email"), nullValue());
assertThat(claims.getClaim("name"), nullValue());
assertThat(claims.getClaim("preferred_username"), nullValue());
}

}

0 comments on commit 3f3cb3e

Please sign in to comment.