feat: add password hashing and JWT utils

Made-with: Cursor
This commit is contained in:
Anton
2026-03-04 14:04:24 +03:00
parent 41b4f48a0f
commit 5cd13cd8ea
2 changed files with 62 additions and 0 deletions

47
src/utils/jwt.ts Normal file
View File

@@ -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<AccessPayload, 'type'>): Promise<string> {
return new jose.SignJWT({ ...payload, type: 'access' })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime(env.JWT_ACCESS_TTL)
.sign(secret);
}
export async function signRefreshToken(payload: Omit<RefreshPayload, 'type'>): Promise<string> {
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<JwtPayload> {
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';
}

15
src/utils/password.ts Normal file
View File

@@ -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<string> {
return argon2.hash(plain, HASH_OPTIONS);
}
export async function verifyPassword(hash: string, plain: string): Promise<boolean> {
return argon2.verify(hash, plain);
}