XMLHttpRequest cannot load ... due to access control checks

Probably the last two hours I now tried to get access to the user metadata (after I finally got it done to access the username, image, etc.) in my frontend … and can’t make it work… while using the API explorer works perfectly fine, in the frontend I only get error messages (hosted on Heroku):

not sure whats wrong…
here the app.js header:

window.addEventListener('load', function () {
var idToken;
var accessToken;
var expiresAt;

var apiUrl = 'https://heyskillme.auth0.com/api/v2';
var userProfile;


var webAuth = new auth0.WebAuth({
    domain: 'heyskillme.auth0.com',
    clientID: '...',
    redirectUri: 'https://heyskillme.herokuapp.com/home',
    responseType: 'token id_token',
    audience: 'https://heyskillme.auth0.com/api/v2/',
    scope: 'openid profile read:users',
    leeway: 60
});

and where I call the API:

function localLogin(authResult) {
    console.log('localLogin()')

    // Set isLoggedIn flag in localStorage
    localStorage.setItem('isLoggedIn', 'true');
    // Set the time that the access token will expire at
    expiresAt = JSON.stringify(
        authResult.expiresIn * 1000 + new Date().getTime()
    );
    accessToken = authResult.accessToken;
    idToken = authResult.idToken;

    if (accessToken && show_user_menu_button) {
        getProfile();
        showElement('id', 'show_user_menu_button', 'flex')
        switchClass('id', 'show_user_menu_button', 'cta secondary make_stick_right user_menu_button w-inline-block', 100)
        callAPI('/read:users_app_metadata', true)
    }
}

and the API call function in the app.js:

function callAPI(endpoint, secured) {
    try {
        var url = apiUrl + endpoint;
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        if (secured) {
            xhr.setRequestHeader(
                'Authorization',
                'Bearer ' + accessToken
            );
        }
        xhr.onload = function () {
            if (xhr.status == 200) {
                // update message
                document.querySelector('#ping-view h2').innerHTML = JSON.parse(
                    xhr.responseText
                ).message;
            } else {
                alert('Request failed: ' + xhr.statusText);
            }
        };
        xhr.send();
    } catch (error) {
        console.log(error)
    }

}

Have in mind that most of the endpoints (and associated scopes) associated with the Management API are intended for sensitive operation that allow to completely manage/control a specific tenant. The implications it that most of these endpoints and action are also not suitable to be invoked directly from a browser application.

In other words, in order to obtain an access token applicable for most operation in this API you need to perform a client credentials grant as most scopes will not be granted in end-user based flows like the one you are initiating in your application. A client credentials grant implies a confidential client which excludes browser-based application so if most of the scopes (read:users being one of them) require a confidential client and a browser-based application cannot be a confidential client it is not really relevant what the Management API supports in terms of CORS requests (the error you’re getting is a CORS related error).

In conclusion, you’ll need to change your approach as Management API is mostly not meant to be called directly from a browser application. In your specific application you could either:

  • obtain an access token with client credentials from your own back-end and then from the SPA call your back-end (lots of responsibility for you here, as you would need to ensure that calls to your backed are properly authorized).
  • make use of custom claims (OpenID Connect Scopes) to surface user metadata information directly in ID tokens or /userinfo endpoint response. This way the client application could get the relevant information either from the ID token or the user information endpoint and not have to call Management API.

The second point would likely be the general recommendation to meet your requirements.

works now - thanks :slight_smile: However… how can I update/create entries in the userdata? Does this require the use of your management API? All the different documentation you have make it honestly quiet confusing to setup Auth0…

I understand your point about the documentation; the reality is that there’s some flexibility and different ways to achieve the same end-goal. Depending on the scenario one may be better suited than other, but then this complicates reference documentation which needs to cover what’s available, but may fail to cover all the scenarios where one should be used versus the other.

In relation to the problem at hand, I hate to do this, but if creating/updating is also part of the equation then the general recommendation I made before may even need to be revisited. For purely read access I would maintain that ID token or /userinfo are likely the simplest way to get the information.

However, if the client application will also be managing it then it depends… will you be needing to manage just user_metadata or also app_metadata entries?

Both user meta data (what the user is learning, what city he/she lives in, etc.) and app meta data (activity points, based on which the user level gets calculated & the selected plan, based on if the user upgraded/ downgraded or not. Guess gonna use stripe for the payments)

Given that the application will be managing app_metadata and this requires a Management API access token obtained through client credentials then you’re guaranteed to be required to have a backend from where you could perform this.

Assuming a backend is present and that backend already has logic to call Management API (for updating app_metadata purposes) then my recommendation is that this backend exposes endpoints that allow the SPA part of the application to update user_metadata for the current user.

The high-level flow would be the following:

  1. end-user authenticates and chooses to update their profile.
  2. the application calls a custom endpoint in your backend that allows to update user_metadata based on the data received from the SPA.
  3. the backend validates that the call is authenticated (this may either be through a token or a cookie based session).
  4. from either the token or cookie based session the backend knows who is the user currently authenticated so uses a client credentials access token to call the Management API and update the user metadata for that specific user.

Any documentations for Django to make this happen? Or do I need to add NodeJS to my server as well?

I confess I’m not an expert on Python (far from it actually), but for the part about calling Management API from a Python-based backend you can check the Python SDK (GitHub - auth0/auth0-python: Auth0 SDK for Python).