fix(web): skip GetCurrentUser on init when no token is stored

When no token exists in sessionStorage, AuthContext.initialize() was
still calling GetCurrentUser, triggering the auth interceptor to attempt
RefreshToken and retry — producing a burst of 5+ auth API calls in under
a second that reverse proxies with rate limiting (e.g. CrowdSec) flag as
brute force.

Add hasStoredToken() to auth-state and bail out of initialize() early
when there is definitively no session to restore. The refresh flow for
expired tokens is preserved since hasStoredToken() checks for presence
regardless of expiry.

Fixes #5647
This commit is contained in:
Steven 2026-02-22 13:40:34 +08:00
parent b8bca6bacf
commit 4aaebc507e
2 changed files with 29 additions and 1 deletions

View File

@ -62,6 +62,19 @@ export const isTokenExpired = (bufferMs: number = 30000): boolean => {
return new Date() >= new Date(tokenExpiresAt.getTime() - bufferMs);
};
// Returns true if a token exists in sessionStorage, even if it is expired.
// Used to decide whether to attempt GetCurrentUser on app init — if no token
// was ever stored, the user is definitively not logged in and there is nothing
// to refresh, so we can skip the network round-trip entirely.
export const hasStoredToken = (): boolean => {
if (accessToken) return true;
try {
return !!sessionStorage.getItem(SESSION_TOKEN_KEY);
} catch {
return false;
}
};
export const clearAccessToken = (): void => {
accessToken = null;
tokenExpiresAt = null;

View File

@ -1,6 +1,6 @@
import { useQueryClient } from "@tanstack/react-query";
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from "react";
import { clearAccessToken } from "@/auth-state";
import { clearAccessToken, hasStoredToken } from "@/auth-state";
import { authServiceClient, shortcutServiceClient, userServiceClient } from "@/connect";
import { userKeys } from "@/hooks/useUserQueries";
import type { Shortcut } from "@/types/proto/api/v1/shortcut_service_pb";
@ -52,6 +52,21 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const initialize = useCallback(async () => {
setState((prev) => ({ ...prev, isLoading: true }));
// If there is no stored token at all, the user is not authenticated.
// Skip the network call — there is nothing to refresh and no session to restore.
if (!hasStoredToken()) {
setState({
currentUser: undefined,
userGeneralSetting: undefined,
userWebhooksSetting: undefined,
shortcuts: [],
isInitialized: true,
isLoading: false,
});
return;
}
try {
const { user: currentUser } = await authServiceClient.getCurrentUser({});