Files
nextgo/lib/backend-jwt-refresh.ts
Beyhan Oğur 9eb7aea821 first commit
2026-04-26 22:15:25 +03:00

117 lines
3.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { JWT } from 'next-auth/jwt'
import { encode } from 'next-auth/jwt'
const API_BASE = process.env.API_BASE_URL ?? 'http://localhost:8080'
/** Access token bitiminden önce yenile (ms) — backend 15 dk ise güvenli tampon */
const REFRESH_BUFFER_MS = 120_000
/** NextAuth JWT şifreli cookie ömrü (saniye) — varsayılan NextAuth ile uyumlu */
const JWT_COOKIE_MAX_AGE_SEC = 30 * 24 * 60 * 60
export function sessionCookieName(): string {
return isSecureSessionCookieEnabled()
? '__Secure-next-auth.session-token'
: 'next-auth.session-token'
}
export function getJwtExpMs(accessToken: string): number {
try {
const payloadPart = accessToken.split('.')[1]
if (!payloadPart) return Date.now()
const payload = JSON.parse(Buffer.from(payloadPart, 'base64url').toString('utf8')) as {
exp?: number
}
if (!payload.exp) return Date.now()
return payload.exp * 1000
} catch {
return Date.now()
}
}
/** Access süresi dolmak üzereyse veya dolmuşsa true */
export function shouldRefreshBackendToken(token: JWT | null): boolean {
if (!token?.refreshToken) return false
const exp = token.accessTokenExpires as number | undefined
if (!exp) return true
return Date.now() >= exp - REFRESH_BUFFER_MS
}
export async function fetchRefreshedBackendJwt(token: JWT): Promise<JWT | null> {
const refreshToken = token.refreshToken
if (!refreshToken) return null
try {
const res = await fetch(`${API_BASE}/api/v1/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken }),
})
if (!res.ok) return null
const data = (await res.json()) as { access?: unknown; refresh?: unknown }
// Backend: { "access": "...", "refresh": "..." } — Login_Register.md ile aynı
if (typeof data.access !== 'string' || !data.access) return null
const accessToken = data.access
const nextRefresh =
typeof data.refresh === 'string' && data.refresh.length > 0
? data.refresh
: refreshToken
return {
...token,
accessToken,
refreshToken: nextRefresh,
accessTokenExpires: getJwtExpMs(accessToken),
error: undefined,
}
} catch {
return null
}
}
export async function encodeSessionJwt(token: JWT): Promise<string> {
const secret = process.env.NEXTAUTH_SECRET ?? process.env.AUTH_SECRET
if (!secret) throw new Error('NEXTAUTH_SECRET eksik')
return encode({
token,
secret,
maxAge: JWT_COOKIE_MAX_AGE_SEC,
})
}
export function sessionCookieOptions(): {
httpOnly: boolean
secure: boolean
sameSite: 'lax'
path: string
maxAge: number
} {
const secure = isSecureSessionCookieEnabled()
return {
httpOnly: true,
secure,
sameSite: 'lax',
path: '/',
maxAge: JWT_COOKIE_MAX_AGE_SEC,
}
}
export function isSecureSessionCookieEnabled(): boolean {
return (
!!process.env.NEXTAUTH_URL?.startsWith('https://') ||
!!process.env.VERCEL
)
}
/** Server action / Route Handler: güncellenmiş JWTyi NextAuth session çerezine yazar */
export function applySessionCookie(
cookieStore: { set: (name: string, value: string, options: Record<string, unknown>) => void },
jwt: string
): void {
cookieStore.set(sessionCookieName(), jwt, sessionCookieOptions())
}