1+ import path from 'path' ;
2+ import fs from 'fs-extra' ;
3+ import crypto from 'crypto' ;
4+ import { AuthConfig } from '../types/user.js' ;
5+ import { getDbClient } from './db.js' ;
6+ import { Request } from 'express' ;
7+
8+ export const getAuthConfig = ( ) : AuthConfig => {
9+ const envPath = path . join ( process . cwd ( ) , 'env.json' ) ;
10+ let envData : any = { } ;
11+
12+ if ( fs . existsSync ( envPath ) ) {
13+ envData = JSON . parse ( fs . readFileSync ( envPath , 'utf-8' ) ) ;
14+ }
15+
16+ const authConfig = envData . auth || { } ;
17+
18+ return {
19+ jwtSecret : authConfig . jwtSecret || process . env . JWT_SECRET || crypto . randomBytes ( 64 ) . toString ( 'hex' ) ,
20+ jwtExpiresIn : authConfig . jwtExpiresIn || '1h' ,
21+ refreshTokenExpiresIn : authConfig . refreshTokenExpiresIn || '7d' ,
22+ bcryptRounds : authConfig . bcryptRounds || 12 ,
23+ maxLoginAttempts : authConfig . maxLoginAttempts || 5 ,
24+ lockoutDuration : authConfig . lockoutDuration || 15 ,
25+ maxSessionsPerUser : authConfig . maxSessionsPerUser || 5
26+ } ;
27+ } ;
28+
29+
30+ /**
31+ * Hash a token for secure storage
32+ */
33+ export const hashToken = ( token : string ) : string => {
34+ return crypto . createHash ( 'sha256' ) . update ( token ) . digest ( 'hex' ) ;
35+ } ;
36+
37+
38+ /**
39+ * Get client IP address
40+ */
41+ export const getClientIp = ( req : Request ) : string => {
42+ const forwarded = req . headers [ 'x-forwarded-for' ] ;
43+ if ( typeof forwarded === 'string' ) {
44+ return forwarded . split ( ',' ) [ 0 ] . trim ( ) ;
45+ }
46+ return req . ip || req . socket . remoteAddress || 'unknown' ;
47+ } ;
48+
49+ /**
50+ * Log audit event
51+ */
52+ export const logAuditEvent = async (
53+ userId : string | null ,
54+ action : string ,
55+ req : Request ,
56+ details ?: any
57+ ) : Promise < void > => {
58+ try {
59+ const sql = getDbClient ( ) ;
60+ await sql `
61+ INSERT INTO auth_audit_log (user_id, action, ip_address, user_agent, details)
62+ VALUES (${ userId } , ${ action } , ${ getClientIp ( req ) } , ${ req . headers [ 'user-agent' ] || null } , ${ details ? JSON . stringify ( details ) : null } )
63+ ` ;
64+ } catch ( error ) {
65+ console . error ( 'Failed to log audit event:' , error ) ;
66+ }
67+ } ;
68+
69+ /**
70+ * Parse duration string to milliseconds
71+ */
72+ export const parseDuration = ( duration : string ) : number => {
73+ const match = duration . match ( / ^ ( \d + ) ( [ s m h d ] ) $ / ) ;
74+ if ( ! match ) return 3600000 ; // default 1 hour
75+
76+ const value = parseInt ( match [ 1 ] ) ;
77+ const unit = match [ 2 ] ;
78+
79+ switch ( unit ) {
80+ case 's' : return value * 1000 ;
81+ case 'm' : return value * 60 * 1000 ;
82+ case 'h' : return value * 60 * 60 * 1000 ;
83+ case 'd' : return value * 24 * 60 * 60 * 1000 ;
84+ default : return 3600000 ;
85+ }
86+ } ;
0 commit comments