Author: Heesun Lee
Last updated: Aug 19, 2025
accessToken
: short-lived token (~= 15m). keeps exposure low if it leaks
export type AccessPayload = JWTPayload & { sub: string; type: 'access'; role: Role };
refreshToken
: long-lived token (~= 7d). stored in HttpOnly cookie, so not exposed to JS
→ used to re-issue access silently
export type RefreshPayload = JWTPayload & { sub: string; type: 'refresh'; jti: string; role?: Role };
Session table
with jti
jti
: JWT(JSON Web Token) ID. unique identifier string attached to each token.
const jti = crypto.randomUUID();
session table with jti
allows server-side invalidation → logout, password reset, or force revoke.
revokedAt: DateTime?
: soft-revoke timestamp. When set, the refresh session is invalid even if expiresAt
is in the future (used on rotation & logout).
Cookie Storage
: HttpOnly + Secure + SameSite
Token rotation
: issuing a new refresh token each time and revoking the old one
/profile
/admin
→ deny if no role: ADMIN