fix: safari cookie issue
This commit is contained in:
@@ -44,6 +44,13 @@ export const api = createTRPCProxyClient<AppRouter>({
|
||||
headers: () => {
|
||||
const csrfToken = getCSRFToken();
|
||||
return csrfToken ? { "x-csrf-token": csrfToken } : {};
|
||||
},
|
||||
// CRITICAL FOR SAFARI: Ensure cookies are sent and received
|
||||
fetch(url, options) {
|
||||
return fetch(url, {
|
||||
...options,
|
||||
credentials: "include" // Safari requires this for cookie handling
|
||||
});
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
@@ -23,6 +23,7 @@ class TokenRefreshManager {
|
||||
private onlineHandler: (() => void) | null = null;
|
||||
private focusHandler: (() => void) | null = null;
|
||||
private lastRefreshTime: number | null = null;
|
||||
private lastCheckTime: number = 0;
|
||||
|
||||
/**
|
||||
* Start monitoring and auto-refresh
|
||||
@@ -73,7 +74,18 @@ class TokenRefreshManager {
|
||||
window.addEventListener("online", this.onlineHandler);
|
||||
|
||||
// Re-check on window focus (device was asleep or user switched apps)
|
||||
// Debounce to prevent Safari from firing this too frequently
|
||||
this.focusHandler = () => {
|
||||
const now = Date.now();
|
||||
const timeSinceLastCheck = now - this.lastCheckTime;
|
||||
|
||||
// Debounce: only check if last check was >1s ago (prevents Safari spam)
|
||||
if (timeSinceLastCheck < 1000) {
|
||||
console.log("[Token Refresh] Window focused but debouncing (Safari)");
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastCheckTime = now;
|
||||
console.log("[Token Refresh] Window focused, checking token status");
|
||||
this.checkAndRefreshIfNeeded();
|
||||
};
|
||||
|
||||
@@ -1660,17 +1660,33 @@ export const authRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4: Refresh CSRF token
|
||||
// Step 4: Force response headers to be sent immediately
|
||||
// This is critical for Safari to receive the new session cookies
|
||||
// Safari is very strict about cookie updates from fetch responses
|
||||
try {
|
||||
const headers = event.node?.res?.getHeaders?.() || {};
|
||||
console.log(
|
||||
"[Token Refresh] Response headers set:",
|
||||
Object.keys(headers)
|
||||
);
|
||||
} catch (e) {
|
||||
// Headers already sent or not available - that's OK
|
||||
}
|
||||
|
||||
// Step 5: Refresh CSRF token
|
||||
setCSRFToken(event);
|
||||
|
||||
// Step 5: Opportunistic cleanup (serverless-friendly)
|
||||
// Step 6: Opportunistic cleanup (serverless-friendly)
|
||||
import("~/server/token-cleanup")
|
||||
.then((module) => module.opportunisticCleanup())
|
||||
.catch((err) => console.error("Opportunistic cleanup failed:", err));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Token refreshed successfully"
|
||||
message: "Token refreshed successfully",
|
||||
// Return new session ID for Safari fallback
|
||||
// If Safari doesn't apply cookies, client can use this to restore
|
||||
sessionId: newSession.sessionId
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Token refresh error:", error);
|
||||
|
||||
@@ -232,7 +232,7 @@ export async function createAuthSession(
|
||||
// Explicitly seal/flush the session to ensure cookie is written
|
||||
// This is important in serverless environments where response might stream early
|
||||
const { sealSession } = await import("vinxi/http");
|
||||
sealSession(event, configWithMaxAge);
|
||||
await sealSession(event, configWithMaxAge);
|
||||
|
||||
console.log("[Session Create] Session sealed");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user