All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 1m22s
86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
import crypto from 'node:crypto';
|
|
import type { NextFunction, Request, Response } from 'express';
|
|
import { createSession, createUser, deleteSession, findUserBySessionToken, findUserByUsername } from './db.ts';
|
|
import type { AuthenticatedUser } from './types.ts';
|
|
|
|
declare global {
|
|
namespace Express {
|
|
interface Request {
|
|
authUser?: AuthenticatedUser;
|
|
authToken?: string;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function hashPassword(password: string, salt = crypto.randomBytes(16).toString('hex')) {
|
|
const hash = crypto.scryptSync(password, salt, 64).toString('hex');
|
|
return { hash, salt };
|
|
}
|
|
|
|
export function verifyPassword(password: string, passwordHash: string, passwordSalt: string) {
|
|
const { hash } = hashPassword(password, passwordSalt);
|
|
return crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(passwordHash, 'hex'));
|
|
}
|
|
|
|
export function issueSession(userId: number) {
|
|
const token = crypto.randomBytes(32).toString('hex');
|
|
createSession(token, userId);
|
|
return token;
|
|
}
|
|
|
|
export function registerUser(username: string, password: string) {
|
|
if (findUserByUsername(username)) {
|
|
throw new Error('账号已存在');
|
|
}
|
|
const { hash, salt } = hashPassword(password);
|
|
const user = createUser(username, hash, salt);
|
|
if (!user) {
|
|
throw new Error('账号创建失败');
|
|
}
|
|
const token = issueSession(user.id);
|
|
return {
|
|
token,
|
|
user: { id: user.id, username: user.username },
|
|
};
|
|
}
|
|
|
|
export function loginUser(username: string, password: string) {
|
|
const user = findUserByUsername(username);
|
|
if (!user || !verifyPassword(password, user.password_hash, user.password_salt)) {
|
|
throw new Error('账号或密码错误');
|
|
}
|
|
const token = issueSession(user.id);
|
|
return {
|
|
token,
|
|
user: { id: user.id, username: user.username },
|
|
};
|
|
}
|
|
|
|
export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
|
const header = req.headers.authorization || '';
|
|
const token = header.startsWith('Bearer ') ? header.slice(7) : '';
|
|
if (!token) {
|
|
res.status(401).json({ error: '未登录' });
|
|
return;
|
|
}
|
|
|
|
const user = findUserBySessionToken(token);
|
|
if (!user) {
|
|
res.status(401).json({ error: '登录已失效' });
|
|
return;
|
|
}
|
|
|
|
req.authToken = token;
|
|
req.authUser = { id: user.id, username: user.username };
|
|
next();
|
|
}
|
|
|
|
export function resolveSession(token: string) {
|
|
const user = findUserBySessionToken(token);
|
|
return user ? { id: user.id, username: user.username } : null;
|
|
}
|
|
|
|
export function logoutSession(token: string) {
|
|
deleteSession(token);
|
|
}
|