Invoking an API from a SPA with getTokenWithPopup/getTokenSilently -- is there a better way to do this?


I’m a developer working on bringing auth0 to our company. We have a SPA Vue app that I’ve setup with @auth0/auth0-spa-js, and a node API that uses jwt/jwks to use the bearer token for authentication.

I develop on my localhost and ended up doing this to invoke the API:

async getBearerToken() {
    const tokenGoodToGo = (t) => {
        let dotIndex = t.indexOf('.')
        return dotIndex > 0

    const auth = getInstance()

    const silentBearerToken = await auth.getTokenSilently({audience: this.apiAudience})
    if(tokenGoodToGo(silentBearerToken)) {
        return silentBearerToken

    return await auth.getTokenWithPopup({audience: this.apiAudience})

It feels a bit clunky, but seems to work. Does anyone have a recommended alternative? I try to avoid having different code paths in dev and production but this was the best I could do here.

Hi @mpekar,

I would take a look at our vue quickstart:

This shows how we recommend calling the API. It could help smooth out some of your code.

Hope this helps!

I started with that but when I call getTokenSilently() on localhost it fails.

What error are you getting?

I shouldn’t have said it failed. Rather, it returns what I guess is an " opaque" token in some circumstances (probably when I’m on localhost). This opaque token will not validate with the API, and the sample code referenced above demonstrates no handling of it.

getTokenWithPopup always seems to return a valid auth token so I use that as the fallback on my localhost development. A popup briefly flickers but I assume I can use that same code path in both development and production.


The opaque token is an access token intended for internal auth0 APIs, like the /userinfo endpoint. If you want to get an encoded JWT you must declare an external audience.

How do I declare an external audience? I’m passing the ‘Identifier’ shown for my API on the Auth0 website. It’s described as:

“Unique identifier for the API. This value will be used as the audience parameter on authorization calls.”

Can you post all of your code? Particularly the initialization of the auth0-spa-js package.

On the UI side:

authGuard.txt (727 Bytes)
index.txt (4.4 KB)
main.txt (2.6 KB)

with the SPA initialization like so:

const Auth0Domain = process.env.VUE_APP_AUTH0_DOMAIN
const Auth0ClientId = process.env.VUE_APP_AUTH0_CLIENT_ID
const Auth0ApiAudience = process.env.VUE_APP_AUTH0_API_AUDIENCE
Vue.use(Auth0Plugin, {
     domain: Auth0Domain, clientId: Auth0ClientId,
     redirectUri: window.location.origin,
     onRedirectCallback: appState => {
             appState && appState.targetUrl ? appState.targetUrl  : window.location.pathname
     onLoginComplete: () => {

On the API side, it’s just this little middleware bit:

const JwtCheck = jwt({
    secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `https://${AUTH0_DOMAIN}/.well-known/jwks.json`
    issuer: `https://${AUTH0_DOMAIN}/`,
    algorithms: ['RS256']

To reiterate though, this code does work, and it’s almost exactly what the Auth0 samples have. I’m just confused why the API has these separate getTokenSilently() and getTokenWithPopup() methods, and why in particular getTokenSilently() sometimes returns an opaque JWT and other times a token that’s perfectly compliant with what the API expects.

Shouldn’t the API give me a method I can always trust will return a JWT that the API can accept?

You should only get an opaque token if you aren’t specifying an audience for the call. You are intermittently getting an opaque token?

Have you tried specifying the audience when you initialize the auth0 client? Take a look at main.js in the vue quickstart I linked above. It should be the API identifier.


Dan, I must have not been passing the audience somehow as you suggested. If I pass it either in the call to getTokenSilently()

await auth.getTokenSilently({audience: this.apiAudience})

…or in the Auth0Plugin config:

Vue.use(Auth0Plugin, {
         domain: Auth0Domain, clientId: Auth0ClientId,
         audience: Auth0ApiAudience,

Or in both, then getTokenSilently() seems to return correctly. My apologies for all that back and forth for what was apparently user error.

1 Like

No problem! Glad you worked it out. Let us know if you run into anything else.

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