auth0 getAccessTokenSilently returns empty object when trying to set vue apollo client bearer token

117 views Asked by At

We have a Vue application that connects to a Hasura API via the vue apollo client and uses auth0 for authentication.

The app runs fine when we give the apollo client the Hasura admin secret header (see x-hasura-admin-secret commented out below).

However when we try to use the bearer token it fails because auth0.getAccessTokenSilently() returns an empty object even though we can successfully login via auth0's redirect.

We know the auth0 login is working because useAuth0 on the .vue pages returns the correct logged in user data as expected (we can currently only get to these pages by uncommenting the x-hasura-admin-secret headers).

See our main.js below:

import { createApp, provide, h } from 'vue'
import './tailwind.css'
import App from './App.vue'
import { routes } from './routes.js'
import { createRouter, createWebHistory } from 'vue-router'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { createAuth0 } from '@auth0/auth0-vue';
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
import { HttpLink } from 'apollo-link-http'

const auth0 = createAuth0({
  domain: import.meta.env.VITE_AUTH0_DOMAIN,
  client_id: import.meta.env.VITE_AUTH0_CLIENT_ID,
  redirect_uri: window.location.origin
})

function getToken(){
  // let token = localStorage.getItem('Auth_token')
  let token = auth0.getAccessTokenSilently()
  console.log("token="+JSON.stringify(token))
  return token
}

const httpLink = new HttpLink({
  uri: import.meta.env.VITE_API_URI,
  headers: {
    "content-type": "application/json",
    // "x-hasura-admin-secret": import.meta.env.VITE_HASURA_ADMIN_SECRET,
    "Authorization": `Bearer ${getToken()}`,
  }
})

const wsLink = new WebSocketLink({
  uri: import.meta.env.VITE_API_WS,
  options: {
    reconnect: true,
    timeout: 30000,
    inactivityTimeout: 30000,
    lazy: true,
    connectionParams: {
      headers: {
        "content-type": "application/json",
        // "x-hasura-admin-secret": import.meta.env.VITE_HASURA_ADMIN_SECRET,
        "Authorization": `Bearer ${getToken()}`,
      }
}
  },

})

const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query)
    return definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
  },
  wsLink,
  httpLink
)

const cache = new InMemoryCache({
  typePolicies: {
    Subscription: {
      goal: {
        goals: {
          fields: {
            merge(existing, incoming) {
              return incoming;
            }
          }
        }
      }
    }
  }
})

const apolloClient = new ApolloClient({
  link,
  cache,
  connectToDevTools: true,
})

const app = createApp({
  setup () {
    provide(DefaultApolloClient, apolloClient)
  },

  render: () => h(App),
})

const router = createRouter({
  history: createWebHistory(),
  routes,
})

app.use(router)
app.use(
  auth0
);
app.mount('#app')
1

There are 1 answers

0
Jesse Carter On

getAccessTokenSilently returns a promise. You need to await it in order to get the actual value.

You may need to use the Apollo setContext link in order to inject some async processing before things are passed over to the HttpLink.

I haven't used Vue before but a similar approach from a React app could look something like this:

  const authLink = setContext(async (_request, { headers }) => {
    const token = await auth0.getAccessTokenSilently();

    const newHeaders = {
      ...headers,
      Authorization: `Bearer ${token}`,
    };

    return {
      headers: newHeaders,
    };
  });
  
  // create your HttpLink as before but concat it so that the auth
  // one runs first
  const httpLink = new HttpLink(/** stuff **/);  

  const finalLink = authLink.concat(httpLink);