How does auth0-spa-js store tokens

I understand that the best practices are to use and not store tokens in local storage. I am currently trying to understand how secure auth0-spa-js is but I can not find any information on the architecture for that library. Internally, how does auth0-spa-js store both access and refresh tokens?

EDIT: Things have changed since this discussion initially occurred. SPAs can use rotating refresh tokens, stored in local storage.

1 Like

There’s never a refresh token in a SPA scenario. And the SPA just keeps the access/ID token in memory, not persistent. When you reload the page, the tokens are gone, but the SDK will get a new one using the getTokenSilently method then, which is possible due to an existing user session at Auth0 side.

It’s the silent authentication concept.

Also relevant, as you’ve probably already seen:

EDIT: Refresh tokens are now allowed in SPAs via refresh token rotation


I understand, if using the Implicit Flow, that there is not a refresh token. I am looking at using the Authorization Code with Proof Key for Code Exchange (PKCE) flow.

Yes, that applies in the same way for this grant type, if the client is a SPA. It’s also the grant type that the auth0-spa-js uses.

Hi, I’m struggle to get the accessToken using getTokenSilently and validate it on the server-side. It throws Unauthorized Error: jwt malformed, but it works perfectly if I use access token from https://<AUTH0_DOMAIN>/oauth/token endpoint.
The access token from https://<AUTH0_DOMAIN>/oauth/token is longer than getTokenSilently.
Which part am I missing?
thank you

did you set the audience correctly? that should enable JWT access_token instead of a string access token.
Can you also check what is getting sent in headers

1 Like

Thanks for the reply. No, I didn’t set the audience when calling getTokenSilently. I just called getTokenSilently(). Ok, I got it. It’s hard to find the documentation that the audience should be set correctly to call getTokenSilently

Yeah, it works now. Thanks @npatel.

My steps are:
set the audience when calling getTokenSilently({audience: <from Dashboard - API - Detail>}), and also set Allow Skipping User Consent to enabled
and then modify etc/host ( and run the application in HTTPS


Cool you have figured it out!

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