@@ -22,14 +22,14 @@ export class HybridAuthGuard implements CanActivate {
22
22
return this . handleApiKeyAuth ( request , apiKey ) ;
23
23
}
24
24
25
- // Try JWT Bearer authentication (for internal frontend with JWT tokens )
25
+ // Try Bearer JWT token authentication (for internal frontend)
26
26
const authHeader = request . headers [ 'authorization' ] as string ;
27
27
if ( authHeader ?. startsWith ( 'Bearer ' ) ) {
28
28
return this . handleJwtAuth ( request , authHeader ) ;
29
29
}
30
30
31
31
throw new UnauthorizedException (
32
- 'Authentication required: Provide X-API-Key or Authorization Bearer JWT token' ,
32
+ 'Authentication required: Provide either X-API-Key or Bearer JWT token' ,
33
33
) ;
34
34
}
35
35
@@ -61,56 +61,56 @@ export class HybridAuthGuard implements CanActivate {
61
61
authHeader : string ,
62
62
) : Promise < boolean > {
63
63
try {
64
- // Extract JWT token from Bearer header
65
- const token = authHeader . replace ( 'Bearer ' , '' ) ;
64
+ // Extract token from " Bearer <token>"
65
+ const token = authHeader . substring ( 7 ) ;
66
66
67
- // Verify JWT using Better Auth JWKS endpoint
67
+ // Create JWKS for token verification using Better Auth endpoint
68
68
const JWKS = createRemoteJWKSet (
69
69
new URL ( `${ process . env . BETTER_AUTH_URL } /api/auth/jwks` ) ,
70
70
) ;
71
71
72
+ // Verify JWT token
72
73
const { payload } = await jwtVerify ( token , JWKS , {
73
74
issuer : process . env . BETTER_AUTH_URL ,
74
75
audience : process . env . BETTER_AUTH_URL ,
75
76
} ) ;
76
77
77
- // Extract user information from JWT payload
78
- // By default, Better Auth includes the entire user object in the payload
79
- const user = payload . user as { id : string ; email : string } ;
80
- if ( ! user ?. id ) {
78
+ // Extract user information from JWT payload (user data is directly in payload for Better Auth JWT)
79
+ const userId = payload . id as string ;
80
+ const userEmail = payload . email as string ;
81
+
82
+ if ( ! userId ) {
81
83
throw new UnauthorizedException (
82
84
'Invalid JWT payload: missing user information' ,
83
85
) ;
84
86
}
85
87
86
- // Require explicit organization ID via header for JWT auth
87
- const organizationId = request . headers [ 'x-organization-id' ] as string ;
88
- if ( ! organizationId ) {
88
+ // JWT authentication REQUIRES explicit X-Organization-Id header
89
+ const explicitOrgId = request . headers [ 'x-organization-id' ] as string ;
90
+
91
+ if ( ! explicitOrgId ) {
89
92
throw new UnauthorizedException (
90
- 'Organization context required: Provide X-Organization-Id header for JWT authentication' ,
93
+ 'Organization context required: X-Organization-Id header is mandatory for JWT authentication' ,
91
94
) ;
92
95
}
93
96
94
- // Critical: Verify user has access to the requested organization
95
- const hasAccess = await this . verifyUserOrgAccess ( user . id , organizationId ) ;
97
+ // Verify user has access to the requested organization
98
+ const hasAccess = await this . verifyUserOrgAccess ( userId , explicitOrgId ) ;
96
99
if ( ! hasAccess ) {
97
100
throw new UnauthorizedException (
98
- `User does not have access to organization: ${ organizationId } ` ,
101
+ `User does not have access to organization: ${ explicitOrgId } ` ,
99
102
) ;
100
103
}
101
104
102
105
// Set request context for JWT auth
103
- request . userId = user . id ;
104
- request . userEmail = user . email ;
105
- request . organizationId = organizationId ;
106
+ request . userId = userId ;
107
+ request . userEmail = userEmail ;
108
+ request . organizationId = explicitOrgId ;
106
109
request . authType = 'jwt' ;
107
110
request . isApiKey = false ;
108
111
109
112
return true ;
110
- } catch ( error : unknown ) {
111
- if ( error instanceof UnauthorizedException ) {
112
- throw error ;
113
- }
113
+ } catch ( error ) {
114
114
console . error ( 'JWT verification failed:' , error ) ;
115
115
throw new UnauthorizedException ( 'Invalid or expired JWT token' ) ;
116
116
}
0 commit comments