Skip to content

Commit

Permalink
Merge pull request #38842 from michalvavrik/feature/custom-auth-mech-…
Browse files Browse the repository at this point in the history
…do-not-req-identity-provider

Do not require IdentityProviders for HTTP Authentication mechanisms without credential types and fail if the required provider is missing
  • Loading branch information
sberyozkin authored Feb 19, 2024
2 parents 58408af + 65f47c0 commit 4d2610b
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 8 deletions.
6 changes: 6 additions & 0 deletions docs/src/main/asciidoc/security-customization.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism
}
----

TIP: The `HttpAuthenticationMechanism` should transform incoming HTTP request with suitable authentication credentials
into an `io.quarkus.security.identity.request.AuthenticationRequest` instance and delegate the authentication to the `io.quarkus.security.identity.IdentityProviderManager`.
Leaving authentication to the `io.quarkus.security.identity.IdentityProvider`s gives you more options for credentials verifications,
as well as convenient way to perform blocking tasks.
Nevertheless, the `io.quarkus.security.identity.IdentityProvider` can be omitted and the `HttpAuthenticationMechanism` is free to authenticate request on its own in trivial use cases.

[[dealing-with-more-than-one-http-auth-mechanisms]]
== Dealing with more than one HttpAuthenticationMechanism

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
public class DisabledProactiveSecIdentityProviderTest {

private static final String APP_PROPS = "" +
"quarkus.http.auth.basic=true\n" +
"quarkus.http.auth.proactive=false\n" +
"quarkus.http.auth.policy.r1.roles-allowed=admin\n" +
"quarkus.http.auth.permission.roles1.paths=/admin\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Set;
import java.util.function.Function;

import io.quarkus.security.identity.IdentityProvider;
import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.AuthenticationRequest;
Expand All @@ -21,10 +22,17 @@ public interface HttpAuthenticationMechanism {
Uni<ChallengeData> getChallenge(RoutingContext context);

/**
* Returns the required credential types. If there are no identity managers installed that support the
* listed types then this mechanism will not be enabled.
* If this mechanism delegates authentication to the {@link IdentityProviderManager} using the
* {@link IdentityProviderManager#authenticate(AuthenticationRequest)} call, then the mechanism must provide
* supported {@link AuthenticationRequest} request types. It allows Quarkus to validate that one or more
* {@link IdentityProvider} providers with matching supported {@link IdentityProvider#getRequestType()} request
* types exist and fail otherwise.
*
* @return required credential types
*/
Set<Class<? extends AuthenticationRequest>> getCredentialTypes();
default Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
return Set.of();
}

default Uni<Boolean> sendChallenge(RoutingContext context) {
return getChallenge(context).map(new ChallengeSender(context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.quarkus.security.spi.runtime.AuthenticationFailureEvent;
import io.quarkus.security.spi.runtime.AuthenticationSuccessEvent;
import io.quarkus.security.spi.runtime.SecurityEventHelper;
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

Expand All @@ -47,7 +48,7 @@ public class HttpAuthenticator {
public HttpAuthenticator(IdentityProviderManager identityProviderManager,
Event<AuthenticationFailureEvent> authFailureEvent,
Event<AuthenticationSuccessEvent> authSuccessEvent,
BeanManager beanManager,
BeanManager beanManager, HttpBuildTimeConfig httpBuildTimeConfig,
Instance<HttpAuthenticationMechanism> httpAuthenticationMechanism,
Instance<IdentityProvider<?>> providers,
@ConfigProperty(name = "quarkus.security.events.enabled") boolean securityEventsEnabled) {
Expand All @@ -56,6 +57,15 @@ public HttpAuthenticator(IdentityProviderManager identityProviderManager,
this.identityProviderManager = identityProviderManager;
List<HttpAuthenticationMechanism> mechanisms = new ArrayList<>();
for (HttpAuthenticationMechanism mechanism : httpAuthenticationMechanism) {
if (mechanism.getCredentialTypes().isEmpty()) {
// mechanism does not require any IdentityProvider
log.debugf("HttpAuthenticationMechanism '%s' provided no required credential types, therefore it needs "
+ "to be able to perform authentication without any IdentityProvider", mechanism.getClass().getName());
mechanisms.add(mechanism);
continue;
}

// mechanism requires an IdentityProvider, therefore we verify that such a provider exists
boolean found = false;
for (Class<? extends AuthenticationRequest> mechType : mechanism.getCredentialTypes()) {
for (IdentityProvider<?> i : providers) {
Expand All @@ -68,10 +78,22 @@ public HttpAuthenticator(IdentityProviderManager identityProviderManager,
break;
}
}
// Add mechanism if there is a provider with matching credential type
// If the mechanism has no credential types, just add it anyway
if (found || mechanism.getCredentialTypes().isEmpty()) {
if (found) {
mechanisms.add(mechanism);
} else if (BasicAuthenticationMechanism.class.equals(mechanism.getClass())
&& httpBuildTimeConfig.auth.basic.isEmpty()) {
log.debug("""
BasicAuthenticationMechanism has been enabled because no other authentication mechanism has been
detected, but there is no IdentityProvider based on username and password. Please use
one of supported extensions if you plan to use the mechanism.
For more information go to the https://quarkus.io/guides/security-basic-authentication-howto.
""");
} else {
throw new RuntimeException("""
HttpAuthenticationMechanism '%s' requires one or more IdentityProviders supporting at least one
of the following credentials types: %s.
Please refer to the https://quarkus.io/guides/security-identity-providers for more information.
""".formatted(mechanism.getClass().getName(), mechanism.getCredentialTypes()));
}
}
if (mechanisms.isEmpty()) {
Expand Down

0 comments on commit 4d2610b

Please sign in to comment.