diff --git a/www/app/(auth)/userInfo.tsx b/www/app/(auth)/userInfo.tsx
index 24b8f15e..bf6a5b62 100644
--- a/www/app/(auth)/userInfo.tsx
+++ b/www/app/(auth)/userInfo.tsx
@@ -8,9 +8,10 @@ export default function UserInfo() {
const status = auth.status;
const isLoading = status === "loading";
const isAuthenticated = status === "authenticated";
+ const isRefreshing = status === "refreshing";
return isLoading ? (
- ) : !isAuthenticated ? (
+ ) : !isAuthenticated && !isRefreshing ? (
{
- console.warn(
- "illegal state: authenticated but have no session/or access token. ignoring",
- );
- return { status: "unauthenticated" as const };
- })()
- : { status: "unauthenticated" as const }),
+ : status === "loading" && customSession
+ ? { status: "refreshing" as const }
+ : status === "authenticated" && customSession?.accessToken
+ ? {
+ status,
+ accessToken: customSession.accessToken,
+ accessTokenExpires: customSession.accessTokenExpires,
+ user: customSession.user,
+ }
+ : status === "authenticated" && !customSession?.accessToken
+ ? (() => {
+ console.warn(
+ "illegal state: authenticated but have no session/or access token. ignoring",
+ );
+ return { status: "unauthenticated" as const };
+ })()
+ : { status: "unauthenticated" as const }),
update,
signIn,
signOut,
diff --git a/www/app/lib/SessionAutoRefresh.tsx b/www/app/lib/SessionAutoRefresh.tsx
index 9f4e2658..1b7b05f0 100644
--- a/www/app/lib/SessionAutoRefresh.tsx
+++ b/www/app/lib/SessionAutoRefresh.tsx
@@ -18,15 +18,17 @@ export function SessionAutoRefresh({
const accessTokenExpires =
auth.status === "authenticated" ? auth.accessTokenExpires : null;
+ const refreshIntervalMs = refreshInterval * 1000;
+
useEffect(() => {
const interval = setInterval(() => {
- if (accessTokenExpires) {
+ if (accessTokenExpires !== null) {
const timeLeft = accessTokenExpires - Date.now();
- if (timeLeft < refreshInterval * 1000) {
+ if (timeLeft < refreshIntervalMs) {
auth.update();
}
}
- }, refreshInterval * 1000);
+ }, refreshIntervalMs);
return () => clearInterval(interval);
}, [accessTokenExpires, refreshInterval, auth.update]);
diff --git a/www/app/lib/auth.ts b/www/app/lib/auth.ts
index 51a7e909..1d2cc953 100644
--- a/www/app/lib/auth.ts
+++ b/www/app/lib/auth.ts
@@ -8,16 +8,14 @@ import {
parseMaybeNonEmptyString,
} from "./utils";
-const PRETIMEOUT = 60; // seconds before token expires to refresh it
+const PRETIMEOUT = 600;
-// Simple in-memory cache for tokens (in production, consider using a proper cache solution)
const tokenCache = new Map<
string,
{ token: JWTWithAccessToken; timestamp: number }
>();
const TOKEN_CACHE_TTL = 60 * 60 * 24 * 30 * 1000; // 30 days in milliseconds
-// Simple lock mechanism to prevent concurrent token refreshes
const refreshLocks = new Map>();
const CLIENT_ID = assertExistsAndNonEmptyString(
@@ -100,32 +98,25 @@ async function lockedRefreshAccessToken(
): Promise {
const lockKey = `${token.sub}-refresh`;
- // Check if there's already a refresh in progress
const existingRefresh = refreshLocks.get(lockKey);
if (existingRefresh) {
- return existingRefresh;
+ return await existingRefresh;
}
- // Create a new refresh promise
const refreshPromise = (async () => {
try {
- // Check cache for recent token
const cached = tokenCache.get(`token:${token.sub}`);
if (cached) {
- // Clean up old cache entries
if (Date.now() - cached.timestamp > TOKEN_CACHE_TTL) {
tokenCache.delete(`token:${token.sub}`);
} else if (Date.now() < cached.token.accessTokenExpires) {
- // Token is still valid
return cached.token;
}
}
- // Refresh the token
const currentToken = cached?.token || (token as JWTWithAccessToken);
const newToken = await refreshAccessToken(currentToken);
- // Update cache
tokenCache.set(`token:${token.sub}`, {
token: newToken,
timestamp: Date.now(),
@@ -133,7 +124,6 @@ async function lockedRefreshAccessToken(
return newToken;
} finally {
- // Clean up the lock after a short delay
setTimeout(() => refreshLocks.delete(lockKey), 100);
}
})();