From 5cd13cd8eab6f133c0f5c74febe8e85cc5d5374a Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 4 Mar 2026 14:04:24 +0300 Subject: [PATCH] feat: add password hashing and JWT utils Made-with: Cursor --- src/utils/jwt.ts | 47 +++++++++++++++++++++++++++++++++++++++++++ src/utils/password.ts | 15 ++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/utils/jwt.ts create mode 100644 src/utils/password.ts diff --git a/src/utils/jwt.ts b/src/utils/jwt.ts new file mode 100644 index 0000000..da1479e --- /dev/null +++ b/src/utils/jwt.ts @@ -0,0 +1,47 @@ +import * as jose from 'jose'; +import { env } from '../config/env.js'; + +export interface AccessPayload { + sub: string; + email: string; + type: 'access'; +} + +export interface RefreshPayload { + sub: string; + sid: string; + type: 'refresh'; +} + +type JwtPayload = AccessPayload | RefreshPayload; + +const secret = new TextEncoder().encode(env.JWT_SECRET); + +export async function signAccessToken(payload: Omit): Promise { + return new jose.SignJWT({ ...payload, type: 'access' }) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setExpirationTime(env.JWT_ACCESS_TTL) + .sign(secret); +} + +export async function signRefreshToken(payload: Omit): Promise { + return new jose.SignJWT({ ...payload, type: 'refresh' }) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setExpirationTime(env.JWT_REFRESH_TTL) + .sign(secret); +} + +export async function verifyToken(token: string): Promise { + const { payload } = await jose.jwtVerify(token, secret); + return payload as unknown as JwtPayload; +} + +export function isAccessPayload(p: JwtPayload): p is AccessPayload { + return p.type === 'access'; +} + +export function isRefreshPayload(p: JwtPayload): p is RefreshPayload { + return p.type === 'refresh'; +} diff --git a/src/utils/password.ts b/src/utils/password.ts new file mode 100644 index 0000000..947b756 --- /dev/null +++ b/src/utils/password.ts @@ -0,0 +1,15 @@ +import * as argon2 from 'argon2'; + +const HASH_OPTIONS: argon2.Options = { + type: argon2.argon2id, + memoryCost: 19456, + timeCost: 2, +}; + +export async function hashPassword(plain: string): Promise { + return argon2.hash(plain, HASH_OPTIONS); +} + +export async function verifyPassword(hash: string, plain: string): Promise { + return argon2.verify(hash, plain); +}