Skip to content

Commit 8f8b5fc

Browse files
docs: add docs for optimizing token refresh in middleware (#2426)
2 parents 4242867 + 3b73a2b commit 8f8b5fc

File tree

1 file changed

+130
-1
lines changed

1 file changed

+130
-1
lines changed

EXAMPLES.md

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,76 @@ export default withApiAuthRequired(async function handler(
923923
By setting `{ refresh: true }`, you instruct the SDK to bypass the standard expiration check and request a new access token from the identity provider using the refresh token (if available and valid). The new token set (including the potentially updated access token, refresh token, and expiration time) will be saved back into the session automatically.
924924
This will in turn, update the `access_token`, `id_token` and `expires_at` fields of `tokenset` in the session.
925925

926+
### Optimizing Token Refresh in Middleware
927+
928+
When using `getAccessToken()` in middleware for Backend-for-Frontend (BFF) patterns or to ensure fresh tokens for Server Components, avoid calling it on every request. Instead, implement time-based refresh logic to only refresh when the token is nearing expiration.
929+
930+
> [!NOTE]
931+
> This pattern is designed for **centralized token management** in middleware. For **per-request latency mitigation** (e.g., checking token expiry immediately before a critical API call), see [Mitigating Token Expiration Race Conditions](#mitigating-token-expiration-race-conditions-in-latency-sensitive-operations).
932+
933+
#### Why This Matters
934+
Calling `getAccessToken()` on every request can:
935+
- Increase latency by 50-200ms per request
936+
- Generate unnecessary load on Auth0's token endpoint
937+
- Risk hitting rate limits at scale
938+
- Waste computational resources
939+
940+
#### Recommended Pattern
941+
```typescript
942+
import { NextRequest, NextResponse } from "next/server";
943+
944+
import { auth0 } from "./lib/auth0";
945+
946+
// Define your refresh threshold (in seconds before expiry)
947+
const TOKEN_REFRESH_THRESHOLD = 5 * 60; // 5 minutes
948+
949+
export async function middleware(request: NextRequest) {
950+
const authRes = await auth0.middleware(request);
951+
952+
if (request.nextUrl.pathname.startsWith("/auth")) {
953+
return authRes;
954+
}
955+
956+
const session = await auth0.getSession(request);
957+
958+
if (!session) {
959+
return NextResponse.redirect(
960+
new URL("/auth/login", request.nextUrl.origin)
961+
);
962+
}
963+
964+
// Only refresh if token is expiring soon
965+
if (session.tokenSet?.expiresAt) {
966+
const expiresInSeconds = session.tokenSet.expiresAt - Date.now() / 1000;
967+
968+
if (expiresInSeconds < TOKEN_REFRESH_THRESHOLD) {
969+
try {
970+
await auth0.getAccessToken(request, authRes, { refresh: true });
971+
// Token refreshed and persisted via authRes
972+
} catch (error) {
973+
console.error("Token refresh failed:", error);
974+
return NextResponse.redirect(
975+
new URL("/auth/logout", request.nextUrl.origin)
976+
);
977+
}
978+
}
979+
}
980+
981+
return authRes;
982+
}
983+
984+
export const config = {
985+
matcher: [
986+
// Apply to protected routes only
987+
"/dashboard/:path*",
988+
"/api/:path*"
989+
]
990+
};
991+
```
992+
993+
> [!WARNING]
994+
> Server Components cannot persist token updates. Always refresh tokens in middleware (where cookies can be set) rather than in Server Components to ensure refreshed tokens are saved to the session.
995+
926996
### Multi-Resource Refresh Tokens (MRRT)
927997

928998
Multi-Resource Refresh Tokens allow using a single refresh token to obtain access tokens for multiple audiences, simplifying token management in applications that interact with multiple backend services.
@@ -1063,13 +1133,72 @@ const token = await auth0.getAccessToken({
10631133

10641134
For applications where an API call might be made very close to the token's expiration time, network latency can cause the token to expire before the API receives it. To prevent this race condition, you can implement a strategy to refresh the token proactively when it's within a certain buffer period of its expiration.
10651135

1136+
> [!NOTE]
1137+
> This pattern is designed for **per-request latency mitigation** immediately before critical API calls. For **centralized token management** in middleware that benefits all Server Components, see [Optimizing Token Refresh in Middleware](#optimizing-token-refresh-in-middleware).
1138+
10661139
The general approach is as follows:
10671140

10681141
1. Before making a sensitive API call, get the session and check the `expiresAt` timestamp from the `tokenSet`.
1069-
2. Determine if the token is within your desired buffer period (e.g., 30-90 seconds) of expiring.
1142+
2. Determine if the token is within your desired buffer period of expiring.
10701143
3. If it is, force a token refresh by calling `auth0.getAccessToken({ refresh: true })`.
10711144
4. Use the newly acquired access token for your API call.
10721145

1146+
**Example Implementation:**
1147+
1148+
```typescript
1149+
// app/api/critical-operation/route.ts
1150+
import { auth0 } from "@/lib/auth0";
1151+
import { NextResponse } from "next/server";
1152+
1153+
// Define your latency buffer (in seconds before expiry)
1154+
const LATENCY_BUFFER = 30; // 30 seconds
1155+
1156+
export async function POST() {
1157+
const session = await auth0.getSession();
1158+
1159+
if (!session?.tokenSet?.expiresAt) {
1160+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
1161+
}
1162+
1163+
const expiresInSeconds = session.tokenSet.expiresAt - Date.now() / 1000;
1164+
1165+
let token = session.tokenSet.accessToken;
1166+
1167+
// Refresh if token expires within the latency buffer
1168+
if (expiresInSeconds < LATENCY_BUFFER) {
1169+
const refreshed = await auth0.getAccessToken({ refresh: true });
1170+
token = refreshed.token;
1171+
}
1172+
1173+
// Make critical API call with fresh token
1174+
const response = await fetch("https://api.example.com/critical", {
1175+
method: "POST",
1176+
headers: {
1177+
Authorization: `Bearer ${token}`,
1178+
"Content-Type": "application/json"
1179+
},
1180+
body: JSON.stringify({ /* ... */ })
1181+
});
1182+
1183+
return NextResponse.json(await response.json());
1184+
}
1185+
```
1186+
1187+
**Buffer Configuration:**
1188+
1189+
Adjust the buffer based on your API's typical response time and network conditions:
1190+
1191+
```typescript
1192+
// Short API calls with fast network
1193+
const LATENCY_BUFFER = 15; // 15 seconds
1194+
1195+
// Standard configuration
1196+
const LATENCY_BUFFER = 30; // 30 seconds
1197+
1198+
// Slow APIs or unreliable network
1199+
const LATENCY_BUFFER = 90; // 90 seconds
1200+
```
1201+
10731202
This ensures that the token you send is guaranteed to be valid for at least the duration of the buffer, accounting for potential network delays.
10741203

10751204
> [!IMPORTANT]

0 commit comments

Comments
 (0)