Skip to content

Support custom role claim#1492

Merged
jfallows merged 13 commits intoaklivity:developfrom
akrambek:bug/metrics
Jun 11, 2025
Merged

Support custom role claim#1492
jfallows merged 13 commits intoaklivity:developfrom
akrambek:bug/metrics

Conversation

@akrambek
Copy link
Contributor

@akrambek akrambek commented Jun 8, 2025

Fixes #1476

JwtOptionsConfig(
String issuer,
String audience,
String guarded,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with roles instead of guarded.

.map(Arrays::asList)
.orElse(null);
List<String> roles = null;
String path = (this.roles != null && !this.roles.isEmpty()) ? this.roles : "scope";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schema should prevent empty string value if not permitted here and this code can then be simplified to remove the check.
The value of roles should be defaulted in the builder during parse to avoid implementing the defaulting logic here where options are being used.

Comment on lines +187 to +203
List<String> roles = null;
String path = (this.roles != null && !this.roles.isEmpty()) ? this.roles : "scope";
Object claimObj = claimValue(claims, path);

if (claimObj instanceof List)
{
List<Object> listClaim = (List<Object>) claimObj;
roles = listClaim.stream()
.map(Object::toString)
.map(String::intern)
.collect(Collectors.toList());
}
else if (claimObj != null)
{
roles = Arrays.asList(claimObj.toString().split(SCOPE_VALUE_PATTERN));
roles.replaceAll(String::intern);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
List<String> roles = null;
String path = (this.roles != null && !this.roles.isEmpty()) ? this.roles : "scope";
Object claimObj = claimValue(claims, path);
if (claimObj instanceof List)
{
List<Object> listClaim = (List<Object>) claimObj;
roles = listClaim.stream()
.map(Object::toString)
.map(String::intern)
.collect(Collectors.toList());
}
else if (claimObj != null)
{
roles = Arrays.asList(claimObj.toString().split(SCOPE_VALUE_PATTERN));
roles.replaceAll(String::intern);
}
Object rolesValue = claimValue(claims, this.roles);
@SuppressWarnings("unchecked")
List<String> rolesValueAsList = (rolesValue instanceof List)
? (List<String>) rolesValue
: Optional.ofNullable(rolesValue)
.map(Object::toString)
.map(s -> s.split("\\s+"))
.map(Arrays::asList)
.orElse(null);
List<String> roles = rolesValueAsList != null
? rolesValueAsList.stream()
.map(Object::toString)
.map(String::intern)
.toList()
: null;

Comment on lines +676 to +678
Map<String, Object> realmAccess = new HashMap<>();
realmAccess.put("roles", asList("default-roles-backend", "offline_access", "uma_authorization"));
claims.setClaim("realm_access", realmAccess);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Map<String, Object> realmAccess = new HashMap<>();
realmAccess.put("roles", asList("default-roles-backend", "offline_access", "uma_authorization"));
claims.setClaim("realm_access", realmAccess);
claims.setClaim("realm_access",
Map.of("roles", List.of("default-roles-backend", "offline_access", "uma_authorization")));

.inject(identity())
.issuer("test issuer")
.audience("testAudience")
.roles("scope")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should already default roles to scope in the builder .build() method, so it can be omitted here and still have the value scope in the returned JwtOptionsConfig object.

.inject(identity())
.issuer("test issuer")
.audience("testAudience")
.roles("scope")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same feedback here.

Comment on lines +56 to +57
private static final String SCOPE_VALUE_PATTERN = "\\s+";
private static final String SCOPE_PATH_PATTERN = "\\.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private static final String SCOPE_VALUE_PATTERN = "\\s+";
private static final String SCOPE_PATH_PATTERN = "\\.";
private static final String SPLIT_VALUE_PATTERN = "\\s+";
private static final String SPLIT_PATH_PATTERN = "\\.";


List<String> roles = rolesValueAsList != null
? rolesValueAsList.stream()
.map(Object::toString)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this line be removed since the type of rolesValueAsList is already known to be List<String>, so individual role members are already known to be of type String?

@jfallows jfallows merged commit 09b88c8 into aklivity:develop Jun 11, 2025
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom Role Claim Support in Zilla JWT Validation

2 participants