diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-13 23:46:16 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-13 23:46:16 +0900 |
| commit | 7258ca81812a24edd382438ce6e9ebc538549427 (patch) | |
| tree | 9bbc034be62777a2412d871211188268d7c56da4 /frontend/app/auth.ts | |
| parent | 7757f26295cbf19c4d6fa068e2cb6bdc2589d01a (diff) | |
| download | phperkaigi-2026-albatross-7258ca81812a24edd382438ce6e9ebc538549427.tar.gz phperkaigi-2026-albatross-7258ca81812a24edd382438ce6e9ebc538549427.tar.zst phperkaigi-2026-albatross-7258ca81812a24edd382438ce6e9ebc538549427.zip | |
feat(auth): store JWT in HTTP-only cookie instead of JS-accessible cookie
Prevent XSS-based token theft by making the JWT inaccessible to
JavaScript. The backend now sets/clears the cookie via Set-Cookie
headers, and the frontend retrieves user info from /api/me instead
of decoding the JWT directly.
- Add JWTCookieMiddleware to parse cookie and inject claims into context
- Add /me and /logout endpoints to OpenAPI spec and handlers
- Update PostLogin to return user object + Set-Cookie header
- Replace Authorization header auth with cookie-based auth throughout
- Rewrite frontend auth to use /api/me instead of jwt-decode
- Remove jwt-decode dependency
- Configure CORS with credentials for local dev
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'frontend/app/auth.ts')
| -rw-r--r-- | frontend/app/auth.ts | 42 |
1 files changed, 0 insertions, 42 deletions
diff --git a/frontend/app/auth.ts b/frontend/app/auth.ts index 7a3d10d..769ac27 100644 --- a/frontend/app/auth.ts +++ b/frontend/app/auth.ts @@ -1,45 +1,3 @@ -import { type JwtPayload, jwtDecode } from "jwt-decode"; import type { components } from "./api/schema"; export type User = components["schemas"]["User"]; - -const COOKIE_NAME = "albatross_token"; - -export function getToken(): string | null { - const match = document.cookie - .split("; ") - .find((row) => row.startsWith(`${COOKIE_NAME}=`)); - if (!match) return null; - return match.split("=").slice(1).join("="); -} - -export function setToken(token: string): void { - document.cookie = `${COOKIE_NAME}=${token}; path=/; SameSite=Lax`; -} - -export function clearToken(): void { - document.cookie = `${COOKIE_NAME}=; path=/; SameSite=Lax; max-age=0`; -} - -export function getUserFromToken(): User | null { - const token = getToken(); - if (!token) return null; - try { - return jwtDecode<User & JwtPayload>(token); - } catch { - return null; - } -} - -export function isTokenExpired(): boolean { - const token = getToken(); - if (!token) return true; - try { - const decoded = jwtDecode<JwtPayload>(token); - if (decoded.exp == null) return false; - // If the token will expire in less than an hour, treat it as expired. - return new Date((decoded.exp - 3600) * 1000) < new Date(); - } catch { - return true; - } -} |
