fix: No token refresh or revocation mechanism~
This commit is contained in:
@@ -1,23 +1,90 @@
|
||||
const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5270'
|
||||
|
||||
let authToken = null
|
||||
let refreshToken = null
|
||||
let refreshPromise = null
|
||||
let onSessionExpired = null
|
||||
|
||||
export function setAuthToken(token) {
|
||||
authToken = token
|
||||
}
|
||||
|
||||
export function setRefreshToken(token) {
|
||||
refreshToken = token
|
||||
}
|
||||
|
||||
export function onSessionExpiredCallback(callback) {
|
||||
onSessionExpired = callback
|
||||
}
|
||||
|
||||
function authHeaders(extra = {}) {
|
||||
const headers = { 'Content-Type': 'application/json', ...extra }
|
||||
if (authToken) headers['Authorization'] = `Bearer ${authToken}`
|
||||
return headers
|
||||
}
|
||||
|
||||
async function attemptRefresh() {
|
||||
if (!refreshToken) return false
|
||||
if (refreshPromise) return refreshPromise
|
||||
|
||||
refreshPromise = (async () => {
|
||||
try {
|
||||
const res = await fetch(`${BASE_URL}/api/v1/auth/refresh`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ refreshToken }),
|
||||
})
|
||||
if (!res.ok) return false
|
||||
const envelope = await res.json()
|
||||
if (!envelope.success) return false
|
||||
|
||||
authToken = envelope.data.accessToken
|
||||
refreshToken = envelope.data.refreshToken
|
||||
if (onSessionExpired) {
|
||||
onSessionExpired({
|
||||
type: 'refreshed',
|
||||
accessToken: envelope.data.accessToken,
|
||||
refreshToken: envelope.data.refreshToken,
|
||||
expiresAt: envelope.data.expiresAt,
|
||||
})
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
} finally {
|
||||
refreshPromise = null
|
||||
}
|
||||
})()
|
||||
|
||||
return refreshPromise
|
||||
}
|
||||
|
||||
function handleSessionExpired() {
|
||||
if (onSessionExpired) {
|
||||
onSessionExpired({ type: 'expired' })
|
||||
}
|
||||
}
|
||||
|
||||
async function request(path, options = {}) {
|
||||
const res = await fetch(`${BASE_URL}${path}`, {
|
||||
headers: authHeaders(options.headers),
|
||||
...options,
|
||||
})
|
||||
if (res.status === 401) {
|
||||
const refreshed = await attemptRefresh()
|
||||
if (refreshed) {
|
||||
const retry = await fetch(`${BASE_URL}${path}`, {
|
||||
headers: authHeaders(options.headers),
|
||||
...options,
|
||||
})
|
||||
const retryEnvelope = await retry.json()
|
||||
if (!retry.ok || !retryEnvelope.success) {
|
||||
const msg = retryEnvelope.error?.message ?? `API ${retry.status}: ${path}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return retryEnvelope.data
|
||||
}
|
||||
handleSessionExpired()
|
||||
throw new Error('Session expired — please log in again.')
|
||||
}
|
||||
const envelope = await res.json()
|
||||
@@ -28,13 +95,26 @@ async function request(path, options = {}) {
|
||||
return envelope.data
|
||||
}
|
||||
|
||||
/** Returns null when the resource does not exist yet (HTTP 404 or empty optional payload). */
|
||||
async function requestOptional(path) {
|
||||
const res = await fetch(`${BASE_URL}${path}`, {
|
||||
headers: authHeaders(),
|
||||
})
|
||||
if (res.status === 404) return null
|
||||
if (res.status === 401) {
|
||||
const refreshed = await attemptRefresh()
|
||||
if (refreshed) {
|
||||
const retry = await fetch(`${BASE_URL}${path}`, {
|
||||
headers: authHeaders(),
|
||||
})
|
||||
if (retry.status === 404) return null
|
||||
const retryEnvelope = await retry.json()
|
||||
if (!retry.ok || !retryEnvelope.success) {
|
||||
const msg = retryEnvelope.error?.message ?? `API ${retry.status}: ${path}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return retryEnvelope.data ?? null
|
||||
}
|
||||
handleSessionExpired()
|
||||
throw new Error('Session expired — please log in again.')
|
||||
}
|
||||
const envelope = await res.json()
|
||||
@@ -66,6 +146,21 @@ export const api = {
|
||||
headers: authHeaders(),
|
||||
})
|
||||
if (res.status === 401) {
|
||||
const refreshed = await attemptRefresh()
|
||||
if (refreshed) {
|
||||
const retry = await fetch(`${BASE_URL}${path}`, {
|
||||
method: 'DELETE',
|
||||
headers: authHeaders(),
|
||||
})
|
||||
if (retry.status === 204) return null
|
||||
const retryEnvelope = await retry.json()
|
||||
if (!retry.ok || !retryEnvelope.success) {
|
||||
const msg = retryEnvelope.error?.message ?? `API ${retry.status}: ${path}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return retryEnvelope.data ?? null
|
||||
}
|
||||
handleSessionExpired()
|
||||
throw new Error('Session expired — please log in again.')
|
||||
}
|
||||
if (res.status === 204) return null
|
||||
|
||||
Reference in New Issue
Block a user