Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auth0 client is null in Vue SPA on page refresh

I have a Vue SPA based on one of Auth0's quickstart apps (https://github.com/auth0-samples/auth0-vue-samples). Everything works fine out of the box, but as soon as I try using the Auth0 client in my component code I run into problems. I followed the "Calling an API" tutorial (https://auth0.com/docs/quickstart/spa/vuejs/02-calling-an-api), which unhelpfully only shows how to call an API using a button. What I want to do is trigger an authenticated call to my API on initial page load so that I can ensure certain data exists in my own API (or create it if it does not). This seems like it should be pretty straightforward. I just throw this code in my created hook of my Vue component:

await this.$auth.getTokenSilently().then((authToken) => {
    // reach out to my API using authToken
});

This actually works fine if the app hot reloads from my npm dev server, it reaches out to my API, which authorizes the request using the token, and sends back the correct data. The problem is when I manually reload the page, which causes this:

Uncaught (in promise) TypeError: Cannot read property 'getTokenSilently' of null
    at Vue.getTokenSilently (authWrapper.js?de49:65)
    at _callee$ (App.vue?234e:49)

Inside the authWrapper.js file (where the Auth0 client lives), the function call is here:

getTokenSilently(o) {
    return this.auth0Client.getTokenSilently(o);
}

When I debug the call, "auth0Client" doesn't exist, which is why it's failing. What I can't understand is the correct way to ensure it does exist before I attempt to make the call. There's nothing in the samples that indicates the right way to do this. I tried putting my component code in different components and different Vue lifecycle hooks (created, beforeMount, mounted, etc), all with the same result. The client becomes available after 800 ms or so, but not when this code executes.

This is clearly a timing problem, but it's not clear to me how to tell my component code to sit and wait until this.auth0Client is non-null without doing something horrible and hacky like a setInterval.

like image 759
adammtlx Avatar asked Feb 19 '20 14:02

adammtlx


1 Answers

I figured out a workaround for now, which I'll add as an answer in case anyone else has this issue, although it's not really the answer I want. Per the authGuard, you can use the exported "instance" from the authWrapper and watch its "loading" flag before executing your code that depends on the auth0Client being ready, like this:

import { getInstance } from "./auth/authWrapper";

// ... Vue component:
created() {
    this.init(this.doSomethingWithAuth0Client);
},
methods: {
    init(fn) {
        // have to do this nonsense to make sure auth0Client is ready
        var instance = getInstance();
        instance.$watch("loading", loading => {
            if (loading === false) {
                fn(instance);
            }
        });
    },
    async doSomethingWithAuth0Client(instance) {
        await instance.getTokenSilently().then((authToken) => {
            // do authorized API calls with auth0 authToken here
        });
    }
}

It's hardly ideal, but it does work.

like image 185
adammtlx Avatar answered Sep 18 '22 23:09

adammtlx