How to Use Refresh Tokens in a SPA
Using refresh tokens is a common design pattern for Single Page Applications (SPAs) implementing authentication and authorization using Auth0. While relatively straightforward, several steps are required to get an app up and running with refresh tokens.
- Refresh Tokens
- Single-Page Applications (SPA)
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 the 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
