Expand my Community achievements bar.

Don’t miss the AEM Skill Exchange in SF on Nov 14—hear from industry leaders, learn best practices, and enhance your AEM strategy with practical tips.

Unable to access libs/granite/csrf/token.json from an external application which is not in AEM

Avatar

Level 4

We have a scenario where we need to provide our AEM application header and footer to an external application (this is not in AEM). As we know with AEM 6.1 we need to include <cq:includeClientLib categories="granite.csrf.standalone"/> in order to make all the Form submissions or POST requests working. In our header we have a search bar which is embeded in a form tag. When we enter text and click on search icon it submits the form as a POST request. This works absolutely fine within AEM application. This particular scenario is not working when the AEM application header is consumed by an external application. Its throwing an error saying csrf.js?_=1468868152688:228 XMLHttpRequest cannot load   AEMApplicationdomain/libs/granite/csrf/token.json. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.

What I understood from the above error is since it is an external Application which is not in AEM, it might be throwing the CORS(Cross origin request) error. To further analyze I have gone through the following code in /etc/clientlibs/granite/jquery/granite/csrf/source/csrf.js file:

var FIELD_NAME = ':cq_csrf_token',
        HEADER_NAME = 'CSRF-Token',
        TOKEN_SERVLET = '/libs/granite/csrf/token.json';

    var promise, globalToken;

    function getToken() {
        promise = new Promise();

        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                try {
                    var data = JSON.parse(xhr.responseText);
                    globalToken = data.token;
                    promise.resolve(globalToken);
                } catch (ex) {
                    promise.reject(xhr.responseText);
                }
            }
        };
        xhr.open('GET', Granite.HTTP.externalize(TOKEN_SERVLET), true);
        xhr.send();
    }

It is taking the TOKEN_SERVLET path and making an ajax call to a servlet in backend to get the random token ID. Since the TOKEN_SERVLET is a relative url, I have changed it to absolute url by adding AEM application domain to it. In this way when an external application is trying to submit the form which is in AEM, the TOKEN_SERVLET won't end up taking the domain of the external application (HOST) which is not an AEM application. However still I am getting the CORS error saying it cannot access it. 

So I assume, though I have changed the TOKEN_SERVLET path to absolute with the domain of AEM application appending to it still its throwing the error since the page is not hosted in the very same AEM application domain but from an external Application domain.

If that is the case how to fix the CORS issue in order to allow external App domains to access /libs/granite/csrf/token.json.

8 Replies

Avatar

Level 3

That error suggests you didn't add http|https to the URL. Try accessing the token url directly from the browser and check if you see the token.

Avatar

Level 4

abhishekb wrote...

That error suggests you didn't add http|https to the URL. Try accessing the token url directly from the browser and check if you see the token.

 

 

If I access the token url directly(local instance publish - http://localhost:4503/libs/granite/csrf/token.json) from the browser it works fine. I am able to get the token ID. But from an external applications the token url is not working. Its throwing CORS error. Some how it needs to allow the external domain to call the TOKEN_SERVLET to get the response.

Avatar

Level 3

Rohit Nalla wrote...

abhishekb wrote...

That error suggests you didn't add http|https to the URL. Try accessing the token url directly from the browser and check if you see the token.

 

 

If I access the token url directly(local instance publish - http://localhost:4503/libs/granite/csrf/token.json) from the browser it works fine. I am able to get the token ID. But from an external applications the token url is not working. Its throwing CORS error. Some how it needs to allow the external domain to call the TOKEN_SERVLET to get the response.

 

You could write a Filter for enabling CORS in AEM. Check this SO post. In case you have dispatcher in your setup, you'll need to enable in the apache module as well.

Avatar

Level 5

Rohit Nalla wrote...

abhishekb wrote...

That error suggests you didn't add http|https to the URL. Try accessing the token url directly from the browser and check if you see the token.

 

 

If I access the token url directly(local instance publish - http://localhost:4503/libs/granite/csrf/token.json) from the browser it works fine. I am able to get the token ID. But from an external applications the token url is not working. Its throwing CORS error. Some how it needs to allow the external domain to call the TOKEN_SERVLET to get the response.

 

It works because the browser is intelligent enough to make a second request with https whenthe first http request fails and gets a 301 as response from the service. 

Avatar

Level 10

try writing a custom AEM Servlet that retrieves the data and then return the data as a GET request. 

Avatar

Level 3

smacdonald2008 wrote...

try writing a custom AEM Servlet that retrieves the data and then return the data as a GET request. 

 

If you're saying custom servlet for fetching the token, that will duplicating AEM's OOTB servlet. If you're saying GET request for the search query instead of POST (which makes sense for a search query), that is a design change and up to the OP.

Avatar

Level 4

abhishekb wrote...

smacdonald2008 wrote...

try writing a custom AEM Servlet that retrieves the data and then return the data as a GET request. 

 

If you're saying custom servlet for fetching the token, that will duplicating AEM's OOTB servlet. If you're saying GET request for the search query instead of POST (which makes sense for a search query), that is a design change and up to the OP.

 

Well I am not sure if we can duplicate the OOTB servlet in order to generate a random Token ID.

Avatar

Former Community Member

Hi

Can you try adding crossdomain.xml something like below. We had some cross-domain calling issues in AEM 5.6 and we added this file to resolve. Basically <yourdomain>/crossdomain.xml should render this - 

<cross-domain-policy>
<!--
Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html 
-->
<!-- Most restrictive policy: -->
<allow-access-from domain="<callingdomain>"/>

<!-- Least restrictive policy: -->
<!--
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/> 
-->
</cross-domain-policy>