File size: 1,151 Bytes
87cb242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import jwt from 'jsonwebtoken';
import { CONFIG } from '../config';

export interface JWTPayload {
  uid: string;
  iat?: number;
  exp?: number;
}

/**
 * Verifies a JWT and returns the decoded payload.
 * Throws if the token is invalid or expired.
 * The uid is ALWAYS extracted from the verified token — never from client input.
 */
export function verifyToken(token: string): JWTPayload {
  const decoded = jwt.verify(token, CONFIG.JWT_SECRET) as JWTPayload;

  if (!decoded.uid || typeof decoded.uid !== 'string') {
    throw new Error('JWT payload missing uid field');
  }

  // Sanitize uid to prevent Redis key injection
  if (!/^[a-zA-Z0-9_-]{1,64}$/.test(decoded.uid)) {
    throw new Error('JWT uid contains invalid characters');
  }

  return decoded;
}

/** Extract Bearer token from Upgrade/Authorization header or query param */
export function extractToken(
  authHeader: string | undefined,
  queryToken: string | undefined
): string {
  if (authHeader?.startsWith('Bearer ')) {
    return authHeader.slice(7).trim();
  }
  if (queryToken) {
    return queryToken.trim();
  }
  throw new Error('No authorization token provided');
}