Search code examples
angularjsauthenticationcorsrestlet

Restlet CorsFilter with ChallengeAuthenticator


I'm building a RESTful API with the Restlet framework and need it to work with cross domain calls (CORS) as well as basic authentication.

At the moment I'm using the CorsFilter which does the job of making my webservice support CORS requests. But, when I try to use this with a simple ChallengeAuthenticator with HTTP Basic Authentication it won't work as I want it to (from a web site).

When I access the webservice directly via Chrome it works as intended, but when I try it in a small web application written in angularjs (jquery/javascript) and try to access the webservice it does not.

Basically what happens is that when a OPTIONS request is sent to my webservice it will not respond with the headers: 'Access-Control-Allow-Origin', 'Access-Control-Allow-Credentials', etc. as it should. Instead it is sending a respond with HTTP status code 401 saying that the authentication failed.. Is this because the authenticator is overriding the CorsFilter somehow?

My createInboundRoot method can be seen below.

@Override
        public Restlet createInboundRoot() {
            ChallengeAuthenticator authenticator = createAuthenticator();
            RoleAuthorizer authorizer = createRoleAuthorizer();
            Router router = new Router(getContext());
            router.attach("/items", ItemsServerResource.class);
            router.attach("/items/", ItemsServerResource.class);
            Router baseRouter = new Router(getContext());

            authorizer.setNext(ItemServerResource.class);
            authenticator.setNext(baseRouter);

            baseRouter.attach("/items/{itemID}", authorizer);
            baseRouter.attach("", router);
            // router.attach("/items/{itemID}", ItemServerResource.class);

            CorsFilter corsFilter = new CorsFilter(getContext());
            corsFilter.setNext(authenticator);
            corsFilter.setAllowedOrigins(new HashSet(Arrays.asList("*")));
            corsFilter.setAllowedCredentials(true);
            return corsFilter;
        }

(The authorizer and authenticator code is taken from the "official" restlet guide for authorization and authentication)

I've tried alot of changes to my code but none which given me any luck. But I noticed that when setting the argument "optional" in ChallengeAuthenticator to true (which "Indicates if the authentication success is optional") the CorsFilter does its job, but obviously the ChallengeAuthenticator does not care about authenticating the client and lets anything use the protected resources..

Has anyone had a similar problem? Or have you solved this (CORS + Authentication in Restlet) in any other way?

Thanks in advance!


Solution

  • I think that it's a bug of the Restlet CORS filter. As a matter of fact, the filter uses the method afterHandle to set the CORS headers. See the source code: https://github.com/restlet/restlet-framework-java/blob/4e8f0414b4f5ea733fcc30dd19944fd1e104bf74/modules/org.restlet/src/org/restlet/engine/application/CorsFilter.java#L119.

    This means that the CORS processing is done after executing the whole processing chain (authentication, ...). So if your authentication failed, you will have a status code 401. It's actually the case since CORS preflighted requests don't send authentication hints.

    For more details about using CORS with Restlet, you could have a look at this link: https://templth.wordpress.com/2014/11/12/understanding-and-using-cors/. This can provide you a workaround until this bug was fixed in Restlet itself.

    I opened an issue in Github for your problem: https://github.com/restlet/restlet-framework-java/issues/1019.

    Hope it helps, Thierry