diff --git a/extensions/oidc-token-propagation-reactive/deployment/pom.xml b/extensions/oidc-token-propagation-reactive/deployment/pom.xml
index b2da0cf50cd25..1c3757368a4f0 100644
--- a/extensions/oidc-token-propagation-reactive/deployment/pom.xml
+++ b/extensions/oidc-token-propagation-reactive/deployment/pom.xml
@@ -56,6 +56,11 @@
rest-assured
test
+
+ net.sourceforge.htmlunit
+ htmlunit
+ test
+
diff --git a/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/FrontendResource.java b/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/FrontendResource.java
index a5a3d0ee9fe97..960c894cad4f0 100644
--- a/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/FrontendResource.java
+++ b/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/FrontendResource.java
@@ -8,8 +8,6 @@
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.eclipse.microprofile.rest.client.inject.RestClient;
-import io.quarkus.security.identity.CurrentIdentityAssociation;
-
@Path("/frontend")
public class FrontendResource {
@Inject
@@ -19,9 +17,6 @@ public class FrontendResource {
@Inject
JsonWebToken jwt;
- @Inject
- CurrentIdentityAssociation identityAssociation;
-
@GET
@Path("token-propagation")
@RolesAllowed("admin")
@@ -31,7 +26,7 @@ public String userNameTokenPropagation() {
@GET
@Path("token-propagation-with-augmentor")
- @RolesAllowed("tester") // tester role is granted by SecurityIdentityAugmentor
+ @RolesAllowed("Bearertester") // Bearertester role is granted by SecurityIdentityAugmentor
public String userNameTokenPropagationWithSecIdentityAugmentor() {
return getResponseWithExchangedUsername();
}
diff --git a/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/OidcTokenPropagationWithSecurityIdentityAugmentorTest.java b/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/OidcTokenPropagationWithSecurityIdentityAugmentorTest.java
index e918f9f04d6af..79d4d1f89b9c5 100644
--- a/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/OidcTokenPropagationWithSecurityIdentityAugmentorTest.java
+++ b/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/OidcTokenPropagationWithSecurityIdentityAugmentorTest.java
@@ -2,13 +2,21 @@
import static io.quarkus.oidc.token.propagation.reactive.RolesSecurityIdentityAugmentor.SUPPORTED_USER;
import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.io.IOException;
import java.util.Set;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import com.gargoylesoftware.htmlunit.SilentCssErrorHandler;
+import com.gargoylesoftware.htmlunit.TextPage;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.oidc.server.OidcWiremockTestResource;
@@ -51,4 +59,25 @@ public String getBearerAccessToken() {
return OidcWiremockTestResource.getAccessToken(SUPPORTED_USER, Set.of("admin"));
}
+ @Test
+ public void testGetUserNameWithTokenPropagationWithCodeFlow() throws IOException, InterruptedException {
+ try (final WebClient webClient = createWebClient()) {
+ HtmlPage page = webClient.getPage("http://localhost:8081/frontend/token-propagation-with-augmentor");
+
+ HtmlForm form = page.getFormByName("form");
+ form.getInputByName("username").type("alice");
+ form.getInputByName("password").type("alice");
+
+ TextPage textPage = form.getInputByValue("login").click();
+
+ assertEquals("Token issued to alice has been exchanged, new user name: bob", textPage.getContent());
+ webClient.getCookieManager().clearCookies();
+ }
+ }
+
+ private WebClient createWebClient() {
+ WebClient webClient = new WebClient();
+ webClient.setCssErrorHandler(new SilentCssErrorHandler());
+ return webClient;
+ }
}
diff --git a/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/RolesResource.java b/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/RolesResource.java
index 80d7167434c0c..6ea97cbe2a7c1 100644
--- a/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/RolesResource.java
+++ b/extensions/oidc-token-propagation-reactive/deployment/src/test/java/io/quarkus/oidc/token/propagation/reactive/RolesResource.java
@@ -19,7 +19,8 @@ public class RolesResource {
@GET
public String get() {
if ("bob".equals(jwt.getName())) {
- return "tester";
+ String tokenType = jwt.getClaim("typ");
+ return tokenType + "tester";
}
throw new ForbiddenException("Only user 'bob' is allowed to request roles");
}
diff --git a/extensions/oidc-token-propagation-reactive/deployment/src/test/resources/application.properties b/extensions/oidc-token-propagation-reactive/deployment/src/test/resources/application.properties
index 04051ed31aa29..d0459eade8177 100644
--- a/extensions/oidc-token-propagation-reactive/deployment/src/test/resources/application.properties
+++ b/extensions/oidc-token-propagation-reactive/deployment/src/test/resources/application.properties
@@ -1,6 +1,8 @@
quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=secret
+quarkus.oidc.application-type=hybrid
+quarkus.oidc.token.audience=any
quarkus.oidc-client.auth-server-url=${quarkus.oidc.auth-server-url}
quarkus.oidc-client.client-id=${quarkus.oidc.client-id}
diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/AbstractOidcAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/AbstractOidcAuthenticationMechanism.java
index d3cec873ebb47..000b9283f43b1 100644
--- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/AbstractOidcAuthenticationMechanism.java
+++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/AbstractOidcAuthenticationMechanism.java
@@ -1,5 +1,8 @@
package io.quarkus.oidc.runtime;
+import io.quarkus.oidc.AccessTokenCredential;
+import io.quarkus.oidc.IdTokenCredential;
+import io.quarkus.oidc.common.runtime.OidcConstants;
import io.quarkus.security.credential.TokenCredential;
import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
@@ -42,7 +45,12 @@ protected Uni authenticate(IdentityProviderManager identityPro
// during authentication TokenCredential is not accessible via CDI, thus we put it to the duplicated context
VertxContextSafetyToggle.validateContextIfExists(ERROR_MSG, ERROR_MSG);
final var ctx = Vertx.currentContext();
- ctx.putLocal(TokenCredential.class.getName(), token);
+ // If the primary token is ID token then the code flow access token is available as
+ // a RoutingContext `access_token` property.
+ final var tokenCredential = (token instanceof IdTokenCredential)
+ ? new AccessTokenCredential(context.get(OidcConstants.ACCESS_TOKEN_VALUE))
+ : token;
+ ctx.putLocal(TokenCredential.class.getName(), tokenCredential);
return identityProviderManager
.authenticate(HttpSecurityUtils.setRoutingContextAttribute(new TokenAuthenticationRequest(token), context))
.invoke(new Runnable() {
diff --git a/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java b/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java
index fbdc8faa8d49d..9f76443d13692 100644
--- a/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java
+++ b/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java
@@ -29,6 +29,7 @@
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import io.smallrye.jwt.build.Jwt;
+import io.smallrye.jwt.build.JwtClaimsBuilder;
/**
* Provides a mock OIDC server to tests.
@@ -42,6 +43,10 @@ public class OidcWiremockTestResource implements QuarkusTestResourceLifecycleMan
"https://server.example.com");
private static final String TOKEN_AUDIENCE = System.getProperty("quarkus.test.oidc.token.audience",
"https://server.example.com");
+ private static final String TOKEN_SUBJECT = "123456";
+ private static final String BEARER_TOKEN_TYPE = "Bearer";
+ private static final String ID_TOKEN_TYPE = "ID";
+
private static final String TOKEN_USER_ROLES = System.getProperty("quarkus.test.oidc.token.user-roles", "user");
private static final String TOKEN_ADMIN_ROLES = System.getProperty("quarkus.test.oidc.token.admin-roles", "user,admin");
private static final String ENCODED_X5C = "MIIC+zCCAeOgAwIBAgIGAXx/E9rgMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMTEwMTQxMzUzMDBaFw0yMjEwMTQxMzUzMDBaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIicN95dXlQLBqEZUsqPhQopnjnPgGmW80NohEgNzZLqN0xW9cyJJrdJM5Z1lRrePHZGiJdd1XXn4fYasP6/cjRfMWal9X6dD5wlnOTP01/4beX5vctE6W4lZrI3kTFmZ+I69w7BaLsUPWgV1CYrtuldL3dr6xAnngK3hU+JraB2Ndw9llXib26HOZhCXKedCTYcUQieVJGPI0f8H1JNk88+PnwI+cUGgXHF56iTLv9QujI6AhIgextXdd21T0XiHgBkSlSSBeqIKAjfCW6zoXP+PJU+Lso24J3duG3mrbilqHZlmIWnLRaG0RmKOeedXIDHvAaMaVUOLaN9HBgNKo0CAwEAAaNTMFEwHQYDVR0OBBYEFMYGoBNHBTMvMT4DwClVHVVwn+5VMB8GA1UdIwQYMBaAFMYGoBNHBTMvMT4DwClVHVVwn+5VMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFulB0DKhykXGbGPIBPcj63ItLNilgl1i8i43my8fYdV6OBWLIhZ4InhpX1+XmYCNPNtu94Jy1csS00K2/Hhn4ByBd+6nd5DSr0W0VdVQyhLz3GW1nf0J3X2N+tD818O0KtKKPTq4p9reg/XtV+DNv7DeDAGzlfgRL4E4fQx6OYeuu35kGrPvAddIA70leJMELJRylCLfEcl2ne/Bht8cZVp7ZCxnfXnsc+7hCW84mhzGjJycA3E6TnZPD3pD+q9FoIAQMxMQqUCH71u9vTvz1Q5JdokuJJY2eTHSUKyHA9MwSFq8DFDICJFBoQuFyDlK5yxSUcQpR3mBwKdimj6oA0=";
@@ -364,24 +369,33 @@ private Set getUserRoles() {
}
public static String getAccessToken(String userName, Set groups) {
- return generateJwtToken(userName, groups);
+ return generateJwtToken(userName, groups, TOKEN_SUBJECT, BEARER_TOKEN_TYPE);
}
public static String getIdToken(String userName, Set groups) {
- return generateJwtToken(userName, groups);
+ return generateJwtToken(userName, groups, TOKEN_SUBJECT, ID_TOKEN_TYPE);
}
public static String generateJwtToken(String userName, Set groups) {
- return generateJwtToken(userName, groups, "123456");
+ return generateJwtToken(userName, groups, TOKEN_SUBJECT);
}
public static String generateJwtToken(String userName, Set groups, String sub) {
- return Jwt.preferredUserName(userName)
+ return generateJwtToken(userName, groups, sub, null);
+ }
+
+ public static String generateJwtToken(String userName, Set groups, String sub, String type) {
+ JwtClaimsBuilder builder = Jwt.preferredUserName(userName)
.groups(groups)
.issuer(TOKEN_ISSUER)
.audience(TOKEN_AUDIENCE)
.claim("sid", "session-id")
- .subject(sub)
+ .subject(sub);
+ if (type != null) {
+ builder.claim("typ", type);
+ }
+
+ return builder
.jws()
.keyId("1")
.sign("privateKey.jwk");