Auth0 Single Page Application doesn't allow first-time user authentication popups due to mismatched origins

Hello,

I have a React app using the SPA starter with the Auth0Provider React wrapper. I have bought the starter package where I am using a custom domain, e.g. domain.com.

My application is hosted on blog.domain.com and I have auth0 authenticating via auth.domain.com.

Despite this, when a user signs up for the first time on my application, e.g. via apple ID, I get the following errors:

[Error] Blocked a frame with origin "https://blog.domain.com" from accessing a frame with origin "https://auth.domain.com". Protocols, domains, and ports must match.
	r (index-xxxxxxxx.js:...)

And also this error:

[Error] Unhandled Promise Rejection: Error: Unable to open a popup for loginWithPopup - window.open returned `null`
	promiseEmptyOnRejected (index-f1fe79f8.js:194:3276)
	promiseReactionJob

Now if I were to run this flow on desktop, the popup would open, I can grant access to my open id scopes, etc. and then happily continue onto my application as a signed in user.
However, for mobile users (which is 99.99% of my audience for this application) the popup is blocked on the browsers I have tried (Safari, and Firefox).

This is the code that is executed on page load:

export class AuthUtils {
  static async getBearer(authInstance) {
    return new Promise((resolve, reject) => {
      const fetchAccessToken = async () => {
        const auth = {
          authorizationParams: {
            scope: 'openid profile email',
            audience: 'https://xxxxxx.eu.auth0.com/api/v2/'
          }
        };

        if (!authInstance) {
          throw new Error('Invalid authentication handler');
        }

        try {
          return await authInstance.getAccessTokenSilently(auth);
        } catch (e) {
          if (e.error === 'consent_required') {
            return await authInstance.getAccessTokenWithPopup(auth);
          } else if (e.error === 'login_required') {
            return await authInstance.loginWithRedirect();
          } else {
            throw e;
          }
        }
      };

      Promise.resolve(fetchAccessToken()).then((token) => resolve(token));
    });
  }
}

So when a user loads the page, this method is executed and the returned token is stored in state and assigned as a bearer token to all requests.

My application code loads as follows:

<Auth0Provider
      domain="auth.domain.com"
      clientId="xxxxx"
      authorizationParams={{
        redirect_uri: window.location.origin
      }}
    >
      <Provider store={reduxStore}>
        <AppWrapper />
      </Provider>
    </Auth0Provider>
  );

I’m not entirely sure where to go from here. All of my settings are configured to allow for callbacks, CORS, etc. on my domains (auth and blog subdomains).

The only thing I was thinking was to perhaps authenticate without popup - is this doable? Or have I done something wrong in the setup of my application?

Thanks!

Would also like to note that I have just tried passing a ‘display’ field to the authorizationParams object passed in to these functions.
Every value I put in (touch, wap, page, etc) seems to be ignored with the preference being to use a popup.

Ok, live debugging thread going on - here are my thoughts so far.

I think I have been using an ID token to pass as my Bearer token to my backend. This gets parsed (against the jwks provided from auth0) and validated for the claims before allowing the API to do whatever it is it needs to do.
From what I’ve been looking into this is wrong and I should be using an access token instead.

My current understanding (though I’m still trying to clear this up) is that the access token is generated without any information that requires identifiable information from the customer so no openid emails, etc.

I think the way to generate this (looking at my code snippet) is to not specify the audience or scope when requesting an access token.

The only problem now is that I think this is not a valid JWT anymore as it does not have the three segments so my Bearer parsing logic fails on the backend/api side.

Is there any guidance with how I should be validating these tokens on the backend? And also, is the assumption that i’m making here true - being that if I am given an access token as an API and can do some kind of verifications on it* then the token is legit + I can serve/respond to this request?

*not sure what verifications to do on the token beyond checking headers are correct. any advice on this?

Thanks!

1 Like

If anyone is struggling with this in the future. I’ve cracked it.

Do not trust anything I wrote up above… I was confused and tired (up late debugging).

In my case it does seem to make sense to pass an ID token to my own internal APIs for verification against the Claims of the JWT. This is fine!

An access token is for another problem which is authorization not authentication, e.g. scopes for permissions on using an API.

Anyhow. The problem was that my authentication was too fast. The custom domain works fine for dealing with the cross-origin stuff, but the problem was that I was using lots of rapid redirects to my end customer.

The solution is to basically break up the authentication mechanism so that it fails when consent_required is given.
This is then process as an extra page where the user is given a button to click to ‘allow authentication’ which will then open a window, pass that window in to the auth0 method for getting a token with popup, and then they are redirected using this window.

In short, most browsers require that popups happen after one click from the user to avoid spammy ux.
So essentially, register a click handler like a button with an onClick, open your window dialog, and pass it in to the API for authentication.

Hope this helps someone!
Thanks,
Felix

1 Like

No worries! I guess we’ve all been there! Anyways thanks for sharing with the rest of community!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.