docs(auth): trim wordy refresh-dedup and retry comments
This commit is contained in:
parent
fa34e955c0
commit
95e4cf43b5
|
|
@ -35,11 +35,10 @@ export const removeToken = () => {
|
|||
localStorage.removeItem('desktopOAuthRefreshToken')
|
||||
}
|
||||
|
||||
// Coalesces concurrent refresh calls in the same tab into a single underlying
|
||||
// refresh. The Web Locks API below only exists in secure contexts, so on
|
||||
// insecure HTTP it falls back to an uncoordinated refresh — without this guard,
|
||||
// triggers that fire close together (focus, proactive timer, 401 interceptor)
|
||||
// each POST with the same single-use refresh cookie and all but one get a 401.
|
||||
// Coalesces concurrent same-tab refreshes into one POST. Web Locks (below) is
|
||||
// secure-context-only, so on insecure HTTP there's no cross-tab coordination —
|
||||
// without this guard, refreshes firing close together each spend the single-use
|
||||
// cookie and all but one get a 401.
|
||||
let inFlightRefresh: Promise<void> | null = null
|
||||
|
||||
/**
|
||||
|
|
@ -47,10 +46,8 @@ let inFlightRefresh: Promise<void> | null = null
|
|||
* The refresh token is sent automatically as an HttpOnly cookie.
|
||||
* The server rotates the cookie on every call.
|
||||
*
|
||||
* Concurrent calls in the same tab share one in-flight refresh. This is the
|
||||
* always-on primary dedup and works in every context (HTTP included). The Web
|
||||
* Locks API used inside is the secondary, cross-tab coordination layer that
|
||||
* only exists in secure contexts.
|
||||
* Same-tab concurrent calls share one in-flight refresh (always-on dedup); the
|
||||
* Web Locks API inside adds cross-tab coordination only in secure contexts.
|
||||
*/
|
||||
export async function refreshToken(persist: boolean): Promise<void> {
|
||||
if (inFlightRefresh) {
|
||||
|
|
|
|||
|
|
@ -55,12 +55,9 @@ function redirectToSpecifiedProvider() {
|
|||
}
|
||||
}
|
||||
|
||||
// Refreshes the token, retrying exactly once if the first attempt fails.
|
||||
// After a lost refresh race (common on insecure HTTP without Web Locks, or
|
||||
// behind a proxy that delays the rotated Set-Cookie), the browser's cookie is
|
||||
// already the rotated, valid one — a second attempt then succeeds. Only when
|
||||
// both attempts fail is the session genuinely dead. Exactly one retry, so a
|
||||
// truly dead session still logs out without looping.
|
||||
// A race-loser's refresh fails but the rotated cookie is already valid, so a
|
||||
// second attempt succeeds — recovering what would otherwise be a spurious
|
||||
// logout. Exactly one retry: a genuinely dead session still logs out, no loop.
|
||||
async function refreshTokenWithRetry(persist: boolean): Promise<void> {
|
||||
try {
|
||||
await refreshToken(persist)
|
||||
|
|
@ -525,8 +522,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||
const response = await HTTP.post('user/token')
|
||||
saveToken(response.data.token, false)
|
||||
} else {
|
||||
// User sessions renew via the refresh-token cookie. Retry once so
|
||||
// a lost refresh race doesn't spuriously log the user out.
|
||||
// User sessions renew via the refresh-token cookie.
|
||||
await refreshTokenWithRetry(true)
|
||||
}
|
||||
await checkAuth()
|
||||
|
|
|
|||
Loading…
Reference in New Issue