2

In Restlet 2.3 I am using a ChallengeAuthenticator with ChallengeScheme.HTTP_BASIC to protect application resources. When the server receives an incorrect set of credentials the server correctly returns a 401 Unauthorized response. Also correctly it adds the following header:

WWW-Authenticate → Basic realm="My security Realm"

The problem is when that response goes back to a browser rather than a server (as is the case with the AngularJS application GUI), the browser natively interprets that 401 response and launches an 'Authentication Required' modal.

enter image description here

What I would like to try and achieve is to read the request headers (easily done) and if the X-Requested-With: XMLHttpRequest header is present I would like to suppress the WWW-Authenticate header in the '401' response.

Currently the WWW-Authenticate header is automatically set so my question is how can I override this default header being set and handle it manually?

tarka
  • 4,623
  • 8
  • 41
  • 71

2 Answers2

2

In your case, you should use a filter to remove the header WWW-Authenticate from the response. This header corresponds to a challenge request in the response.

Here is the content of the filter:

public class SecurityPostProcessingFilter extends Filter {
    public SecurityPostProcessingFilter(
              Context context, Restlet next) {
        super(context, next);
    }

    @Override
    protected void afterHandle(Request request, Response response) {
        String requestedWith
           = request.getHeaders().getFirstValue("X-Requested-With");
        if ("XMLHttpRequest".equals(requestedWith)) {
            response.getChallengeRequests().clear();
        }
    }
}

You need to add it within the createInboundRoot method of your Restlet application, as described below

public class RestletApplication extends Application {
    (...)

    @Override
    public Restlet createInboundRoot() {
        Router router = new Router(getContext());
        (...)

        ChallengeAuthenticator guard = new ChallengeAuthenticator(
                null, ChallengeScheme.HTTP_BASIC, "testRealm");
        (...)
        guard.setNext(router);

        Filter filter = new SecurityPostProcessingFilter(
             getContext(), guard);

        return filter;
    }
}

This will remove the header WWW-Authenticate from the response when the value of the header X-Requested-From is equals to XMLHttpRequest in the request.

FYI, there is a page on the Restlet web site that describes the mapping between HTTP headers and the Restlet API: http://restlet.com/technical-resources/restlet-framework/guide/2.2/core/http-headers-mapping.

Hope it helps you, Thierry

wu-lee
  • 567
  • 3
  • 12
Thierry Templier
  • 182,931
  • 35
  • 372
  • 339
  • Thanks Thierry, I didn't realise that the filter `afterHandle(Request, Response)` in the `createInboundRoot()` would handle the response after the request had been fulfilled. The way I interpreted the documentation was that `beforeHandle` and `afterHandle` where executed either side of the filters handling. i.e. I thought the order was `beforeHandle() -> handle() -> afterHandle() -> accessResource -> return response` – tarka Oct 09 '15 at 07:50
  • You're welcome! No in fact, it's: beforeHandle -> next.handle() -> afterHandle ;-) – Thierry Templier Oct 09 '15 at 07:51
0

Another way is to override the ChallengeAuthenticator#challenge method. By default it set the response status and add a challengeRequest:

    ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(), ChallengeScheme.HTTP_BASIC, "realm") {
        public void challenge(org.restlet.Response response, boolean stale) {
            String requestedFrom = response.getRequest().getHeaders().getFirstValue("X-Requested-With");
            if (!"XMLHttpRequest".equals(requestedFrom)) {
                super.challenge(response, stale);
            } else {
                response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
            }

        };
    };
Thierry Boileau
  • 856
  • 5
  • 8