auth0/auth0-vue

_client is undefined when you create another auth0 instance (for linking another account)

felixzapata opened this issue · 5 comments

Describe the problem

Hi, I'm trying to create a basic example with login and account linking using the playground. I know that this option is available and there is an example about how to do that in the spa version, and although the angular example has not had the code showing how to add a link account button, I made it following the API of your Angular module.

So, following more or less the same steps I need to test the link account option using a Vue application but I can't and I am not very sure if is because there is a bug or if I am misunderstanding something.

What was the expected behavior?

After login into my application, I want to add a button to open the login popup in order to select another account and link to the main which is already logged in (as the spa basic example or my angular example do)

Reproduction

  • Use the playground to log in via Auth0 in a Vue application.
  • Create a button inside the application in order to link to another account:
async function linkAccount() {
      const token = await getAccessTokenSilently();
      const {
        __raw: targetUserIdToken,
        email_verified,
        email,
      } = await authenticateUser();
    }

    async function authenticateUser() {
      const a0 = createAuth0({
        domain: domain,
        client_id: clientId,
        redirect_uri: window.location.origin,
        useCookiesForTransactions: true,
        audience: `https://${domain}/api/v2/`,
        scope:
          'openid email profile read:current_user update:current_user_identities',
      });
      console.log(a0._client)
      // Not working at the moment
      await a0.loginWithPopup({
        max_age: 0,
        scope: 'openid',
      });

Once you click on the button, you will see how the new instance has no the _client property:

Captura de pantalla 2022-08-26 a las 10 51 19

Captura de pantalla 2022-08-26 a las 10 51 22

I've checked the code and the _client property is only set when the plugin is installed/registered on the application:

this._client = new Auth0Client({
      ...this.clientOptions,
      auth0Client: {
        name: 'auth0-vue',
        version: version
      }
    });

So, I don't know if this is the correct way to create another instance in order to log in to another account in order to link it to the main or if the plugin has a bug and it is not ready to do this.

Environment

  • Version of auth0-vue used: 1.0.2
  • Version of vue used: ^3.2.37
  • Which browsers have you tested in? Chrome
  • Other modules/plugins/libraries that might be involved: none

Hey,

You are not supposed to use createAuth0 like this, it's a Vue plugin so you need to pass it to Vue accordingly. Can you elaborate why you want to create another instance?
You mention you have a working Angular example that does what you want, can you share that?

On top of that, can you share a running sample of what you are trying to do in order for us to see what's going on? But keep in mind, randomly calling createAuth0 somewhere is not going to work, that is not how Vue plugins are supposed to be instantiated.

Hi, I can't share the Angular example complete but I can show the functions used to do the same:

async authenticateUser (): Promise<IdToken | undefined> {
    const config: AuthClientConfig = new AuthClientConfig();
    config.set({
      domain: this.authConfig.get().domain,
      clientId: this.authConfig.get().clientId,
      redirectUri: window.location.origin,
      audience: `https://${this.authConfig.get().domain}/api/v2/`,
      scope: 'openid email profile read:current_user update:current_user_identities'
    });
    const a0 = await Auth0ClientFactory.createClient(config);
    await a0.loginWithPopup({
      max_age: 0,
      scope: 'openid',
    });
    return a0.getIdTokenClaims();
  }

  async _linkAccount() {
    const user = await firstValueFrom(this.auth.getUser());
    const sub = user?.sub;
    const userData = await this.authenticateUser();
......
more code here
......

And your SPA example more or less is the same:

const authenticateUser = async () => {
  const a0 = new Auth0Client({
    domain: config.domain,
    client_id: config.clientId,
  });
  await a0.loginWithPopup({
    max_age: 0,
    scope: "openid",
  });
  return await a0.getIdTokenClaims();
};

const linkAccount = async () => {
  const accessToken = await auth0.getTokenSilently();
  const { sub } = await auth0.getUser();
  const {
    __raw: targetUserIdToken,
    email_verified,
    email,
  } = await authenticateUser();

so, I am assuming that I need to do the same in a Vue application: I can't use the same instance where the user is logged and I must create another one in order to show the popup login.

Again, I am not very sure if is because there is a bug or if I am misunderstanding something on the plugin API and this is not the way to do the linking account option.

Maybe instead of use const a0 = createAuth0({ I have to use const a0 = new Auth0Client({ as the SPA example does.

Can you elaborate on why you need to create a new instance so we can better understand what you are trying to do ?

createAuth0 creates a Vue Plugin, which you need to pass to Vue.use(). Creating it and not passing it to Vue.use is not supported, as you can see it won't behave as expected.

Our Vue SDK provides a simplified API for the majority of our users, but people having complicated and very specific needs can still resort back to Auth0-SPA-JS where needed.

Being the maintainer or our Angular SDK, the same goes with Angular, it might work the way you re using it but it's not guaranteed not to cause issues.

If you could elaborate what you are trying to do, we can see if there are alternative options besides resorting back to Auth0-SPA-JS.

I am trying to do the same as this example. An application where you can log in and link and see your accounts.

For an Angular application, I used your angular module and for a Vue application, I am using your Vue plugin.

The new instance is because, as I said in my comments, I am assuming and following the original example from the SPA. I mean, to link another account into a logged account you must create another auth object and open the login popup using this object.

As you say, maybe for this kind of behavior I need to use directly the Auth0-SPA-JS instead of trying to do it through the plugin.

Yes, I believe functionality like that needs the use of Auth0-SPA-JS, or a different way to architect things and ensure the logging into a different account uses an entirely different page, resulting in the fact that on one page we still only ever have one instance of Auth0Client running. Doing that would be supported work with our Angular and Vue SDK, but requires more effort in terms of communicating between the different windows on your side.

I mean, to link another account into a logged account you must create another auth object and open the login popup using this object.

I believe it does not require a second instance on the same page. It may be so that your code as well as that example does that, but it's probably not the only way. Apart from what's mentioned above, I can also think of ways where you sync stuff to your own local storage (or anywhere else realy, even your own database), and then use the information of both logged in users again to link them, which again wouldn't need a second client. Long story short, I believe there are options, but if your focus is on two instances on the same page, you can't run our Vue SDK in the way you want.

Anyway, easiest for you would probably be to just use Auth0-SPA-JS for the linking. However, keep in mind that if you do the following:

  • User1 logs in to the "root SDK instance"
  • User2 logs in to the "temprary SDK instance for linking"

If you would be using local-storage or opt-in to using refresh tokens, User2 might now also be the user used for the "root SDK instance", even thought it might not feel like that. So definitely something to be mindful about. When you are using linking, this may not matter at all. But in the case where you never finalize the linking, you might now be logged in as User 2 and no longer User1, which can or can not be expected.

Note: the above would also be the case with any of the mentioned alternatives here.

Closing as there is not much we can do from our Vue SDK. Happy to continue the conversation if needed.