How to use refresh tokens in a SPA (Single Page Application)?
This is a common design pattern for SPAs implementing authentication and authorization using Auth0. While relatively straightforward, there are several steps required to get an app up and running with refresh tokens.
Step 1: Configure required settings in the Auth0 Dashboard
-
Be sure the SPA application in Auth0 has the Refresh Token grant type enabled
-
Confirm the Allowed Callback URLs are properly set to the application’s URL where Auth0 can redirect after authentication.
-
Enable Refresh Token Rotation - Refresh token rotation is a security feature that helps protect against the misuse of refresh tokens. It ensures that refresh tokens are not long-lived and can be rotated (i.e., replaced) frequently, reducing the risk of token theft and unauthorized access.
-
Enable Allow Offline Access in the settings of the API registered in Auth0.
Important to note:
- Refresh token grant is not available for the Management API. Management API access tokens require a client credentials exchange and should generally not be handled in a public client (SPA). If your use case permits, Management API access tokens can be requested from a frontend, but will be limited in scope.
- If an audience param is omitted from the authorize request, the returned access token will be opaque. See below on how to include this param in all Auth0 Single-Page Application (SPA) SDK Libraries.
Step 2: Configure the Auth0 SPA SDK of choice
auth0-react
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Auth0Provider } from '@auth0/auth0-react';
import App from './App';
const root = createRoot(document.getElementById('root'));
root.render(
<Auth0Provider
domain='{yourDomain}'
clientId='{yourClientId}'
useRefreshTokens={true}
authorizationParams={{
redirect_uri: window.location.origin
audience: 'https://your_audience'
}}
>
<App />
</Auth0Provider>,
);
auth0-angular
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAuth0 } from '@auth0/auth0-angular';
import { AppComponent } from './app.component';
bootstrapApplication(AppComponent, {
providers: [
provideAuth0({
domain: '{yourDomain}',
clientId: '{yourClientId}',
useRefreshTokens: true,
authorizationParams: {
redirect_uri: window.location.origin,
audience: 'https://your_audience'
}
}),
]
});
auth0-vue
import { createAuth0 } from '@auth0/auth0-vue';
const app = createApp(App);
app.use(
createAuth0({
domain: '<AUTH0_DOMAIN>',
clientId: '<AUTH0_CLIENT_ID>',
authorizationParams: {
redirect_uri: '<MY_CALLBACK_URL>',
audience: '<AUTH0_AUDIENCE>',
useRefreshTokens: true
}
})
);
app.mount('#app');
auth0-flutter
Future<void> login() async {
try {
if (kIsWeb) {
return auth0Web.loginWithRedirect(
redirectUrl: 'http://localhost:3000',
audience: 'https://your_audience',
scopes: 'openid, profile, offline_access'
);
}
var credentials = await auth0
.webAuthentication(scheme: dotenv.env['AUTH0_CUSTOM_SCHEME'])
// Use a Universal Link callback URL on iOS 17.4+ / macOS 14.4+
// useHTTPS is ignored on Android
.login(useHTTPS: true);
setState(() {
_user = credentials.user;
});
} catch (e) {
print(e);
}
}
auth0-spa-js
await createAuth0Client({
domain: '<AUTH0_DOMAIN>',
clientId: '<AUTH0_CLIENT_ID>',
useRefreshTokens: true,
authorizationParams: {
redirect_uri: '<MY_CALLBACK_URL>',
audience: 'https://your_audience'
}
});
Logging
Successful refresh token exchanges will be logged under the event type code sertft
- More on Auth0 dashboard logging here.
Additional Useful Material:
- What are Refresh Tokens?! and…How to Use Them Securely (Video)
- Auth0 SPA SDK Quickstarts