import type { Middleware, ResponseContext } from '../api/generated/default' import type { OAuth } from '../oauth/OAuth' import fetch from '../utils/fetch' /** * Middleware that transparently refreshes an expired access token on 401. * * When a response comes back with HTTP 401 the middleware delegates to * `OAuth.refreshAccessToken()` which checks for a refresh token, performs the * HTTP exchange, and updates the token store. On success the original request * is retried with the fresh access token. On failure the 401 propagates. * * When the client is unauthenticated (no refresh token stored) the middleware * short-circuits immediately, avoiding noisy error callbacks. */ export const addTokenRefreshMiddleware = ({ oauth }: { oauth: OAuth }): Middleware => { let refreshInFlight: Promise | null = null return { post: async (context: ResponseContext): Promise => { if (context.response.status !== 401) { return context.response } // Skip refresh when unauthenticated to avoid noisy error callbacks. if (!(await oauth.hasRefreshToken())) { return context.response } // Coalesce concurrent 401s into a single refresh call. if (!refreshInFlight) { refreshInFlight = oauth .refreshAccessToken() .catch(() => null) .finally(() => { refreshInFlight = null }) } const newAccessToken = await refreshInFlight if (!newAccessToken) { return context.response } // Retry the original request with the new access token. const retryInit: RequestInit = { ...context.init, headers: { ...((context.init.headers as Record) ?? {}), Authorization: `Bearer ${newAccessToken}` } } return fetch(context.url, retryInit) } } }