Auth0-spa-js: Unable to call getTokenSilently concurrently

Created on 24 Jul 2019  路  8Comments  路  Source: auth0/auth0-spa-js

Hi!

I'm having an error in the console when creating the Auth0 client using createAuth0Client().

The error is the following:

> ERROR TypeError: Cannot read property 'close' of undefined
    at o (auth0-spa-js.production.js:formatted:1)
    at ZoneDelegate.invokeTask (zone-evergreen.js:391)
    at Object.onInvokeTask (auth0-spa-js.production.js:formatted:1)
    at ZoneDelegate.invokeTask (zone-evergreen.js:390)
    at Zone.runTask (zone-evergreen.js:168)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:465)
    at invokeTask (zone-evergreen.js:1603)
    at globalZoneAwareCallback (zone-evergreen.js:1640)

On the beautyfied production code, the error points to this line:

o = function(i) {
  // Below is the culprit, apparently, 'i.source' is undefined
  i.origin == m && i.data && "authorization_response" === i.data.type && (i.source.close(),
  i.data.response.error ? t(i.data.response) : e(i.data.response),
  clearTimeout(n),
  window.removeEventListener("message", o, !1),
  window.document.body.removeChild(r))
};

And the code that's calling the createAuth0Client() function is this one (pretty much the exact same as the Angular Login Tutorial):

// This is a method of my AuthService
async getAuth0Client(): Promise<Auth0Client> {
  if (!this.auth0Client) {
    console.log("AuthService - generating new Auth0Client");
    this.auth0Client = await createAuth0Client(environment.auth0);
    console.log("AuthService - new Auth0Client", this.auth0Client);

    // Provide the current value of isAuthenticated
    this.isAuthenticated.next(await this.auth0Client.isAuthenticated());

    // Whenever isAuthenticated changes, provide the current value of `getUser`
    this.isAuthenticated
      .subscribe(async isAuthenticated => await this.updateUserProfile(isAuthenticated));
  }
  return this.auth0Client;
}

Note that this error does not prevent the client to be effectively created and functionning. But it's still an unwanted error 馃槃

Also, some other people seem to have the same kind of issue, see this thread in the Auth0 Community Forums

Most helpful comment

Thanks for posting this @Schmaga! We recently upgraded our Angular Quickstart with the shareReplaymethod as well. Here's our take on a great AuthService: https://auth0.com/docs/quickstart/spa/angular2/01-login#add-an-authentication-service

All 8 comments

This is probably happening because the iframe was already closed 馃 Can you inspect dev tools and see if you're sending two requests to the /authorize endpoint?

I've got the same problem, and there are 2 requests sent to /authorize.

Same problem, two requests. Happens only if you refresh a page after sign-in (I'm using Auth0 with Angular SPA), without refreshing it works fine and sends just one request.

@gosuhiman @rkuprin Is it possible that you have two instances of the client? can you build a repro to help me identify why are you seeing two requests?

Same problem if you write code like this:

class Auth { 
 getAuthClient = () => new Promise((resolve) => {
    if (this.auth0Client) {
      return resolve(this.auth0Client);
    }

    return resolve(createAuth0Client({
    domain: DOMAIN,
    client_id: CLIENT_ID,
    connection: DATABASE,
    audience: AUDIENCE,
    redirect_uri: ORIGIN,
    }).then(auth0Client => resolve(auth0Client)));
  });

  getUserInfo = async () => {
    const auth0Client = await this.getAuthClient();
    return auth0Client.getUser();
  }
}

If you call getUserInfo in several places in your project, after refreshing page, it will invoke function createAuth0Client several times.
In function createAuth0Client, it will call getTokenSilently. That is, if you invoke createAuth0Client multiple times, it will use a same iframe window(not sure). Once a tread called close, the other thread will fail.
So I changed the code to this:

class Auth {
  constructor() {
    this.setUp();
  }

  setUp = async () => createAuth0Client({
    domain: DOMAIN,
    client_id: CLIENT_ID,
    connection: DATABASE,
    audience: AUDIENCE,
    redirect_uri: ORIGIN,
  }).then((auth0Client) => {
    this.auth0Client = auth0Client;
  });

  getAuthClient = () => new Promise((resolve) => {
    const checkAuth0Ready = setInterval(() => {
      if (this.auth0Client) {
        resolve(this.auth0Client);
        clearInterval(checkAuth0Ready);
      }
    }, 500);

    setTimeout(() => clearInterval(checkAuth0Ready), 10000);
  });

  getUserInfo = async () => {
    const auth0Client = await this.getAuthClient();
    return auth0Client.getUser();
  }

And it doesn't throw exceptions now.

This is a duplicate of my post from the auth0 community forum, but I hope it might help someome:

I think I might have found the problem. Because of the observables and multiple subscriptions, there might have been concurrency issues or multiple instances of the Auth0Client. I got rid of the error by doing it this way:

Create the client as shared observable in a similar way like you proposed in your updated angular quickstart:

  private auth0Client$ = (from(createAuth0Client(this.config))).pipe(
    shareReplay(1), // Every subscription receives the same shared value
  );

And using it using a piped observable.

isAuthenticated() {
 return this.auth0Client$.pipe(switchMap(client => {
   return from(client.isAuthenticated());
}));
}

This has solved the error for me.

Thanks for posting this @Schmaga! We recently upgraded our Angular Quickstart with the shareReplaymethod as well. Here's our take on a great AuthService: https://auth0.com/docs/quickstart/spa/angular2/01-login#add-an-authentication-service

@luisrudge I've seen that! And I got to say that this is a waaaay better AuthService than the one before 馃憤
(By the way, I did not run onto this thread issue)

Was this page helpful?
0 / 5 - 0 ratings