11const jwt = require ( 'jsonwebtoken' ) ;
22const { CONFIG_AUTH } = require ( '../config' ) ;
33
4- function verifyToken ( req , res , next ) {
5- // Allow users to set token
6- // eslint-disable-next-line dot-notation
7- let token = req . headers [ 'x-access-token' ] || req . headers [ 'authorization' ] ;
8- if ( token . startsWith ( 'Bearer ' ) ) {
9- // Remove Bearer from string
10- token = token . slice ( 7 , token . length ) ;
4+ const { RefreshToken, User } = require ( '../models' ) ;
5+ const crypto = require ( 'crypto' ) ;
6+ const AuthUtils = require ( '../../shared/authorizationUtils' ) ;
7+
8+ const SECRET = CONFIG_AUTH . TOKEN_SECRET ;
9+
10+ // Utility functions
11+
12+ function generateAccessToken ( user , auth_origin ) {
13+ return jwt . sign (
14+ {
15+ id : user . _id ,
16+ email : user . email ,
17+ role : user . accessLevel ,
18+ accessLevel : user . accessLevel ,
19+ auth_origin : auth_origin ,
20+ } ,
21+ SECRET ,
22+ { expiresIn : CONFIG_AUTH . ACCESS_TOKEN_EXPIRATION } ,
23+ ) ;
24+ }
25+
26+ function generateRefreshToken ( ) {
27+ return crypto . randomBytes ( 32 ) . toString ( 'hex' ) ;
28+ }
29+
30+ function hashToken ( token ) {
31+ return crypto . createHash ( 'sha256' ) . update ( token ) . digest ( 'hex' ) ;
32+ }
33+
34+ function getClientIp ( req ) {
35+ // Check X-Forwarded-For header (most common)
36+ const forwarded = req . headers [ 'x-forwarded-for' ] ;
37+ if ( forwarded ) {
38+ // Takes the first IP if there are multiple
39+ return forwarded . split ( ',' ) [ 0 ] . trim ( ) ;
1140 }
12- if ( ! token ) {
13- return res . sendStatus ( 403 ) ;
41+
42+ // Check other common headers
43+ return (
44+ req . headers [ 'x-real-ip' ] || req . connection . remoteAddress || req . socket . remoteAddress || req . ip
45+ ) ;
46+ }
47+
48+ async function authenticateAccessToken ( req , res , next ) {
49+ try {
50+ // Extract token from Authorization header
51+ let accessToken =
52+ req . cookies . token || req . headers [ 'x-access-token' ] || req . headers [ 'authorization' ] ;
53+
54+ if ( ! accessToken ) {
55+ return res . status ( 401 ) . json ( { error : 'Access token required' } ) ;
56+ }
57+
58+ if ( accessToken . startsWith ( 'Bearer ' ) ) {
59+ accessToken = accessToken . slice ( 7 , accessToken . length ) ;
60+ }
61+
62+ const decoded = jwt . verify ( accessToken , SECRET ) ;
63+ // Attach user info to request
64+ req . user = decoded ;
65+
66+ next ( ) ;
67+ } catch ( error ) {
68+ if ( error . name === 'TokenExpiredError' ) {
69+ return res . status ( 401 ) . json ( { error : 'Token expired' } ) ;
70+ }
71+
72+ if ( error . name === 'JsonWebTokenError' ) {
73+ return res . status ( 401 ) . json ( { error : 'Invalid token' } ) ;
74+ }
75+
76+ return res . status ( 401 ) . json ( { error : 'Authentication failed' } ) ;
1477 }
78+ }
79+
80+ // shorthand for authenticateAccessToken
81+ const authUser = authenticateAccessToken ;
1582
83+ async function authenticateRefreshToken ( req , res , next ) {
1684 try {
17- const decoded = jwt . verify ( token , CONFIG_AUTH . SECRET ) ;
18- res . cookie ( 'token' , token , { httpOnly : true } ) ;
19- req . userId = decoded . id ;
20- return next ( ) ;
21- } catch ( err ) {
22- return res . sendStatus ( 401 ) ;
85+ const refreshToken = req . cookies ?. refresh_token ;
86+
87+ if ( ! refreshToken ) {
88+ return res . status ( 401 ) . json ( { error : 'Refresh token required' } ) ;
89+ }
90+
91+ const tokenHash = hashToken ( refreshToken ) ;
92+
93+ const tokenDoc = await RefreshToken . findOne ( {
94+ hash : tokenHash ,
95+ expiresAt : { $gt : new Date ( ) } ,
96+ } ) ;
97+
98+ if ( ! tokenDoc ) {
99+ return res . status ( 401 ) . json ( { error : 'Invalid or expired refresh token' } ) ;
100+ }
101+
102+ const user = await User . findById ( tokenDoc . userId ) ;
103+ if ( ! user ) {
104+ return res . status ( 401 ) . json ( { error : 'User not found for this token' } ) ;
105+ }
106+
107+ // Attach user & refresh token to request for downstream handlers
108+ req . user = user ;
109+ req . refreshToken = tokenDoc ;
110+
111+ next ( ) ;
112+ } catch ( error ) {
113+ console . error ( 'Refresh token validation error:' , error ) ;
114+ return res . status ( 401 ) . json ( { error : 'Authentication failed' } ) ;
23115 }
24116}
25117
118+ function requireRole ( ...roles ) {
119+ return ( req , res , next ) => {
120+ if ( ! req . user ) {
121+ return res . status ( 401 ) . json ( { error : 'Authentication required' } ) ;
122+ }
123+
124+ if ( ! AuthUtils . hasAnyRole ( req . user , roles ) ) {
125+ return res . status ( 403 ) . json ( {
126+ error : 'Insufficient permissions' ,
127+ required_role : roles ,
128+ your_role : req . user . accessLevel ,
129+ } ) ;
130+ }
131+
132+ next ( ) ;
133+ } ;
134+ }
135+
136+ function requireMinimumRole ( role ) {
137+ return ( req , res , next ) => {
138+ if ( ! req . user ) {
139+ return res . status ( 401 ) . json ( { error : 'Authentication required' } ) ;
140+ }
141+
142+ const user = req . user ;
143+ if ( ! AuthUtils . hasMinimumRole ( user , role ) ) {
144+ return res . status ( 403 ) . json ( {
145+ error : 'Insufficient permissions' ,
146+ required_minimum_role : role ,
147+ your_role : req . user . accessLevel ,
148+ } ) ;
149+ }
150+ next ( ) ;
151+ } ;
152+ }
153+
26154function verifyCookie ( req , res , next ) {
27- jwt . verify ( req . cookies . token , CONFIG_AUTH . SECRET , ( err , decoded ) => {
155+ jwt . verify ( req . cookies . token , SECRET , ( err , decoded ) => {
28156 if ( err ) {
29157 return res . sendStatus ( 401 ) ;
30158 }
@@ -35,8 +163,15 @@ function verifyCookie(req, res, next) {
35163 } ) ;
36164}
37165
38- const AuthUtil = {
39- verifyToken,
166+ module . exports = {
167+ authenticateAccessToken,
168+ authUser,
169+ authenticateRefreshToken,
170+ requireRole,
171+ requireMinimumRole,
172+ generateAccessToken,
173+ generateRefreshToken,
174+ getClientIp,
175+ hashToken,
40176 verifyCookie,
41177} ;
42- module . exports = AuthUtil ;
0 commit comments