Implementation:Langgenius Dify Refresh Token
| Knowledge Sources | |
|---|---|
| Domains | Frontend, API_Service |
| Last Updated | 2026-02-12 07:00 GMT |
Overview
Cross-tab token refresh coordination module that ensures only one browser tab refreshes the authentication token at a time, preventing race conditions and infinite refresh loops.
Description
refresh-token.ts implements a cross-tab synchronization mechanism for refreshing authentication tokens. When a 401 response is received, this module ensures that only a single browser tab performs the actual token refresh request, while other tabs wait for the result. It uses localStorage as a cross-tab communication channel with two keys: is_other_tab_refreshing (a lock flag set to "1" during refresh) and last_refresh_time (timestamp to detect stale locks). The getNewAccessToken function checks if another tab is already refreshing (via the localStorage flag and a local isRefreshing boolean) and either waits (polling every 1 second via waitUntilTokenRefreshed) or proceeds to refresh. The actual refresh is performed using fetchWithRetry with a direct globalThis.fetch call to /refresh-token (deliberately not using baseFetch to avoid infinite 401 loops). The refresh request uses credentials: "include" to send cookies containing the refresh token. The releaseRefreshLock function cleans up all locks and event listeners. The exported refreshAccessTokenOrReLogin function wraps the refresh in a Promise.race with a timeout, ensuring the process does not hang indefinitely. It also registers a beforeunload event listener to release the lock if the tab is closed during refresh.
Usage
This module is consumed internally by base.ts when a 401 response is encountered during any API call. It should not be called directly from application components. The refreshAccessTokenOrReLogin function is invoked with a timeout value (default 100000ms) and either resolves (allowing the failed request to be retried) or rejects (triggering a redirect to login).
Code Reference
Source Location
- Repository: Langgenius_Dify
- File: web/service/refresh-token.ts
- Lines: 1-88
Signature
// Main exported function: refresh token or timeout
export async function refreshAccessTokenOrReLogin(timeout: number): Promise<void>
// Internal functions (not exported)
function waitUntilTokenRefreshed(): Promise<void>
function isRefreshingSignAvailable(delta: number): boolean
async function getNewAccessToken(timeout: number): Promise<void>
function releaseRefreshLock(): void
Import
import { refreshAccessTokenOrReLogin } from '@/service/refresh-token'
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| timeout | number | Yes | Maximum time in milliseconds to wait for the token refresh before rejecting with a timeout error |
Outputs
| Name | Type | Description |
|---|---|---|
| Promise<void> | Promise<void> | Resolves when token refresh succeeds (new cookie set by server); rejects on timeout, 401 from refresh endpoint, or network error |
Usage Examples
Internal Usage in base.ts
import { refreshAccessTokenOrReLogin } from './refresh-token'
const TIME_OUT = 100000
// When a 401 is received from any API call:
const [refreshErr] = await asyncRunSafe(refreshAccessTokenOrReLogin(TIME_OUT))
if (refreshErr === null) {
// Token refreshed successfully, retry the original request
return baseFetch<T>(url, options, otherOptionsForBaseFetch)
}
// Refresh failed, redirect to login
jumpTo(loginUrl)
Cross-Tab Behavior
// Tab A: Encounters 401, starts refresh
// - Sets localStorage 'is_other_tab_refreshing' = '1'
// - Sets localStorage 'last_refresh_time' = timestamp
// - POSTs to /refresh-token with credentials
// Tab B: Encounters 401 while Tab A is refreshing
// - Detects localStorage lock is set and within timeout
// - Polls every 1000ms via waitUntilTokenRefreshed()
// - Resolves when Tab A clears the lock
// Tab A: Refresh completes
// - Clears localStorage lock
// - Tab B's poll detects clearance and resolves
// - Both tabs can now make authenticated requests
Related Pages
- Principle:Langgenius_Dify_Authentication_Flow
- Langgenius_Dify_Service_Base - Calls refreshAccessTokenOrReLogin on 401 responses
- Langgenius_Dify_Service_Fetch - Low-level fetch layer that triggers 401 error handling