Skip to content

Commit 78b3174

Browse files
authored
Merge pull request quarkusio#50367 from michalvavrik/feature/fix-step-up-auth-with-kc-single-val-attr
Fix step authentication with Keycloak when protocol mapper has 'acr' user attribute configured as single valued string
2 parents 9891318 + 686d184 commit 78b3174

File tree

3 files changed

+44
-12
lines changed

3 files changed

+44
-12
lines changed

extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/StepUpAuthenticationPolicy.java

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ record StepUpAuthenticationPolicy(String[] expectedAcrValues, Long maxAge) imple
2626
private static volatile boolean enabled = false;
2727

2828
StepUpAuthenticationPolicy(String acrValues, Duration maxAge) {
29-
this(acrValues.split(","), maxAge == null ? null : maxAge.toSeconds());
29+
this(splitAcrValues(acrValues), maxAge == null ? null : maxAge.toSeconds());
3030
}
3131

3232
private static final Logger LOG = Logger.getLogger(StepUpAuthenticationPolicy.class);
@@ -63,17 +63,21 @@ private void verifyMaxAge(JsonObject json) {
6363
}
6464

6565
private void verifyAcr(JsonObject json) {
66-
JsonArray acr = json.getJsonArray(OidcConstants.ACR);
67-
if (acr != null && !acr.isEmpty()) {
68-
boolean acrFound = true;
69-
for (String expectedAcrValue : expectedAcrValues) {
70-
if (!acr.contains(expectedAcrValue)) {
71-
LOG.debug("Acr value " + expectedAcrValue + " is required but not found in token 'acr' claim: " + acr);
72-
acrFound = false;
73-
break;
66+
Object acrObject = json.getValue(OidcConstants.ACR);
67+
if (acrObject != null) {
68+
if (acrObject instanceof JsonArray acr && !acr.isEmpty()) {
69+
boolean acrFound = true;
70+
for (String expectedAcrValue : expectedAcrValues) {
71+
if (!acr.contains(expectedAcrValue)) {
72+
LOG.debug("Acr value " + expectedAcrValue + " is required but not found in token 'acr' claim: " + acr);
73+
acrFound = false;
74+
break;
75+
}
7476
}
75-
}
76-
if (acrFound) {
77+
if (acrFound) {
78+
return;
79+
}
80+
} else if (expectedAcrValues.length == 1 && expectedAcrValues[0].equals(acrObject)) {
7781
return;
7882
}
7983
}
@@ -147,4 +151,12 @@ static void markAsEnabled() {
147151
static boolean isEnabled() {
148152
return enabled;
149153
}
154+
155+
private static String[] splitAcrValues(String acrValues) {
156+
String[] acrValuesArr = acrValues.split(",");
157+
if (acrValuesArr.length > 1) {
158+
return Set.of(acrValuesArr).toArray(String[]::new);
159+
}
160+
return acrValuesArr;
161+
}
150162
}

integration-tests/oidc-tenancy/src/main/java/io/quarkus/it/keycloak/OidcResource.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,11 @@ private String jwt(String audience, String subject, String kid, boolean withEmpt
425425
}
426426

427427
if (acr != null && !acr.isEmpty()) {
428-
builder.claim("acr", Arrays.asList(acr.split(",")));
428+
if (acr.endsWith("_string")) {
429+
builder.claim("acr", acr);
430+
} else {
431+
builder.claim("acr", Arrays.asList(acr.split(",")));
432+
}
429433
}
430434

431435
if (authTime != null && !authTime.isEmpty()) {

integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenStepUpAuthenticationTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,15 @@ public void testMaxAgeAndAcrRequired() {
232232
.body(is("max-age-and-acr-required"));
233233
}
234234

235+
@Test
236+
public void testSingleAcrStringValueRequired() {
237+
// wrong single acr -> fail
238+
stepUpMethodLevelRequest(Set.of("3_string"), "no-rbac-annotation-string").statusCode(401);
239+
// correct acr -> pass
240+
stepUpMethodLevelRequest(Set.of("1_string"), "no-rbac-annotation-string").statusCode(200)
241+
.body(is("no-rbac-annotation"));
242+
}
243+
235244
private static ValidatableResponse stepUpMethodLevelRequest(Set<String> acrValues, String path) {
236245
return stepUpMethodLevelRequest(acrValues, path, null);
237246
}
@@ -350,6 +359,13 @@ public String noRbacAnnotationMethodLevel() {
350359
return "no-rbac-annotation";
351360
}
352361

362+
@GET
363+
@AuthenticationContext("1_string")
364+
@Path("no-rbac-annotation-string")
365+
public String noRbacAnnotationMethodLevelString() {
366+
return "no-rbac-annotation";
367+
}
368+
353369
@GET
354370
@AuthenticationContext("3")
355371
@RolesAllowed("user")

0 commit comments

Comments
 (0)