2

We've got a JQuery web app and a JAX-RS-based backend running on Wildfly 8.

The application uses Basic authentication with a custom DatabaseServerLoginModule, which in turn implements PBKDF2 for password hashing. Each request is authenticated in this way.

We've discovered that on some browsers and on some occasions (haven't quite figured out which), the 401 returned by Wildfly upon failed authentication triggers the browser's native login popup to show up, rather than to let our JQuery app handle the status code. Needless to say, this doesn't make for a very nice user experience.

I'm looking for a way to avoid this behaviour, and it seems the general solution seems to be either to return a different status code, or to skip the www-authenticate header. The problem is that I can't for the life of me figure out how to customize this behaviour in Wildfly, and in general the whole area seems to be rather poorly documented. Does anyone know of a simple way of doing it? Or any way of doing it? Can you configure Wildfly or override the class (which one?) that returns 401 upon a failed login?

I found this forum post which deals with the same problem, but it uses PicketLink which I haven't been able to get up and running, and either way it doesn't exactly strike me as a "simple" solution for what I would define as a trivial problem.

Any help would be greatly appreciated.

Koeus
  • 434
  • 1
  • 6
  • 22

2 Answers2

2

We found a solution to this problem. We had to create our own Authentication mecanism (we named it "CUSTOM-BASIC") by extending the existing BASIC authentication mecanism.

To do that:

Extend the class io.undertow.security.impl.BasicAuthenticationMechanism and override the sendChallenge method like this:

public class CustomBasicAuthenticationMecanism extends BasicAuthenticationMechanism {

    public CustomBasicAuthenticationMecanism(final String realmName, final String mechanismName, final boolean silent, final IdentityManager identityManager) {
       super(realmName, mechanismName, silent, identityManager);
    }

    @Override
    public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
        //Commented to remove the www-authenticate header which makes the login popup show up in the browser
        //exchange.getResponseHeaders().add(WWW_AUTHENTICATE, challenge); 
        return new ChallengeResult(true, StatusCodes.UNAUTHORIZED);
    }

    public static class Factory implements AuthenticationMechanismFactory {

        private final IdentityManager identityManager;

        public Factory(IdentityManager identityManager) {
            this.identityManager = identityManager;
        }

        @Override
        public AuthenticationMechanism create(String mechanismName, FormParserFactory formParserFactory, Map<String, String> properties) {
            String realm = properties.get(REALM);
            return new CustomBasicAuthenticationMecanism(realm, mechanismName, false, identityManager);
        }
    }
}

Then you need to "register" your custom authentication mecanism by implementing io.undertow.servlet.ServletExtension:

public class CustomBasicAuthenticationServletExtension implements ServletExtension {
    @Override
    public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
        deploymentInfo.addAuthenticationMechanism("CUSTOM-BASIC", new CustomBasicAuthenticationMecanism.Factory(deploymentInfo.getIdentityManager()));
    }
}

and create a file named io.undertow.servlet.ServletExtension in META-INF/services. The file should contain the fully qualified name of your ServletExtension. Ex:

com.company.authentication.CustomBasicAuthenticationServletExtension

Then in your web.xml you can replace BASIC with you custom authentication mecanism name (CUSTOM-BASIC here):

<login-config>
    <auth-method>CUSTOM-BASIC</auth-method>
    ...
</login-config>

By doing this you will still get a 401 response but without the WWW-Authenticate header.

If for any reason you need another response code (let's say 403), just change this line:

return new ChallengeResult(true, StatusCodes.FORBIDDEN); 
Olivier Masseau
  • 778
  • 7
  • 23
  • This is a great solution if you want programmatic control over the http handler chain. I wish I had more upvotes – aelgn Oct 16 '18 at 16:59
0

If you use:

<auth-method>BASIC?silent=true</auth-method>

then wildfly will never send the www-authenticate header, and you should get a 403 instead of a 401.

This can also be combined with form auth:

<auth-method>BASIC?silent=true,FORM</auth-method>

and you can then map the FORM login page to a Servlet that returns whatever you want.

Stuart Douglas
  • 847
  • 5
  • 4
  • I've made the change to web.xml and it deploys fine, but I still seem to get 401's. Does it require a very recent version of Wildfly or am I missing something? – Koeus May 11 '15 at 08:45
  • Wildfly 9.0.2.Final: Setting silent to true does not prevent having a www-authenticate header in the response and a 401 is still returned – Olivier Masseau Jul 26 '17 at 15:26
  • I get a 401 but it's silent as requested with Jboss 7 (Wildfly 10 I think) ! Definitely simpler than programmatically – user1075613 Jul 10 '18 at 15:55
  • From the Wilfly Documentation: "Basic authentication in silent mode will send challenge to authenticate only if the request contained authorization header, otherwise it is assumed another method will send the challenge." => This means there is still a case when Wildfly will still send a www-authenticate header, even in silent mode ... (see: https://docs.wildfly.org/18/WildFly_Elytron_Security.html) – jbandi Mar 31 '20 at 14:56