@dismissible/nestjs-jwt-auth-hook 2.0.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/package.json +5 -5
- package/src/jwt-auth-hook.config.d.ts +7 -1
- package/src/jwt-auth-hook.config.js.map +1 -1
- package/src/jwt-auth.hook.d.ts +10 -1
- package/src/jwt-auth.hook.js +69 -15
- package/src/jwt-auth.hook.js.map +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,27 @@ export class AppModule {}
|
|
|
105
105
|
|
|
106
106
|
\*\* `userIdMatchRegex` is required when `userIdMatchType` is `regex`.
|
|
107
107
|
|
|
108
|
+
### User ID Match Types
|
|
109
|
+
|
|
110
|
+
The `userIdMatchType` option controls how the user ID from the JWT token claim is compared to the `userId` in the request URL path:
|
|
111
|
+
|
|
112
|
+
- **`exact`** (default): The token claim value must exactly match the URL user ID.
|
|
113
|
+
- **`substring`**: The URL user ID must be contained within the token claim value.
|
|
114
|
+
- **`regex`**: The regex pattern is applied to the token claim value. If a capture group is present, the **first capture group** is extracted and compared to the URL user ID. If no capture group is present, the **full match** is used.
|
|
115
|
+
|
|
116
|
+
#### Regex Matching Examples
|
|
117
|
+
|
|
118
|
+
For a token with `sub` claim value `FfXHGud25MDOUGjQyBZnCWkkWlFDCS0Y@clients`:
|
|
119
|
+
|
|
120
|
+
| Pattern | Extracted Value | URL userId | Result |
|
|
121
|
+
| ----------------- | ---------------------------------- | ---------------------------------- | -------- |
|
|
122
|
+
| `^(.+)@clients$` | `FfXHGud25MDOUGjQyBZnCWkkWlFDCS0Y` | `FfXHGud25MDOUGjQyBZnCWkkWlFDCS0Y` | Match |
|
|
123
|
+
| `^(\w+)@clients$` | `FfXHGud25MDOUGjQyBZnCWkkWlFDCS0Y` | `FfXHGud25MDOUGjQyBZnCWkkWlFDCS0Y` | Match |
|
|
124
|
+
| `clients$` | `clients` | `clients` | Match |
|
|
125
|
+
| `@clients$` | `@clients` | `FfXHGud25MDOUGjQyBZnCWkkWlFDCS0Y` | No Match |
|
|
126
|
+
|
|
127
|
+
> **Tip:** Use capture groups to extract the meaningful part of the token claim value for comparison with the URL user ID.
|
|
128
|
+
|
|
108
129
|
## Environment Variables
|
|
109
130
|
|
|
110
131
|
When using the Dismissible API Docker image or the standalone API, these environment variables configure JWT authentication:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dismissible/nestjs-jwt-auth-hook",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "JWT authentication hook for Dismissible applications using OIDC well-known discovery",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@nestjs/axios": "^4.0.1",
|
|
21
|
-
"@dismissible/nestjs-hooks": "^
|
|
22
|
-
"@dismissible/nestjs-request": "^
|
|
23
|
-
"@dismissible/nestjs-logger": "^
|
|
24
|
-
"@dismissible/nestjs-validation": "^
|
|
21
|
+
"@dismissible/nestjs-hooks": "^3.0.0",
|
|
22
|
+
"@dismissible/nestjs-request": "^3.0.0",
|
|
23
|
+
"@dismissible/nestjs-logger": "^3.0.0",
|
|
24
|
+
"@dismissible/nestjs-validation": "^3.0.0",
|
|
25
25
|
"jwks-rsa": "^3.2.0",
|
|
26
26
|
"jsonwebtoken": "^9.0.3"
|
|
27
27
|
},
|
|
@@ -74,7 +74,13 @@ export declare class JwtAuthHookConfig {
|
|
|
74
74
|
/**
|
|
75
75
|
* Optional: Regex pattern for user ID matching.
|
|
76
76
|
* Required when userIdMatchType is 'regex'.
|
|
77
|
-
*
|
|
77
|
+
*
|
|
78
|
+
* The pattern is matched against the tokenUserId from the JWT claim.
|
|
79
|
+
* If the pattern contains a capture group, the first captured group is
|
|
80
|
+
* extracted and compared against the URL's userId.
|
|
81
|
+
* If no capture group exists, the full match is compared.
|
|
82
|
+
*
|
|
83
|
+
* Example: "^(.+)@clients$" extracts everything before "@clients"
|
|
78
84
|
*/
|
|
79
85
|
readonly userIdMatchRegex?: string;
|
|
80
86
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-auth-hook.config.js","sourceRoot":"","sources":["../../../../libs/jwt-auth-hook/src/jwt-auth-hook.config.ts"],"names":[],"mappings":";;;;AAAA,qDASyB;AACzB,yDAAyC;AACzC,sEAA2F;AAE3F;;GAEG;AACU,QAAA,gCAAgC,GAAG,MAAM,CAAC,kCAAkC,CAAC,CAAC;AAE3F;;GAEG;AACH,IAAY,eAOX;AAPD,WAAY,eAAe;IACzB,mCAAmC;IACnC,kCAAe,CAAA;IACf,wDAAwD;IACxD,0CAAuB,CAAA;IACvB,kEAAkE;IAClE,kCAAe,CAAA;AACjB,CAAC,EAPW,eAAe,+BAAf,eAAe,QAO1B;AAED;;GAEG;AACH,MAAa,iBAAiB;
|
|
1
|
+
{"version":3,"file":"jwt-auth-hook.config.js","sourceRoot":"","sources":["../../../../libs/jwt-auth-hook/src/jwt-auth-hook.config.ts"],"names":[],"mappings":";;;;AAAA,qDASyB;AACzB,yDAAyC;AACzC,sEAA2F;AAE3F;;GAEG;AACU,QAAA,gCAAgC,GAAG,MAAM,CAAC,kCAAkC,CAAC,CAAC;AAE3F;;GAEG;AACH,IAAY,eAOX;AAPD,WAAY,eAAe;IACzB,mCAAmC;IACnC,kCAAe,CAAA;IACf,wDAAwD;IACxD,0CAAuB,CAAA;IACvB,kEAAkE;IAClE,kCAAe,CAAA;AACjB,CAAC,EAPW,eAAe,+BAAf,eAAe,QAO1B;AAED;;GAEG;AACH,MAAa,iBAAiB;CA8G7B;AA9GD,8CA8GC;AA3GiB;IAFf,IAAA,2BAAS,GAAE;IACX,IAAA,oCAAgB,GAAE;;kDACe;AAQlB;IAFf,IAAA,4BAAU,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC;IACrC,IAAA,uBAAK,GAAE;;uDAC8B;AAYtB;IAJf,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;IACT,IAAA,0BAAQ,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,IAAA,2CAAuB,GAAE;;iDACQ;AAQlB;IAFf,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;mDACuB;AAWlB;IAJf,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;IACT,IAAA,0BAAQ,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxB,IAAA,2CAAuB,GAAE;;qDACY;AAStB;IAHf,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;;4DACwB;AAS3B;IAHf,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;;yDACqB;AASxB;IAHf,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;;mDACe;AASlB;IAHf,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;IACX,IAAA,oCAAgB,EAAC,IAAI,CAAC,CAAC,kCAAkC;;;sDACpB;AAQtB;IAFf,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDAC0B;AAQrB;IAFf,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,eAAe,CAAC;;0DAC0B;AAelC;IAFf,IAAA,4BAAU,EAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,eAAe,CAAC,KAAK,CAAC;IAC9D,IAAA,0BAAQ,GAAE;;2DAC+B"}
|
package/src/jwt-auth.hook.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IDismissibleLifecycleHook, IHookResult } from '@dismissible/nestjs-hooks';
|
|
1
|
+
import { IDismissibleLifecycleHook, IHookResult, IBatchHookResult } from '@dismissible/nestjs-hooks';
|
|
2
2
|
import { IRequestContext } from '@dismissible/nestjs-request';
|
|
3
3
|
import { IDismissibleLogger } from '@dismissible/nestjs-logger';
|
|
4
4
|
import { JwtAuthService } from './jwt-auth.service';
|
|
@@ -18,6 +18,15 @@ export declare class JwtAuthHook implements IDismissibleLifecycleHook {
|
|
|
18
18
|
* Runs before any dismissible operation.
|
|
19
19
|
*/
|
|
20
20
|
onBeforeRequest(itemId: string, userId: string, context?: IRequestContext): Promise<IHookResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Validates the JWT bearer token from the Authorization header for batch requests.
|
|
23
|
+
* Runs before any batch dismissible operation.
|
|
24
|
+
*/
|
|
25
|
+
onBeforeBatchRequest(itemIds: string[], userId: string, context?: IRequestContext): Promise<IBatchHookResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Core JWT validation logic shared by both single and batch request handlers.
|
|
28
|
+
*/
|
|
29
|
+
private validateJwtToken;
|
|
21
30
|
/**
|
|
22
31
|
* Matches the token user ID against the request user ID based on the configured match type.
|
|
23
32
|
*/
|
package/src/jwt-auth.hook.js
CHANGED
|
@@ -22,14 +22,39 @@ let JwtAuthHook = class JwtAuthHook {
|
|
|
22
22
|
* Runs before any dismissible operation.
|
|
23
23
|
*/
|
|
24
24
|
async onBeforeRequest(itemId, userId, context) {
|
|
25
|
+
this.logger.debug('onBeforeRequest', {
|
|
26
|
+
itemId,
|
|
27
|
+
userId,
|
|
28
|
+
requestId: context?.requestId,
|
|
29
|
+
});
|
|
30
|
+
return this.validateJwtToken(userId, context);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validates the JWT bearer token from the Authorization header for batch requests.
|
|
34
|
+
* Runs before any batch dismissible operation.
|
|
35
|
+
*/
|
|
36
|
+
async onBeforeBatchRequest(itemIds, userId, context) {
|
|
37
|
+
this.logger.debug('onBeforeBatchRequest', {
|
|
38
|
+
itemIds,
|
|
39
|
+
userId,
|
|
40
|
+
requestId: context?.requestId,
|
|
41
|
+
});
|
|
42
|
+
return this.validateJwtToken(userId, context);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Core JWT validation logic shared by both single and batch request handlers.
|
|
46
|
+
*/
|
|
47
|
+
async validateJwtToken(userId, context) {
|
|
25
48
|
if (!this.config.enabled) {
|
|
26
49
|
return { proceed: true };
|
|
27
50
|
}
|
|
51
|
+
this.logger.debug('validating JWT token', {
|
|
52
|
+
config: this.config,
|
|
53
|
+
});
|
|
28
54
|
const authorizationHeader = context?.headers['authorization'];
|
|
29
55
|
const token = this.jwtAuthService.extractBearerToken(authorizationHeader);
|
|
30
56
|
if (!token) {
|
|
31
|
-
this.logger.debug('
|
|
32
|
-
itemId,
|
|
57
|
+
this.logger.debug('No bearer token provided', {
|
|
33
58
|
userId,
|
|
34
59
|
requestId: context?.requestId,
|
|
35
60
|
});
|
|
@@ -37,8 +62,7 @@ let JwtAuthHook = class JwtAuthHook {
|
|
|
37
62
|
}
|
|
38
63
|
const result = await this.jwtAuthService.validateToken(token);
|
|
39
64
|
if (!result.valid) {
|
|
40
|
-
this.logger.debug('
|
|
41
|
-
itemId,
|
|
65
|
+
this.logger.debug('Token validation failed', {
|
|
42
66
|
userId,
|
|
43
67
|
requestId: context?.requestId,
|
|
44
68
|
error: result.error,
|
|
@@ -50,8 +74,7 @@ let JwtAuthHook = class JwtAuthHook {
|
|
|
50
74
|
const tokenUserId = result.payload?.[userIdClaim];
|
|
51
75
|
if (matchUserId && tokenUserId) {
|
|
52
76
|
if (!this.matchUserIdValue(tokenUserId, userId)) {
|
|
53
|
-
this.logger.debug('
|
|
54
|
-
itemId,
|
|
77
|
+
this.logger.debug('User ID mismatch', {
|
|
55
78
|
userId,
|
|
56
79
|
requestId: context?.requestId,
|
|
57
80
|
tokenSubject: tokenUserId,
|
|
@@ -59,15 +82,12 @@ let JwtAuthHook = class JwtAuthHook {
|
|
|
59
82
|
throw new common_1.ForbiddenException('User ID in request does not match authenticated user');
|
|
60
83
|
}
|
|
61
84
|
}
|
|
62
|
-
this.logger.debug('
|
|
63
|
-
itemId,
|
|
85
|
+
this.logger.debug('Token validated successfully', {
|
|
64
86
|
userId,
|
|
65
87
|
requestId: context?.requestId,
|
|
66
88
|
subject: tokenUserId,
|
|
67
89
|
});
|
|
68
|
-
return {
|
|
69
|
-
proceed: true,
|
|
70
|
-
};
|
|
90
|
+
return { proceed: true };
|
|
71
91
|
}
|
|
72
92
|
/**
|
|
73
93
|
* Matches the token user ID against the request user ID based on the configured match type.
|
|
@@ -75,13 +95,47 @@ let JwtAuthHook = class JwtAuthHook {
|
|
|
75
95
|
matchUserIdValue(tokenUserId, userId) {
|
|
76
96
|
const matchType = this.config.userIdMatchType ?? jwt_auth_hook_config_1.UserIdMatchType.EXACT;
|
|
77
97
|
switch (matchType) {
|
|
78
|
-
case jwt_auth_hook_config_1.UserIdMatchType.EXACT:
|
|
98
|
+
case jwt_auth_hook_config_1.UserIdMatchType.EXACT: {
|
|
99
|
+
this.logger.debug('matching user ID value via exact value', {
|
|
100
|
+
tokenUserId,
|
|
101
|
+
userId,
|
|
102
|
+
matchType,
|
|
103
|
+
});
|
|
79
104
|
return tokenUserId === userId;
|
|
80
|
-
|
|
81
|
-
|
|
105
|
+
}
|
|
106
|
+
case jwt_auth_hook_config_1.UserIdMatchType.SUBSTRING: {
|
|
107
|
+
const result = tokenUserId.includes(userId) || userId.includes(tokenUserId);
|
|
108
|
+
this.logger.debug('matching user ID value via substring', {
|
|
109
|
+
tokenUserId,
|
|
110
|
+
userId,
|
|
111
|
+
matchType,
|
|
112
|
+
result,
|
|
113
|
+
});
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
82
116
|
case jwt_auth_hook_config_1.UserIdMatchType.REGEX: {
|
|
83
117
|
const regex = new RegExp(this.config.userIdMatchRegex);
|
|
84
|
-
|
|
118
|
+
const match = regex.exec(tokenUserId);
|
|
119
|
+
if (!match) {
|
|
120
|
+
this.logger.debug('regex did not match token user ID', {
|
|
121
|
+
tokenUserId,
|
|
122
|
+
userId,
|
|
123
|
+
matchType,
|
|
124
|
+
pattern: this.config.userIdMatchRegex,
|
|
125
|
+
});
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
// Use first capture group if present, otherwise use full match
|
|
129
|
+
const extractedUserId = match[1] ?? match[0];
|
|
130
|
+
const result = extractedUserId === userId;
|
|
131
|
+
this.logger.debug('matching user ID value via regex', {
|
|
132
|
+
tokenUserId,
|
|
133
|
+
userId,
|
|
134
|
+
extractedUserId,
|
|
135
|
+
matchType,
|
|
136
|
+
result,
|
|
137
|
+
});
|
|
138
|
+
return result;
|
|
85
139
|
}
|
|
86
140
|
}
|
|
87
141
|
}
|
package/src/jwt-auth.hook.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-auth.hook.js","sourceRoot":"","sources":["../../../../libs/jwt-auth-hook/src/jwt-auth.hook.ts"],"names":[],"mappings":";;;;AAAA,2CAA+F;
|
|
1
|
+
{"version":3,"file":"jwt-auth.hook.js","sourceRoot":"","sources":["../../../../libs/jwt-auth-hook/src/jwt-auth.hook.ts"],"names":[],"mappings":"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"}
|