77import io .quarkus .arc .Arc ;
88import io .quarkus .arc .ManagedContext ;
99import io .quarkus .security .identity .CurrentIdentityAssociation ;
10+ import io .quarkus .vertx .http .runtime .security .ChallengeData ;
1011import io .quarkus .vertx .http .runtime .security .HttpAuthenticator ;
12+ import io .vertx .core .Context ;
1113import io .vertx .ext .web .RoutingContext ;
1214
1315/**
4850 * myAuthenticatedMethod(); // Has @Authenticated annotation
4951 * });
5052 * } catch (UnauthorizedException | ForbiddenException e) {
51- * VertxSecurityHelper .handleAuthError(ctx, e);
53+ * securityHelper .handleAuthError(ctx, e);
5254 * } catch (Exception e) {
5355 * VertxSecurityHelper.handleGenericError(ctx);
5456 * }
@@ -68,9 +70,8 @@ public final class VertxSecurityHelper {
6870 @ Inject
6971 Instance <CurrentIdentityAssociation > currentIdentityAssociation ;
7072
71-
7273 public VertxSecurityHelper () {
73- // Utility class - no instantiation
74+ // CDI-managed constructor
7475 }
7576
7677 /**
@@ -98,6 +99,10 @@ public VertxSecurityHelper() {
9899 * @throws RuntimeException if the task throws an exception
99100 */
100101 public void runInRequestContext (RoutingContext ctx , Runnable task ) {
102+ if (Context .isOnEventLoopThread ()) {
103+ throw new IllegalStateException (
104+ "Cannot perform blocking authentication on event loop thread. Use blockingHandler()." );
105+ }
101106 ManagedContext requestContext = Arc .container ().requestContext ();
102107 boolean wasActive = requestContext .isActive ();
103108 if (!wasActive ) {
@@ -124,22 +129,31 @@ public void runInRequestContext(RoutingContext ctx, Runnable task) {
124129 *
125130 * <ul>
126131 * <li>{@code ForbiddenException} → HTTP 403 Forbidden</li>
127- * <li>All other auth errors → HTTP 401 Unauthorized with {@code WWW-Authenticate: Basic} header</li>
132+ * <li>All other auth errors → delegates to {@link HttpAuthenticator#getChallenge} to obtain
133+ * the correct {@code WWW-Authenticate} header for the configured auth mechanism
134+ * (Basic, Bearer, etc.) and sends HTTP 401 with the challenge header</li>
128135 * </ul>
129136 *
130137 * @param ctx the routing context
131138 * @param e the authentication or authorization exception
132139 */
133- public static void handleAuthError (RoutingContext ctx , Exception e ) {
140+ public void handleAuthError (RoutingContext ctx , Exception e ) {
134141 if (!ctx .response ().ended ()) {
135142 if (e instanceof io .quarkus .security .ForbiddenException ) {
136143 ctx .response ()
137144 .setStatusCode (403 )
138145 .end ();
139146 } else {
147+ int status = 401 ;
148+ if (!httpAuthenticator .isUnsatisfied ()) {
149+ ChallengeData challenge = httpAuthenticator .get ().getChallenge (ctx ).await ().indefinitely ();
150+ if (challenge != null ) {
151+ status = challenge .status ;
152+ ctx .response ().putHeader (challenge .headerName , challenge .headerContent );
153+ }
154+ }
140155 ctx .response ()
141- .setStatusCode (401 )
142- .putHeader ("WWW-Authenticate" , "Basic realm=\" Quarkus\" " )
156+ .setStatusCode (status )
143157 .end ();
144158 }
145159 }
0 commit comments