diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java index 26a97a7a8b64d8..742586d14dba1f 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java @@ -21,10 +21,13 @@ @ApplicationScoped public class OidcAuthenticationMechanism implements HttpAuthenticationMechanism { + private static HttpCredentialTransport OIDC_SERVICE_TRANSPORT = new HttpCredentialTransport( + HttpCredentialTransport.Type.AUTHORIZATION, OidcConstants.BEARER_SCHEME); + private static HttpCredentialTransport OIDC_WEB_APP_TRANSPORT = new HttpCredentialTransport( + HttpCredentialTransport.Type.AUTHORIZATION_CODE, OidcConstants.CODE_FLOW_CODE); private final BearerAuthenticationMechanism bearerAuth = new BearerAuthenticationMechanism(); private final CodeAuthenticationMechanism codeAuth = new CodeAuthenticationMechanism(); - private final DefaultTenantConfigResolver resolver; public OidcAuthenticationMechanism(DefaultTenantConfigResolver resolver) { @@ -89,10 +92,18 @@ public Set> getCredentialTypes() { } @Override - public HttpCredentialTransport getCredentialTransport() { - //not 100% correct, but enough for now - //if OIDC is present we don't really want another bearer mechanism - return new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, OidcConstants.BEARER_SCHEME); + public Uni getCredentialTransport(RoutingContext context) { + setTenantIdAttribute(context); + return resolve(context).onItem().transform(new Function() { + @Override + public HttpCredentialTransport apply(OidcTenantConfig oidcTenantConfig) { + if (!oidcTenantConfig.tenantEnabled) { + return null; + } + return isWebApp(context, oidcTenantConfig) ? OIDC_SERVICE_TRANSPORT + : OIDC_WEB_APP_TRANSPORT; + } + }); } private static void setTenantIdAttribute(RoutingContext context) { diff --git a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java index e59fe2486b337e..15a58be2a5b28d 100644 --- a/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java +++ b/extensions/smallrye-jwt/runtime/src/main/java/io/quarkus/smallrye/jwt/runtime/auth/JWTAuthMechanism.java @@ -93,7 +93,7 @@ public Set> getCredentialTypes() { } @Override - public HttpCredentialTransport getCredentialTransport() { + public Uni getCredentialTransport(RoutingContext context) { final String tokenHeaderName = authContextInfo.getTokenHeader(); if (COOKIE_HEADER.equals(tokenHeaderName)) { String tokenCookieName = authContextInfo.getTokenCookie(); @@ -101,11 +101,12 @@ public HttpCredentialTransport getCredentialTransport() { if (tokenCookieName == null) { tokenCookieName = BEARER; } - return new HttpCredentialTransport(HttpCredentialTransport.Type.COOKIE, tokenCookieName); + return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.COOKIE, tokenCookieName)); } else if (AUTHORIZATION_HEADER.equals(tokenHeaderName)) { - return new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BEARER); + return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BEARER)); } else { - return new HttpCredentialTransport(HttpCredentialTransport.Type.OTHER_HEADER, tokenHeaderName); + return Uni.createFrom() + .item(new HttpCredentialTransport(HttpCredentialTransport.Type.OTHER_HEADER, tokenHeaderName)); } } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/BasicAuthenticationMechanism.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/BasicAuthenticationMechanism.java index 11e6941f4a76a3..257c7d61e52267 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/BasicAuthenticationMechanism.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/BasicAuthenticationMechanism.java @@ -192,8 +192,8 @@ public Set> getCredentialTypes() { } @Override - public HttpCredentialTransport getCredentialTransport() { - return new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BASIC); + public Uni getCredentialTransport(RoutingContext context) { + return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BASIC)); } @Override diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/FormAuthenticationMechanism.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/FormAuthenticationMechanism.java index e69482a8f6693d..7fe665c992ed6d 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/FormAuthenticationMechanism.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/FormAuthenticationMechanism.java @@ -194,7 +194,7 @@ public Set> getCredentialTypes() { } @Override - public HttpCredentialTransport getCredentialTransport() { - return new HttpCredentialTransport(HttpCredentialTransport.Type.POST, postLocation, FORM); + public Uni getCredentialTransport(RoutingContext context) { + return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.POST, postLocation, FORM)); } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java index 7ab3f4d4f1ee35..e77542b83ea209 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticationMechanism.java @@ -31,11 +31,26 @@ default Uni sendChallenge(RoutingContext context) { } /** - * The credential transport, used to make sure multiple incompatible mechanisms are not installed + * The credential transport, used for finding the best candidate for authenticating and challenging when more than one + * mechanism is installed. + * and finding the best candidate for issuing a challenge when more than one mechanism is installed. * * May be null if this mechanism cannot interfere with other mechanisms */ - HttpCredentialTransport getCredentialTransport(); + @Deprecated + default HttpCredentialTransport getCredentialTransport() { + throw new UnsupportedOperationException(); + } + + /** + * The credential transport, used for finding the best candidate for authenticating and challenging when more than one + * mechanism is installed. + * + * May be null if this mechanism cannot interfere with other mechanisms + */ + default Uni getCredentialTransport(RoutingContext context) { + throw new UnsupportedOperationException(); + } class ChallengeSender implements Function { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java index 73c252b4f5969e..bfaaaa7e32eeb5 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthenticator.java @@ -3,9 +3,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -74,21 +72,6 @@ public int compare(HttpAuthenticationMechanism mech1, HttpAuthenticationMechanis } }); this.mechanisms = mechanisms.toArray(new HttpAuthenticationMechanism[mechanisms.size()]); - //validate that we don't have multiple incompatible mechanisms - Map map = new HashMap<>(); - for (HttpAuthenticationMechanism i : mechanisms) { - HttpCredentialTransport credentialTransport = i.getCredentialTransport(); - if (credentialTransport == null) { - continue; - } - HttpAuthenticationMechanism existing = map.get(credentialTransport); - if (existing != null) { - throw new RuntimeException("Multiple mechanisms present that use the same credential transport " - + credentialTransport + ". Mechanisms are " + i + " and " + existing); - } - map.put(credentialTransport, i); - } - } } @@ -111,14 +94,25 @@ public Uni attemptAuthentication(RoutingContext routingContext String pathSpecificMechanism = pathMatchingPolicy.isResolvable() ? pathMatchingPolicy.get().getAuthMechanismName(routingContext) : null; - HttpAuthenticationMechanism matchingMech = findBestCandidateMechanism(routingContext, pathSpecificMechanism); - if (matchingMech != null) { - routingContext.put(HttpAuthenticationMechanism.class.getName(), matchingMech); - return matchingMech.authenticate(routingContext, identityProviderManager); - } else if (pathSpecificMechanism != null) { - return Uni.createFrom().optional(Optional.empty()); - } + Uni mechUni = findBestCandidateMechanism(routingContext, pathSpecificMechanism); + return mechUni.onItem().transformToUni(new Function>() { + + @Override + public Uni apply(HttpAuthenticationMechanism mech) { + if (mech != null) { + routingContext.put(HttpAuthenticationMechanism.class.getName(), mech); + return mech.authenticate(routingContext, identityProviderManager); + } else if (pathSpecificMechanism != null) { + return Uni.createFrom().optional(Optional.empty()); + } + return createSecurityIdentity(routingContext); + } + + }); + } + + private Uni createSecurityIdentity(RoutingContext routingContext) { Uni result = mechanisms[0].authenticate(routingContext, identityProviderManager); for (int i = 1; i < mechanisms.length; ++i) { HttpAuthenticationMechanism mech = mechanisms[i]; @@ -132,7 +126,6 @@ public Uni apply(SecurityIdentity data) { } }); } - return result; } @@ -199,30 +192,69 @@ public Uni apply(ChallengeData data) { return result; } - private HttpAuthenticationMechanism findBestCandidateMechanism(RoutingContext routingContext, + private Uni findBestCandidateMechanism(RoutingContext routingContext, String pathSpecificMechanism) { if (pathSpecificMechanism != null) { - for (int i = 0; i < mechanisms.length; ++i) { - HttpCredentialTransport credType = mechanisms[i].getCredentialTransport(); - if (credType != null && credType.getAuthenticationScheme().equalsIgnoreCase(pathSpecificMechanism)) { - return mechanisms[i]; - } - } + return getPathSpecificMechanism(0, routingContext, pathSpecificMechanism); } else { String authScheme = getAuthorizationScheme(routingContext); if (authScheme != null) { - for (int i = 0; i < mechanisms.length; ++i) { - HttpCredentialTransport credType = mechanisms[i].getCredentialTransport(); - if (credType != null && credType.getTransportType() == Type.AUTHORIZATION - && credType.getTypeTarget().toLowerCase().startsWith(authScheme.toLowerCase())) { - return mechanisms[i]; - } - } + return getAuthorizationSchemeMechanism(0, routingContext, authScheme); } } return null; } + private Uni getPathSpecificMechanism(int index, RoutingContext routingContext, + String pathSpecificMechanism) { + if (index == mechanisms.length) { + return Uni.createFrom().nullItem(); + } + Uni credTypeUni = getCredentialTransport(mechanisms[index], routingContext); + return credTypeUni.onItem() + .transformToUni(new Function>() { + + @Override + public Uni apply(HttpCredentialTransport t) { + if (t != null && t.getAuthenticationScheme().equalsIgnoreCase(pathSpecificMechanism)) { + return Uni.createFrom().item(mechanisms[index]); + } + return getPathSpecificMechanism(index + 1, routingContext, pathSpecificMechanism); + } + + }); + } + + private Uni getAuthorizationSchemeMechanism(int index, RoutingContext routingContext, + String authScheme) { + if (index == mechanisms.length) { + return Uni.createFrom().nullItem(); + } + Uni credTypeUni = getCredentialTransport(mechanisms[index], routingContext); + return credTypeUni.onItem() + .transformToUni(new Function>() { + + @Override + public Uni apply(HttpCredentialTransport t) { + if (t != null && t.getTransportType() == Type.AUTHORIZATION + && t.getTypeTarget().toLowerCase().startsWith(authScheme.toLowerCase())) { + return Uni.createFrom().item(mechanisms[index]); + } + return getAuthorizationSchemeMechanism(index + 1, routingContext, authScheme); + } + + }); + } + + private static Uni getCredentialTransport(HttpAuthenticationMechanism mechanism, + RoutingContext routingContext) { + try { + return mechanism.getCredentialTransport(routingContext); + } catch (UnsupportedOperationException ex) { + return Uni.createFrom().item(mechanism.getCredentialTransport()); + } + } + private static String getAuthorizationScheme(RoutingContext routingContext) { String authorization = routingContext.request().getHeader(HttpHeaders.AUTHORIZATION); if (authorization != null) { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpCredentialTransport.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpCredentialTransport.java index cd3ba29798c9a0..ebd9c4b46f0468 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpCredentialTransport.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpCredentialTransport.java @@ -9,8 +9,9 @@ * Authorization header * POST * - * It is not permitted for multiple HTTP authentication mechanisms to use the same credential - * transport type, as they will not be able to figure out which mechanisms should process which + * Not that using multiple HTTP authentication mechanisms to use the same credential + * transport type can lead to unexpected authentication failures as they will not be able to figure out which mechanisms should + * process which * request. */ public class HttpCredentialTransport { @@ -49,7 +50,11 @@ public enum Type { /** * X509 */ - X509 + X509, + /** + * Authorizatiob code, type target is the query 'code' parameter + */ + AUTHORIZATION_CODE } @Override diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java index 5fd96c7c83a77c..9c0ab1be8300d4 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/MtlsAuthenticationMechanism.java @@ -73,7 +73,7 @@ public Set> getCredentialTypes() { } @Override - public HttpCredentialTransport getCredentialTransport() { - return new HttpCredentialTransport(HttpCredentialTransport.Type.X509, "X509"); + public Uni getCredentialTransport(RoutingContext context) { + return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.X509, "X509")); } }