All files / middleware auth.ts

0% Statements 0/62
0% Branches 0/1
0% Functions 0/1
0% Lines 0/62

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81                                                                                                                                                                 
import { Context, Next } from 'koa';
import jwt from 'jsonwebtoken';
import { config } from '../config';
import { UnauthorizedError, AppError } from './errorHandler';
import { JwtPayload } from '../types';
import { safeParseInt } from '../utils/safe-parse';
 
export async function authMiddleware(ctx: Context, next: Next): Promise<void> {
  // 开发阶段默认跳过认证(除非显式设置 AUTH_ENABLED=true)
  const authEnabled = process.env.AUTH_ENABLED === 'true';
  if (!authEnabled) {
    ctx.state.user = {
      userId: '1',
      phone: 'test',
      memberLevel: 99,
    };
    await next();
    return;
  }
 
  const authHeader = ctx.get('Authorization');
 
  if (!authHeader) {
    throw new UnauthorizedError('缺少 Authorization 头');
  }
 
  const parts = authHeader.split(' ');
  if (parts.length !== 2 || parts[0] !== 'Bearer') {
    throw new UnauthorizedError('Authorization 格式错误');
  }
 
  const token = parts[1];
 
  try {
    const secret = config.jwt.secret;
    const payload = jwt.verify(token, secret) as JwtPayload;
    ctx.state.user = payload;
    await next();
  } catch (err: unknown) {
    const error = err as Error;
    if (error.name === 'TokenExpiredError') {
      throw new UnauthorizedError('Token 已过期');
    }
    if (error.name === 'JsonWebTokenError') {
      throw new UnauthorizedError('Token 无效');
    }
    throw new AppError('认证失败', 401, 401);
  }
}
 
// 可选认证(允许未登录访问,自动使用测试用户)
export async function optionalAuth(ctx: Context, next: Next): Promise<void> {
  const authHeader = ctx.get('Authorization');
 
  if (authHeader) {
    const parts = authHeader.split(' ');
    if (parts.length === 2 && parts[0] === 'Bearer') {
      try {
        const payload = jwt.verify(parts[1], config.jwt.secret) as JwtPayload;
        ctx.state.user = payload;
      } catch {
        // 忽略错误,使用测试用户
        ctx.state.user = {
          userId: '1',
          phone: 'test',
          memberLevel: 99, // 超级VIP
        };
      }
    }
  } else {
    // 没有认证头,使用测试用户(超级VIP)
    ctx.state.user = {
      userId: '1',
      phone: 'test',
      memberLevel: 99, // 超级VIP
    };
  }
 
  await next();
}