Token Refresh
How refresh works
Section titled “How refresh works”When the access token expires, oidc-js uses the refresh token to obtain a new access token without requiring the user to log in again.
Proactive refresh
Section titled “Proactive refresh”By default, OidcClient proactively refreshes the access token before it expires using a short setInterval poll. Every tick compares the current time against expiresAt - expiryBuffer and triggers a refresh when the token is about to expire. This means the token is always fresh — no 401s, no interceptors needed, no fallback flash.
This is enabled by default. You can configure the behavior with two options:
const config = { issuer: "https://auth.example.com", clientId: "my-app", redirectUri: "http://localhost:5173/callback", expiryBuffer: 60, // refresh 60s before expiry (default: 30) autoRefresh: true, // enabled by default autoRefreshInterval: 10, // polling interval in seconds (default: 10)};The polling interval is lightweight — one number comparison per tick, no re-renders or network calls until a refresh is actually needed. The approach is drift-proof and sleep-proof: after a laptop wakes from sleep, the very first tick catches the expiration.
Disabling proactive refresh
Section titled “Disabling proactive refresh”Set autoRefresh: false to rely on reactive mechanisms instead (interceptors, RequireAuth re-mount):
<AuthProvider config={{ ...config, autoRefresh: false }}> <App /></AuthProvider>Reactive refresh with RequireAuth
Section titled “Reactive refresh with RequireAuth”RequireAuth handles refresh reactively. When a user navigates to a protected route with an expired token:
- Checks
tokens.expiresAtusingisExpiredAt()from core (with an optional buffer) - If expired, calls
actions.refresh() - If refresh succeeds, renders the children with new tokens
- If refresh fails, redirects to login
<RequireAuth> <Dashboard /></RequireAuth>You can set an expiryBuffer (in seconds) on the provider config to refresh the token early, accounting for clock skew and network latency. The default buffer is 30 seconds.
<AuthProvider config={{ ...config, expiryBuffer: 60 }}> <App /></AuthProvider>Manual refresh
Section titled “Manual refresh”You can also trigger a refresh manually:
import { useAuth } from "oidc-js-react";
function RefreshButton() { const { actions } = useAuth();
async function handleRefresh() { try { await actions.refresh(); } catch (error) { console.error("Refresh failed:", error); } }
return <button onClick={handleRefresh}>Refresh Token</button>;}Checking token expiry
Section titled “Checking token expiry”The tokens object from useAuth includes expiry information:
const { tokens } = useAuth();
// Unix timestamp in seconds when the access token expiresconsole.log(tokens.expiresAt);Use the helper functions from oidc-js-core to work with token expiry:
import { isExpiredAt, timeUntilExpiry } from "oidc-js-core";
const { tokens } = useAuth();
// Check if expired (includes a default 30-second buffer)const expired = isExpiredAt(tokens.expiresAt);
// Check with a custom buffer (in seconds)const expiringSoon = isExpiredAt(tokens.expiresAt, 120);
// Get seconds remaining until expiryconst secondsLeft = timeUntilExpiry(tokens.expiresAt);Requesting a refresh token
Section titled “Requesting a refresh token”To receive a refresh token, include offline_access in your scopes:
const config = { issuer: "https://auth.example.com", clientId: "my-app", redirectUri: "http://localhost:5173/callback", scopes: ["openid", "profile", "email", "offline_access"],};Without offline_access, the IdP may not issue a refresh token, and actions.refresh() will throw.