import 'server-only'; /** * JWT의 payload(두 번째 세그먼트)만 파싱한다 — 서명 검증은 하지 않는다. * * 백엔드(edupay-backend)가 발급한 accessToken/refreshToken은 백엔드의 서명 시크릿으로 * 서명되어 있고 우리는 그 시크릿을 갖고 있지 않아 서명 검증 자체가 불가능하다. 따라서 이 * 함수가 반환하는 값은 "신뢰된 인가 판단"에 직접 쓰지 않는다 — 여기서 읽은 클레임은 우리 * 자체 HMAC 세션 토큰(lib/auth/session-token.ts)에 실어 서명한 뒤에만 위조 불가능한 형태로 * 라우트 가드에 쓰인다. 이 함수는 백엔드가 방금 응답한 토큰에서 표시용 정보(로그인 아이디, * 역할 코드 등)를 꺼내는 매핑 용도로만 사용한다. * * 형식 오류·JSON 파싱 실패 등 어떤 이유로든 실패하면 예외를 던지지 않고 null을 반환한다 * (fail-closed) — 호출부가 매번 try/catch를 두지 않아도 되게 한다. */ export function decodeJwtPayload(token: string): T | null { const segments = token.split('.'); if (segments.length !== 3) { return null; } try { const json = Buffer.from(segments[1], 'base64url').toString('utf8'); return JSON.parse(json) as T; } catch { return null; } }