/** * Checks what a verified token says before it is trusted. It types the payloads * of session and password reset tokens, and `lib/jwt.ts` runs their guards when * it verifies one. * * @module */ import { isRole, type Role } from "../../shared/user.ts"; /** * Signed into a session token by `createJWT`, and returned by `authenticate` * to the auth middleware as `ctx.state.user`. By then `role` is the user's live * role, while `username` is still the one at issue time. */ export interface AuthPayload { userId: string; username: string; role: Role; /** Seconds since the epoch. */ exp: number; /** Seconds since the epoch, compared with `users.tokens_valid_after`. */ iat?: number; } /** * Checks whether `obj` is an {@link AuthPayload}. A media or reset token's * payload, which has a `purpose`, never is. */ export function isAuthPayload(obj: unknown): obj is AuthPayload { return !!obj && typeof obj === "object" && // A media or reset token must not pass for a session token. !("purpose" in obj) && "userId" in obj && typeof obj.userId === "string" && "username" in obj && typeof obj.username === "string" && "role" in obj && isRole(obj.role) && "exp" in obj && typeof obj.exp === "number"; } /** * Signed into a reset link's token by `createPasswordResetToken`, and returned * by `verifyPasswordResetToken` to `resetPassword`. */ export interface PasswordResetPayload { purpose: "password-reset"; userId: string; /** Seconds since the epoch. */ exp: number; } /** * Checks whether `obj` is a {@link PasswordResetPayload}. It doesn't check * `exp`, which `jwtVerify` has already enforced. */ export function isPasswordResetPayload( obj: unknown, ): obj is PasswordResetPayload { return !!obj && typeof obj === "object" && "purpose" in obj && (obj as Record).purpose === "password-reset" && "userId" in obj && typeof (obj as Record).userId === "string"; }