Repository: quarkusio/quarkus
Issue: WWW-Authenticate Header missing

Given a protected method in a JAX-RS resource:

    @GET
    @Produces(MediaType.TEXT_HTML)
    @RolesAllowed("user-monitor")
    public String returnBootstrapHTML() {
...

and Elytron configured to use the embedded Auth:

    quarkus.security.embedded.enabled=true
    ...
    ...
    ...
    quarkus.security.embedded.auth-mechanism=BASIC

the curl request doesn't receive the correct header "WWW-Authenticate" that start the http challenge:

curl -v http://localhost:8080/gestione-ordini-bar
	*   Trying ::1...
	* TCP_NODELAY set
	* Connected to localhost (::1) port 8080 (#0)
	> GET /gestione-ordini-bar HTTP/1.1
	> Host: localhost:8080
	> User-Agent: curl/7.54.0
	> Accept: */*
	> 
	< HTTP/1.1 401 Unauthorized
	< Connection: keep-alive
	< Content-Type: text/html;charset=UTF-8
	< Content-Length: 14
	< Date: Fri, 05 Apr 2019 14:59:18 GMT
	< 
	* Connection #0 to host localhost left intact





Changed files:
extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
extensions/elytron-security/deployment/src/test/java/io/quarkus/security/test/BasicAuthTestCase.java
extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/RequestFailer.java

diff --git a/extensions/elytron-security/deployment/src/test/java/io/quarkus/security/test/BasicAuthTestCase.java b/extensions/elytron-security/deployment/src/test/java/io/quarkus/security/test/BasicAuthTestCase.java
index 4324f093db7..8cc8c2e98e2 100644
--- a/extensions/elytron-security/deployment/src/test/java/io/quarkus/security/test/BasicAuthTestCase.java
+++ b/extensions/elytron-security/deployment/src/test/java/io/quarkus/security/test/BasicAuthTestCase.java
@@ -1,5 +1,6 @@
 package io.quarkus.security.test;
 
+import static org.hamcrest.Matchers.containsStringIgnoringCase;
 import static org.hamcrest.Matchers.equalTo;
 
 import org.jboss.shrinkwrap.api.ShrinkWrap;
@@ -54,6 +55,7 @@ public void testSecureAccessSuccess() {
     @Test
     public void testJaxrsGetFailure() {
         RestAssured.when().get("/jaxrs-secured/rolesClass").then()
+                .header("www-authenticate", containsStringIgnoringCase("basic"))
                 .statusCode(401);
     }
 
diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/RequestFailer.java b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/RequestFailer.java
index 0cef204bceb..057331085f0 100644
--- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/RequestFailer.java
+++ b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/RequestFailer.java
@@ -1,16 +1,67 @@
 package io.quarkus.resteasy.runtime;
 
+import java.lang.reflect.Method;
+
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.core.Response;
 
+import org.jboss.resteasy.core.ResteasyContext;
+
+import io.quarkus.security.identity.CurrentIdentityAssociation;
+import io.quarkus.security.identity.SecurityIdentity;
+import io.quarkus.vertx.http.runtime.security.HttpAuthenticator;
+import io.vertx.ext.web.RoutingContext;
+
 /**
  * Helper class for JAXRS request failure responses.
  */
 public class RequestFailer {
 
+    //Servlet API may not be present
+    private static final Class<?> HTTP_SERVLET_REQUEST;
+    private static final Class<?> HTTP_SERVLET_RESPONSE;
+    private static final Method AUTHENTICATE;
+
+    static {
+        Class<?> httpServletReq = null;
+        Class<?> httpServletResp = null;
+        Method auth = null;
+        try {
+            httpServletReq = Class.forName("javax.servlet.http.HttpServletRequest");
+            httpServletResp = Class.forName("javax.servlet.http.HttpServletResponse");
+            auth = httpServletReq.getMethod("authenticate", httpServletResp);
+        } catch (Exception e) {
+
+        }
+        AUTHENTICATE = auth;
+        HTTP_SERVLET_REQUEST = httpServletReq;
+        HTTP_SERVLET_RESPONSE = httpServletResp;
+    }
+
     public static void fail(ContainerRequestContext requestContext) {
         if (requestContext.getSecurityContext().getUserPrincipal() == null) {
-            respond(requestContext, 401, "Not authorized");
+
+            SecurityIdentity identity = CurrentIdentityAssociation.current();
+            if (HTTP_SERVLET_REQUEST != null) {
+                Object httpServletRequest = ResteasyContext.getContextData(HTTP_SERVLET_REQUEST);
+                if (httpServletRequest != null) {
+                    Object httpServletResponse = ResteasyContext.getContextData(HTTP_SERVLET_RESPONSE);
+                    try {
+                        AUTHENTICATE.invoke(httpServletRequest, httpServletResponse);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                    return;
+                }
+            }
+            HttpAuthenticator authenticator = identity.getAttribute(HttpAuthenticator.class.getName());
+            RoutingContext context = ResteasyContext.getContextData(RoutingContext.class);
+            if (authenticator != null && context != null) {
+                authenticator.sendChallenge(context, null);
+            } else {
+                respond(requestContext, 401, "Not authorized");
+            }
+
         } else {
             respond(requestContext, 403, "Access forbidden: role not allowed");
         }
@@ -21,6 +72,7 @@ private static void respond(ContainerRequestContext context, int status, String
                 .entity(message)
                 .type("text/html;charset=UTF-8")
                 .build();
+
         context.abortWith(response);
     }
 }
diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
index b3549900e27..e7bd300e68e 100644
--- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
+++ b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java
@@ -75,14 +75,15 @@ private void dispatchRequestContext(RoutingContext request, InputStream is, Vert
             association.setIdentity(user.getSecurityIdentity());
         }
         try {
-            dispatch(request.request(), is, output);
+            dispatch(request, is, output);
         } finally {
             requestContext.terminate();
         }
     }
 
-    private void dispatch(HttpServerRequest request, InputStream is, VertxOutput output) {
+    private void dispatch(RoutingContext routingContext, InputStream is, VertxOutput output) {
         Context ctx = vertx.getOrCreateContext();
+        HttpServerRequest request = routingContext.request();
         ResteasyUriInfo uriInfo = VertxUtil.extractUriInfo(request, servletMappingPrefix);
         ResteasyHttpHeaders headers = VertxUtil.extractHttpHeaders(request);
         HttpServerResponse response = request.response();
@@ -93,6 +94,7 @@ private void dispatch(HttpServerRequest request, InputStream is, VertxOutput out
         vertxRequest.setInputStream(is);
         try {
             ResteasyContext.pushContext(SecurityContext.class, new QuarkusResteasySecurityContext(request));
+            ResteasyContext.pushContext(RoutingContext.class, routingContext);
             dispatcher.service(ctx, request, response, vertxRequest, vertxResponse, true);
         } catch (Failure e1) {
             vertxResponse.setStatus(e1.getErrorCode());