@carecard/jwt-read 3.0.10 → 3.1.12
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/index.d.ts +159 -12
- package/index.js +34 -4
- package/lib/jwtLib.js +91 -179
- package/package.json +10 -5
- package/readme.md +77 -21
package/index.d.ts
CHANGED
|
@@ -1,25 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utility functions for
|
|
2
|
+
* Utility functions for authentication and authorization in the CareCard ecosystem.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Request, Response, NextFunction } from 'express';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Represents the standard JWT header structure.
|
|
9
|
+
*/
|
|
10
|
+
export interface JwtHeader {
|
|
11
|
+
/** The cryptographic algorithm used to secure the JWT. */
|
|
12
|
+
alg?: string;
|
|
13
|
+
/** The media type of the JWT. Defaults to 'JWT'. */
|
|
14
|
+
typ?: string;
|
|
15
|
+
/** Any other custom header fields. */
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Represents the standard JWT payload (claims) structure.
|
|
9
21
|
*/
|
|
10
22
|
export interface JwtPayload {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
/** Issued at time, in seconds since the epoch. */
|
|
24
|
+
iat?: number;
|
|
25
|
+
/** Expiration time, in seconds since the epoch. */
|
|
26
|
+
exp?: number;
|
|
27
|
+
/** Not before time, in seconds since the epoch. */
|
|
28
|
+
nbf?: number;
|
|
29
|
+
/** Authentication time, in seconds since the epoch. */
|
|
30
|
+
auth_time?: number;
|
|
31
|
+
/** Subject (usually the client ID). */
|
|
32
|
+
sub?: string;
|
|
33
|
+
/** Roles assigned to the user. */
|
|
34
|
+
roles?: string[];
|
|
35
|
+
/** Any other custom payload fields. */
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Container for the decoded header and payload of a JWT.
|
|
41
|
+
*/
|
|
42
|
+
export interface JwtParts {
|
|
43
|
+
/** Decoded JWT header. */
|
|
44
|
+
header: JwtHeader;
|
|
45
|
+
/** Decoded JWT payload. */
|
|
46
|
+
payload: JwtPayload;
|
|
16
47
|
}
|
|
17
48
|
|
|
18
49
|
/**
|
|
19
50
|
* Structure of the JWT object attached to the request.
|
|
20
51
|
*/
|
|
21
52
|
export interface JwtRequestObject {
|
|
22
|
-
header:
|
|
53
|
+
header: JwtHeader;
|
|
23
54
|
payload: JwtPayload;
|
|
24
55
|
age?: number;
|
|
25
56
|
jwtClientId: (req?: any) => string | undefined;
|
|
@@ -32,7 +63,7 @@ export interface JwtRequestObject {
|
|
|
32
63
|
* Structure of the visitor object attached to the request.
|
|
33
64
|
*/
|
|
34
65
|
export interface VisitorRequestObject {
|
|
35
|
-
header:
|
|
66
|
+
header: JwtHeader;
|
|
36
67
|
payload: JwtPayload;
|
|
37
68
|
visitorClientId: (req?: any) => string | undefined;
|
|
38
69
|
}
|
|
@@ -45,78 +76,194 @@ export interface AuthenticatedRequest extends Request {
|
|
|
45
76
|
visitor?: VisitorRequestObject | null;
|
|
46
77
|
}
|
|
47
78
|
|
|
79
|
+
|
|
48
80
|
/**
|
|
49
81
|
* Returns a middleware that verifies a JWT from the 'Authorization: Bearer <token>' header
|
|
50
82
|
* and extracts it into req.jwt. Throws an error if invalid.
|
|
51
83
|
*/
|
|
84
|
+
export function jwtVerify(publicKey: string, customErrorFunction?: () => void): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns a middleware that verifies a JWT from a custom header and extracts it into req.jwt.
|
|
88
|
+
* Throws an error if invalid.
|
|
89
|
+
*/
|
|
90
|
+
export function jwtVerifyWebToken(publicKey: string, headerName: string, customErrorFunction?: () => void): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns a middleware that verifies a JWT from the 'Authorization: Bearer <token>' header
|
|
94
|
+
* and extracts it into req.jwt. Returns false instead of throwing if invalid.
|
|
95
|
+
*/
|
|
96
|
+
export function jwtVerifyNoThrow(publicKey: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Returns a middleware that verifies a JWT from a custom header and extracts it into req.jwt.
|
|
100
|
+
* Returns false instead of throwing if invalid.
|
|
101
|
+
*/
|
|
102
|
+
export function jwtVerifyWebTokenNoThrow(publicKey: string, headerName: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns a middleware that verifies a visitor token from the 'Visitor' header
|
|
106
|
+
* and extracts it into req.visitor. Returns false instead of throwing if invalid.
|
|
107
|
+
*/
|
|
108
|
+
export function jwtVerifyVisitorNoThrow(publicKey: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Returns the sub from the extracted JWT in req.jwt.
|
|
112
|
+
*/
|
|
113
|
+
export function jwtGetClientId(req?: any): string | undefined;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns the sub from the extracted visitor token in req.visitor.
|
|
117
|
+
*/
|
|
118
|
+
export function jwtGetVisitorClientId(req?: any): string | undefined;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if the extracted JWT in req.jwt has expired.
|
|
122
|
+
*/
|
|
123
|
+
export function jwtIsExpired(req: any, jwtValiditySeconds?: number): boolean;
|
|
124
|
+
export function jwtIsExpired(jwtValiditySeconds: number): boolean;
|
|
125
|
+
export function jwtIsExpired(): boolean;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns the age of the extracted JWT in seconds.
|
|
129
|
+
*/
|
|
130
|
+
export function jwtGetAgeInSeconds(req?: any): number;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Returns a middleware that verifies the JWT and checks if the user has the required role.
|
|
134
|
+
*/
|
|
135
|
+
export function jwtVerifyAndHasRole(userRole: string, publicKey: string, customErrorFunction?: () => void): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Gets the full name of a role from its code (e.g., 'ad' -> 'admin').
|
|
139
|
+
*/
|
|
140
|
+
export function jwtGetRoleName(roleCode: string): string;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Gets the code of a role from its name (e.g., 'admin' -> 'ad').
|
|
144
|
+
*/
|
|
145
|
+
export function jwtGetRoleCode(roleName: string): string;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Validates the JWT from the Authorization header and extracts it into req.jwt.
|
|
149
|
+
*/
|
|
150
|
+
export function jwtValidateAndExtract(req: AuthenticatedRequest, publicKey: string, customErrorFunction?: () => void): void;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Validates the JWT from a custom header and extracts it into req.jwt.
|
|
154
|
+
*/
|
|
155
|
+
export function jwtValidateAndExtractWebToken(req: AuthenticatedRequest, publicKey: string, headerName: string, customErrorFunction?: () => void): void;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Validates the JWT from the Authorization header and extracts it into req.jwt (no-throw).
|
|
159
|
+
*/
|
|
160
|
+
export function jwtValidateAndExtractNoThrow(req: AuthenticatedRequest, publicKey: string): void;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validates the JWT from a custom header and extracts it into req.jwt (no-throw).
|
|
164
|
+
*/
|
|
165
|
+
export function jwtValidateAndExtractWebTokenNoThrow(req: AuthenticatedRequest, publicKey: string, headerName: string): void;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Validates the visitor token from the 'Visitor' header and extracts it into req.visitor (no-throw).
|
|
169
|
+
*/
|
|
170
|
+
export function jwtValidateAndExtractVisitorNoThrow(req: AuthenticatedRequest, publicKey: string): void;
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Returns a middleware that verifies a JWT from the 'Authorization: Bearer <token>' header
|
|
175
|
+
* and extracts it into req.jwt. Throws an error if invalid.
|
|
176
|
+
* @deprecated use jwtVerify
|
|
177
|
+
*/
|
|
52
178
|
export function verifyJwt(publicKey: string, customErrorFunction?: () => void): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
53
179
|
|
|
54
180
|
/**
|
|
55
181
|
* Returns a middleware that verifies a JWT from a custom header and extracts it into req.jwt.
|
|
56
182
|
* Throws an error if invalid.
|
|
183
|
+
* @deprecated use jwtVerifyWebToken
|
|
57
184
|
*/
|
|
58
185
|
export function verifyWebToken(publicKey: string, headerName: string, customErrorFunction?: () => void): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
59
186
|
|
|
60
187
|
/**
|
|
61
188
|
* Returns a middleware that verifies a JWT from the 'Authorization: Bearer <token>' header
|
|
62
189
|
* and extracts it into req.jwt. Returns false instead of throwing if invalid.
|
|
190
|
+
* @deprecated use jwtVerifyNoThrow
|
|
63
191
|
*/
|
|
64
192
|
export function verifyJwtNoThrow(publicKey: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
65
193
|
|
|
66
194
|
/**
|
|
67
195
|
* Returns a middleware that verifies a JWT from a custom header and extracts it into req.jwt.
|
|
68
196
|
* Returns false instead of throwing if invalid.
|
|
197
|
+
* @deprecated use jwtVerifyWebTokenNoThrow
|
|
69
198
|
*/
|
|
70
199
|
export function verifyWebTokenNoThrow(publicKey: string, headerName: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
71
200
|
|
|
72
201
|
/**
|
|
73
202
|
* Returns a middleware that verifies a visitor token from the 'Visitor' header
|
|
74
203
|
* and extracts it into req.visitor. Returns false instead of throwing if invalid.
|
|
204
|
+
* @deprecated use jwtVerifyVisitorNoThrow
|
|
75
205
|
*/
|
|
76
206
|
export function verifyVisitorNoThrow(publicKey: string): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
77
207
|
|
|
78
208
|
/**
|
|
79
|
-
* Returns the
|
|
209
|
+
* Returns the sub from the extracted JWT in req.jwt.
|
|
210
|
+
* @deprecated use jwtGetClientId
|
|
80
211
|
*/
|
|
81
212
|
export function jwtClientId(req?: any): string | undefined;
|
|
82
213
|
|
|
83
214
|
/**
|
|
84
|
-
* Returns the
|
|
215
|
+
* Returns the sub from the extracted visitor token in req.visitor.
|
|
216
|
+
* @deprecated use jwtGetVisitorClientId
|
|
85
217
|
*/
|
|
86
218
|
export function visitorClientId(req?: any): string | undefined;
|
|
87
219
|
|
|
88
220
|
/**
|
|
89
221
|
* Checks if the extracted JWT in req.jwt has expired.
|
|
222
|
+
* @deprecated use jwtIsExpired
|
|
90
223
|
*/
|
|
91
|
-
export function isJwtExpired(req
|
|
224
|
+
export function isJwtExpired(req: any, jwtValiditySeconds?: number): boolean;
|
|
225
|
+
/** @deprecated use jwtIsExpired */
|
|
226
|
+
export function isJwtExpired(jwtValiditySeconds: number): boolean;
|
|
227
|
+
/** @deprecated use jwtIsExpired */
|
|
228
|
+
export function isJwtExpired(): boolean;
|
|
92
229
|
|
|
93
230
|
/**
|
|
94
231
|
* Returns the age of the extracted JWT in seconds.
|
|
232
|
+
* @deprecated use jwtGetAgeInSeconds
|
|
95
233
|
*/
|
|
96
234
|
export function jwtAgeInSeconds(req?: any): number;
|
|
97
235
|
|
|
98
236
|
/**
|
|
99
237
|
* Returns a middleware that verifies the JWT and checks if the user has the required role.
|
|
238
|
+
* @deprecated use jwtVerifyAndHasRole
|
|
100
239
|
*/
|
|
101
240
|
export function verifyJwtAndRole(userRole: string, publicKey: string, customErrorFunction?: () => void): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
|
|
102
241
|
|
|
103
242
|
/**
|
|
104
243
|
* Throws a Used_Token error.
|
|
244
|
+
* @deprecated use jwtThrowUsedTokenError
|
|
105
245
|
*/
|
|
106
246
|
export function throwUsedTokenError(): never;
|
|
107
247
|
|
|
248
|
+
|
|
108
249
|
/**
|
|
109
250
|
* Checks if the user in the extracted JWT has the specified role.
|
|
251
|
+
* @deprecated use jwtDoesJwtUserHasRole
|
|
110
252
|
*/
|
|
111
253
|
export function doesJwtUserHasRole(req: any, userRole: string): boolean;
|
|
254
|
+
/** @deprecated use jwtDoesJwtUserHasRole */
|
|
112
255
|
export function doesJwtUserHasRole(userRole: string): boolean;
|
|
113
256
|
|
|
114
257
|
/**
|
|
115
258
|
* Gets the full name of a role from its code (e.g., 'ad' -> 'admin').
|
|
259
|
+
* @deprecated use jwtGetRoleName
|
|
116
260
|
*/
|
|
117
261
|
export function getNameOfRole(roleCode: string): string;
|
|
118
262
|
|
|
263
|
+
|
|
119
264
|
/**
|
|
120
265
|
* Gets the code of a role from its name (e.g., 'admin' -> 'ad').
|
|
266
|
+
* @deprecated use jwtGetRoleCode
|
|
121
267
|
*/
|
|
122
268
|
export function getCodeOfRole(roleName: string): string;
|
|
269
|
+
|
package/index.js
CHANGED
|
@@ -1,21 +1,51 @@
|
|
|
1
|
-
const jwtLib = require(
|
|
2
|
-
const jwtRoles = require(
|
|
3
|
-
|
|
1
|
+
const jwtLib = require('./lib/jwtLib');
|
|
2
|
+
const jwtRoles = require('./lib/jwtRoles');
|
|
4
3
|
|
|
5
4
|
module.exports = {
|
|
5
|
+
jwtVerify: jwtLib.verifyJwt,
|
|
6
|
+
jwtVerifyWebToken: jwtLib.verifyWebToken,
|
|
7
|
+
jwtVerifyNoThrow: jwtLib.verifyJwtNoThrow,
|
|
8
|
+
jwtVerifyWebTokenNoThrow: jwtLib.verifyWebTokenNoThrow,
|
|
9
|
+
jwtVerifyVisitorNoThrow: jwtLib.verifyVisitorNoThrow,
|
|
10
|
+
jwtGetClientId: jwtLib.jwtClientId,
|
|
11
|
+
jwtGetVisitorClientId: jwtLib.visitorClientId,
|
|
12
|
+
jwtIsExpired: jwtLib.isJwtExpired,
|
|
13
|
+
jwtGetAgeInSeconds: jwtLib.jwtAgeInSeconds,
|
|
14
|
+
jwtVerifyAndHasRole: jwtLib.verifyJwtAndRole,
|
|
15
|
+
jwtGetRoleName: jwtRoles.getNameOfRoleFromCode,
|
|
16
|
+
jwtGetRoleCode: jwtRoles.getCodeFromNameOfRole,
|
|
17
|
+
jwtValidateAndExtract: jwtLib.validateAndExtractJwtObject,
|
|
18
|
+
jwtValidateAndExtractWebToken: jwtLib.validateAndExtractWebToken,
|
|
19
|
+
jwtValidateAndExtractNoThrow: jwtLib.validateAndExtractJwtObjectNoThrow,
|
|
20
|
+
jwtValidateAndExtractWebTokenNoThrow: jwtLib.validateAndExtractWebTokenObjectNoThrow,
|
|
21
|
+
jwtValidateAndExtractVisitorNoThrow: jwtLib.validateAndExtractVisitorObjectNoThrow,
|
|
22
|
+
|
|
23
|
+
/** @deprecated use jwtVerify */
|
|
6
24
|
verifyJwt: jwtLib.verifyJwt,
|
|
25
|
+
/** @deprecated use jwtVerifyWebToken */
|
|
7
26
|
verifyWebToken: jwtLib.verifyWebToken,
|
|
27
|
+
/** @deprecated use jwtVerifyNoThrow */
|
|
8
28
|
verifyJwtNoThrow: jwtLib.verifyJwtNoThrow,
|
|
29
|
+
/** @deprecated use jwtVerifyWebTokenNoThrow */
|
|
9
30
|
verifyWebTokenNoThrow: jwtLib.verifyWebTokenNoThrow,
|
|
31
|
+
/** @deprecated use jwtVerifyVisitorNoThrow */
|
|
10
32
|
verifyVisitorNoThrow: jwtLib.verifyVisitorNoThrow,
|
|
33
|
+
/** @deprecated use jwtGetClientId */
|
|
11
34
|
jwtClientId: jwtLib.jwtClientId,
|
|
35
|
+
/** @deprecated use jwtGetVisitorClientId */
|
|
12
36
|
visitorClientId: jwtLib.visitorClientId,
|
|
37
|
+
/** @deprecated use jwtIsExpired */
|
|
13
38
|
isJwtExpired: jwtLib.isJwtExpired,
|
|
39
|
+
/** @deprecated use jwtGetAgeInSeconds */
|
|
14
40
|
jwtAgeInSeconds: jwtLib.jwtAgeInSeconds,
|
|
41
|
+
/** @deprecated use jwtVerifyAndHasRole */
|
|
15
42
|
verifyJwtAndRole: jwtLib.verifyJwtAndRole,
|
|
43
|
+
/** @deprecated use jwtThrowUsedTokenError */
|
|
16
44
|
throwUsedTokenError: jwtLib.throwUsedTokenError,
|
|
45
|
+
/** @deprecated use jwtDoesJwtUserHasRole */
|
|
17
46
|
doesJwtUserHasRole: jwtLib.doesJwtUserHasRole,
|
|
47
|
+
/** @deprecated use jwtGetRoleName */
|
|
18
48
|
getNameOfRole: jwtRoles.getNameOfRoleFromCode,
|
|
49
|
+
/** @deprecated use jwtGetRoleCode */
|
|
19
50
|
getCodeOfRole: jwtRoles.getCodeFromNameOfRole
|
|
20
51
|
};
|
|
21
|
-
|
package/lib/jwtLib.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const {isJwtString} = require('@carecard/validate').validate;
|
|
2
|
-
const {
|
|
2
|
+
const {jwtVerifySignedToken, jwtGetHeaderPayload} = require('@carecard/auth-util');
|
|
3
|
+
|
|
3
4
|
const {
|
|
4
5
|
throwLoginRequiredError,
|
|
5
6
|
throwNotAuthorizedError
|
|
@@ -7,12 +8,12 @@ const {
|
|
|
7
8
|
|
|
8
9
|
function jwtClientId(req) {
|
|
9
10
|
const jwtObj = req?.jwt || this;
|
|
10
|
-
return jwtObj?.payload?.
|
|
11
|
+
return jwtObj?.payload?.sub;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
function visitorClientId(req) {
|
|
14
15
|
const visitorObj = req?.visitor || this;
|
|
15
|
-
return visitorObj?.payload?.
|
|
16
|
+
return visitorObj?.payload?.sub;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
function doesJwtUserHasRole(req, userRole) {
|
|
@@ -24,11 +25,11 @@ function doesJwtUserHasRole(req, userRole) {
|
|
|
24
25
|
|
|
25
26
|
const jwtObj = req?.jwt || this;
|
|
26
27
|
|
|
27
|
-
if (userRole
|
|
28
|
-
return jwtObj?.payload?.roles?.includes(userRole);
|
|
29
|
-
} else {
|
|
28
|
+
if (!userRole || typeof userRole !== "string" || !jwtObj?.payload?.roles || !Array.isArray(jwtObj.payload.roles)) {
|
|
30
29
|
throwNotAuthorizedError();
|
|
31
30
|
}
|
|
31
|
+
|
|
32
|
+
return jwtObj.payload.roles.includes(userRole);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function throwError(customErrorFunction) {
|
|
@@ -83,7 +84,7 @@ function validateAndExtractJwtObject(req, publicKey, customErrorFunction) {
|
|
|
83
84
|
|
|
84
85
|
const jwtString = _validateJwt(req, customErrorFunction);
|
|
85
86
|
|
|
86
|
-
const isJwtSignatureValid =
|
|
87
|
+
const isJwtSignatureValid = jwtVerifySignedToken(jwtString, publicKey);
|
|
87
88
|
|
|
88
89
|
if (jwtString && isJwtSignatureValid) {
|
|
89
90
|
_extractJwtObject(req, jwtString, customErrorFunction);
|
|
@@ -99,7 +100,7 @@ function validateAndExtractWebToken(req, publicKey, headerName, customErrorFunct
|
|
|
99
100
|
|
|
100
101
|
const webTokenString = _validateWebToken(req, headerName, customErrorFunction);
|
|
101
102
|
|
|
102
|
-
const isJwtSignatureValid =
|
|
103
|
+
const isJwtSignatureValid = jwtVerifySignedToken(webTokenString, publicKey);
|
|
103
104
|
|
|
104
105
|
if (webTokenString && isJwtSignatureValid) {
|
|
105
106
|
_extractJwtObject(req, webTokenString, customErrorFunction);
|
|
@@ -112,72 +113,15 @@ function validateAndExtractWebToken(req, publicKey, headerName, customErrorFunct
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
function validateAndExtractJwtObjectNoThrow(req, publicKey) {
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
|
|
118
|
-
const jwtString = _validateJwtNoThrow(req);
|
|
119
|
-
|
|
120
|
-
const isJwtSignatureValid = jwtUtilAuth.verifyJwtSignature(jwtString, publicKey);
|
|
121
|
-
|
|
122
|
-
if (jwtString && isJwtSignatureValid) {
|
|
123
|
-
_extractJwtObjectNoThrow(req, jwtString);
|
|
124
|
-
} else {
|
|
125
|
-
req["jwt"] = null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
} catch (err) {
|
|
129
|
-
|
|
130
|
-
req["jwt"] = null;
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return req;
|
|
116
|
+
return _validateAndExtractGenericNoThrow(req, publicKey, _validateJwtNoThrow, _extractJwtObjectNoThrow, "jwt");
|
|
135
117
|
}
|
|
136
118
|
|
|
137
119
|
function validateAndExtractWebTokenObjectNoThrow(req, publicKey, headerName) {
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
|
|
141
|
-
const jwtString = _validateWebTokenNoThrow(req, headerName);
|
|
142
|
-
|
|
143
|
-
const isJwtSignatureValid = jwtUtilAuth.verifyJwtSignature(jwtString, publicKey);
|
|
144
|
-
|
|
145
|
-
if (jwtString && isJwtSignatureValid) {
|
|
146
|
-
_extractJwtObjectNoThrow(req, jwtString);
|
|
147
|
-
} else {
|
|
148
|
-
req["jwt"] = null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
} catch (err) {
|
|
152
|
-
|
|
153
|
-
req["jwt"] = null;
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return req;
|
|
120
|
+
return _validateAndExtractGenericNoThrow(req, publicKey, (r) => _validateWebTokenNoThrow(r, headerName), _extractJwtObjectNoThrow, "jwt");
|
|
158
121
|
}
|
|
159
122
|
|
|
160
123
|
function validateAndExtractVisitorObjectNoThrow(req, publicKey) {
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
|
|
164
|
-
const jwtString = _validateVisitorNoThrow(req);
|
|
165
|
-
|
|
166
|
-
const isJwtSignatureValid = jwtUtilAuth.verifyJwtSignature(jwtString, publicKey);
|
|
167
|
-
|
|
168
|
-
if (jwtString && isJwtSignatureValid) {
|
|
169
|
-
_extractVisitorObjectNoThrow(req, jwtString);
|
|
170
|
-
} else {
|
|
171
|
-
req["visitor"] = null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
} catch (err) {
|
|
175
|
-
|
|
176
|
-
req["visitor"] = null;
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return req;
|
|
124
|
+
return _validateAndExtractGenericNoThrow(req, publicKey, _validateVisitorNoThrow, _extractVisitorObjectNoThrow, "visitor");
|
|
181
125
|
}
|
|
182
126
|
|
|
183
127
|
function verifyJwtAndRole(role, publicKey, customErrorFunction) {
|
|
@@ -263,34 +207,62 @@ function _isLoginRequired(hasRequiredRole, customErrorFunction) {
|
|
|
263
207
|
|
|
264
208
|
}
|
|
265
209
|
|
|
266
|
-
function
|
|
210
|
+
function _validateGenericNoThrow(req, headerName, extractor) {
|
|
211
|
+
let jwtRaw = req.get(headerName);
|
|
212
|
+
if (!jwtRaw) return null;
|
|
213
|
+
let jwt = extractor(jwtRaw);
|
|
214
|
+
return isJwtString(jwt) ? jwt : null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function _validateGeneric(req, headerName, extractor, customErrorFunction) {
|
|
218
|
+
let jwtRaw = req.get(headerName);
|
|
219
|
+
let jwt = extractor(jwtRaw, customErrorFunction);
|
|
220
|
+
if (isJwtString(jwt)) return jwt;
|
|
221
|
+
throwError(customErrorFunction);
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function _validateAndExtractGenericNoThrow(req, publicKey, validator, extractor, propertyName) {
|
|
226
|
+
try {
|
|
227
|
+
const jwtString = validator(req);
|
|
228
|
+
const isJwtSignatureValid = jwtVerifySignedToken(jwtString, publicKey);
|
|
229
|
+
|
|
230
|
+
if (jwtString && isJwtSignatureValid) {
|
|
231
|
+
extractor(req, jwtString);
|
|
232
|
+
} else {
|
|
233
|
+
req[propertyName] = null;
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
if (req) req[propertyName] = null;
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
return req;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function _extractGenericObjectNoThrow(req, jwt, attacher, propertyName) {
|
|
267
243
|
if (jwt && typeof jwt === "string") {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
req[
|
|
244
|
+
const obj = jwtGetHeaderPayload(jwt);
|
|
245
|
+
attacher(obj);
|
|
246
|
+
req[propertyName] = obj;
|
|
271
247
|
} else {
|
|
248
|
+
if (req) req[propertyName] = null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function _extractJwtObject(req, jwt, customErrorFunction) {
|
|
253
|
+
_extractJwtObjectNoThrow(req, jwt);
|
|
254
|
+
|
|
255
|
+
if (!req["jwt"]) {
|
|
272
256
|
throwError(customErrorFunction);
|
|
273
257
|
}
|
|
274
258
|
}
|
|
275
259
|
|
|
276
260
|
function _extractJwtObjectNoThrow(req, jwt) {
|
|
277
|
-
|
|
278
|
-
const jwtObj = jwtUtilAuth.getHeaderPayloadFromJwt(jwt);
|
|
279
|
-
_attachJwtMethods(jwtObj);
|
|
280
|
-
req["jwt"] = jwtObj;
|
|
281
|
-
} else {
|
|
282
|
-
req["jwt"] = null;
|
|
283
|
-
}
|
|
261
|
+
_extractGenericObjectNoThrow(req, jwt, _attachJwtMethods, "jwt");
|
|
284
262
|
}
|
|
285
263
|
|
|
286
264
|
function _extractVisitorObjectNoThrow(req, jwt) {
|
|
287
|
-
|
|
288
|
-
const visitorObj = jwtUtilAuth.getHeaderPayloadFromJwt(jwt);
|
|
289
|
-
_attachVisitorMethods(visitorObj);
|
|
290
|
-
req["visitor"] = visitorObj;
|
|
291
|
-
} else {
|
|
292
|
-
req["visitor"] = null;
|
|
293
|
-
}
|
|
265
|
+
_extractGenericObjectNoThrow(req, jwt, _attachVisitorMethods, "visitor");
|
|
294
266
|
}
|
|
295
267
|
|
|
296
268
|
function _attachJwtMethods(jwtObj) {
|
|
@@ -314,8 +286,10 @@ function _attachVisitorMethods(visitorObj) {
|
|
|
314
286
|
|
|
315
287
|
async function _isJwtSignatureValid(jwt, publicKey, customErrorFunction) {
|
|
316
288
|
|
|
317
|
-
|
|
318
|
-
|
|
289
|
+
const isValid = await _isJwtSignatureValidNoThrow(jwt, publicKey);
|
|
290
|
+
|
|
291
|
+
if (isValid) {
|
|
292
|
+
return isValid;
|
|
319
293
|
} else {
|
|
320
294
|
throwError(customErrorFunction);
|
|
321
295
|
}
|
|
@@ -324,115 +298,37 @@ async function _isJwtSignatureValid(jwt, publicKey, customErrorFunction) {
|
|
|
324
298
|
async function _isJwtSignatureValidNoThrow(jwt, publicKey) {
|
|
325
299
|
|
|
326
300
|
if (jwt && typeof jwt === "string") {
|
|
327
|
-
return
|
|
301
|
+
return jwtVerifySignedToken(jwt, publicKey)
|
|
328
302
|
} else {
|
|
329
303
|
return false;
|
|
330
304
|
}
|
|
331
305
|
}
|
|
332
306
|
|
|
333
307
|
function _validateJwt(req, customErrorFunction) {
|
|
334
|
-
|
|
335
|
-
// Get from http header.
|
|
336
|
-
let jwtRaw = req.get("Authorization");
|
|
337
|
-
|
|
338
|
-
// Extract jwt from bearer schema.
|
|
339
|
-
let jwt = _extractJwt(jwtRaw, customErrorFunction);
|
|
340
|
-
|
|
341
|
-
// Validate characters string of jwt.
|
|
342
|
-
if (isJwtString(jwt)) {
|
|
343
|
-
return jwt;
|
|
344
|
-
} else {
|
|
345
|
-
throwError(customErrorFunction);
|
|
346
|
-
}
|
|
308
|
+
return _validateGeneric(req, "Authorization", _extractJwt, customErrorFunction);
|
|
347
309
|
}
|
|
348
310
|
|
|
349
311
|
function _validateWebToken(req, headerName, customErrorFunction) {
|
|
350
|
-
|
|
351
|
-
// Get from http header.
|
|
352
|
-
let jwtRaw = req.get(headerName);
|
|
353
|
-
|
|
354
|
-
// Extract jwt from bearer schema.
|
|
355
|
-
let webToken = _extractWebToken(jwtRaw, customErrorFunction);
|
|
356
|
-
|
|
357
|
-
// Validate characters string of jwt.
|
|
358
|
-
if (isJwtString(webToken)) {
|
|
359
|
-
return webToken;
|
|
360
|
-
} else {
|
|
361
|
-
throwError(customErrorFunction);
|
|
362
|
-
}
|
|
312
|
+
return _validateGeneric(req, headerName, _extractWebToken, customErrorFunction);
|
|
363
313
|
}
|
|
364
314
|
|
|
365
315
|
function _validateJwtNoThrow(req) {
|
|
366
|
-
|
|
367
|
-
// Get from http header.
|
|
368
|
-
let jwtRaw = req.get("Authorization");
|
|
369
|
-
|
|
370
|
-
if (!jwtRaw) {
|
|
371
|
-
return null;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Extract jwt from bearer schema.
|
|
375
|
-
let jwt = _extractJwtNoThrow(jwtRaw);
|
|
376
|
-
|
|
377
|
-
// Validate characters string of jwt.
|
|
378
|
-
if (isJwtString(jwt)) {
|
|
379
|
-
return jwt;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
return null;
|
|
316
|
+
return _validateGenericNoThrow(req, "Authorization", _extractJwtNoThrow);
|
|
383
317
|
}
|
|
384
318
|
|
|
385
319
|
function _validateWebTokenNoThrow(req, headerName) {
|
|
386
|
-
|
|
387
|
-
// Get from http header.
|
|
388
|
-
let jwtRaw = req.get(headerName);
|
|
389
|
-
|
|
390
|
-
if (!jwtRaw) {
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Extract jwt from bearer schema.
|
|
395
|
-
let webToken = _extractWebTokenNoThrow(jwtRaw);
|
|
396
|
-
|
|
397
|
-
// Validate characters string of jwt.
|
|
398
|
-
if (isJwtString(webToken)) {
|
|
399
|
-
return webToken;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return null;
|
|
320
|
+
return _validateGenericNoThrow(req, headerName, _extractWebTokenNoThrow);
|
|
403
321
|
}
|
|
404
322
|
|
|
405
323
|
function _validateVisitorNoThrow(req) {
|
|
406
|
-
|
|
407
|
-
// Get from http header.
|
|
408
|
-
let jwtRaw = req.get("Visitor");
|
|
409
|
-
|
|
410
|
-
if (!jwtRaw) {
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Extract jwt from bearer schema.
|
|
415
|
-
let jwt = _extractJwtNoThrow(jwtRaw);
|
|
416
|
-
|
|
417
|
-
// Validate characters string of jwt.
|
|
418
|
-
if (isJwtString(jwt)) {
|
|
419
|
-
return jwt;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
return null;
|
|
324
|
+
return _validateGenericNoThrow(req, "Visitor", _extractJwtNoThrow);
|
|
423
325
|
}
|
|
424
326
|
|
|
425
327
|
function _extractJwt(jwtRaw, customErrorFunction) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (jwtRaw && typeof jwtRaw === "string") {
|
|
429
|
-
jwtSplit = jwtRaw.split(" ");
|
|
430
|
-
} else {
|
|
431
|
-
throwError(customErrorFunction);
|
|
432
|
-
}
|
|
328
|
+
const jwt = _extractJwtNoThrow(jwtRaw);
|
|
433
329
|
|
|
434
|
-
if (
|
|
435
|
-
return
|
|
330
|
+
if (jwt !== null) {
|
|
331
|
+
return jwt;
|
|
436
332
|
} else {
|
|
437
333
|
throwError(customErrorFunction);
|
|
438
334
|
}
|
|
@@ -441,9 +337,10 @@ function _extractJwt(jwtRaw, customErrorFunction) {
|
|
|
441
337
|
}
|
|
442
338
|
|
|
443
339
|
function _extractWebToken(jwtRaw, customErrorFunction) {
|
|
340
|
+
const webToken = _extractWebTokenNoThrow(jwtRaw);
|
|
444
341
|
|
|
445
|
-
if (
|
|
446
|
-
return
|
|
342
|
+
if (webToken !== null) {
|
|
343
|
+
return webToken;
|
|
447
344
|
} else {
|
|
448
345
|
throwError(customErrorFunction);
|
|
449
346
|
}
|
|
@@ -481,8 +378,8 @@ module.exports = {
|
|
|
481
378
|
_isJwtSignatureValid,
|
|
482
379
|
_isJwtSignatureValidNoThrow,
|
|
483
380
|
_extractJwtObject,
|
|
484
|
-
_extractJwtObjectNoThrow,
|
|
485
381
|
validateAndExtractJwtObject,
|
|
382
|
+
validateAndExtractWebToken,
|
|
486
383
|
jwtAgeInSeconds,
|
|
487
384
|
isJwtExpired,
|
|
488
385
|
doesJwtUserHasRole,
|
|
@@ -495,5 +392,20 @@ module.exports = {
|
|
|
495
392
|
verifyJwtNoThrow,
|
|
496
393
|
throwUsedTokenError,
|
|
497
394
|
throwError,
|
|
498
|
-
verifyVisitorNoThrow
|
|
395
|
+
verifyVisitorNoThrow,
|
|
396
|
+
validateAndExtractVisitorObjectNoThrow,
|
|
397
|
+
validateAndExtractJwtObjectNoThrow,
|
|
398
|
+
validateAndExtractWebTokenObjectNoThrow,
|
|
399
|
+
_extractJwtNoThrow,
|
|
400
|
+
_extractWebTokenNoThrow,
|
|
401
|
+
_extractJwt,
|
|
402
|
+
_validateWebTokenNoThrow,
|
|
403
|
+
_validateVisitorNoThrow,
|
|
404
|
+
_extractJwtObjectNoThrow,
|
|
405
|
+
_extractVisitorObjectNoThrow,
|
|
406
|
+
_extractWebToken,
|
|
407
|
+
_isLoginRequired,
|
|
408
|
+
_attachJwtMethods,
|
|
409
|
+
_attachVisitorMethods,
|
|
410
|
+
_validateWebToken
|
|
499
411
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carecard/jwt-read",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.12",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/CareCard-ca/pkg-jwt-read.git"
|
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
"types": "index.d.ts",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"test": "export NODE_ENV=test && mocha --recursive",
|
|
13
|
-
"test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.ts"
|
|
13
|
+
"test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.ts",
|
|
14
|
+
"test:coverage": "tsc --noEmit && export NODE_ENV=test && nyc mocha --recursive -r ts-node/register 'test/**/*.{js,ts}'",
|
|
15
|
+
"prepare": "husky"
|
|
14
16
|
},
|
|
15
17
|
"keywords": [
|
|
16
18
|
"auth",
|
|
17
19
|
"utility",
|
|
18
|
-
"cryptology"
|
|
20
|
+
"cryptology",
|
|
21
|
+
"jwt"
|
|
19
22
|
],
|
|
20
23
|
"author": "CareCard team",
|
|
21
24
|
"license": "ISC",
|
|
@@ -23,13 +26,15 @@
|
|
|
23
26
|
"@types/express": "5.0.6",
|
|
24
27
|
"@types/mocha": "10.0.10",
|
|
25
28
|
"@types/node": "25.5.0",
|
|
29
|
+
"husky": "^9.1.7",
|
|
26
30
|
"mocha": "11.7.5",
|
|
31
|
+
"nyc": "^18.0.0",
|
|
27
32
|
"ts-node": "10.9.2",
|
|
28
33
|
"typescript": "5.9.3"
|
|
29
34
|
},
|
|
30
35
|
"dependencies": {
|
|
31
|
-
"@carecard/
|
|
32
|
-
"@carecard/
|
|
36
|
+
"@carecard/common-util": "3.1.11",
|
|
37
|
+
"@carecard/auth-util": "3.1.12",
|
|
33
38
|
"@carecard/validate": "3.0.10"
|
|
34
39
|
}
|
|
35
40
|
}
|
package/readme.md
CHANGED
|
@@ -1,32 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @carecard/jwt-read
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
|
+

|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Utility package for reading, parsing, and verifying JWTs in the CareCard ecosystem.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
## Features
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
- **JWT Verification**: Middleware-like utilities for signature and role verification.
|
|
11
|
+
- **Express Integration**: Designed to work seamlessly with Express `req` objects.
|
|
12
|
+
- **Role Mapping**: Simple utility for translating internal role codes to human-readable names.
|
|
13
|
+
- **Claims Extraction**: Easy extraction of `sub` (clientId) and other JWT payload claims.
|
|
14
|
+
- **Expiration Management**: Helpers to check if a JWT is expired and calculate its remaining TTL.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @carecard/jwt-read
|
|
11
20
|
```
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Middleware-like Verification (`verifyJwtAndRole`)
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
const { verifyJwtAndRole, throwUsedTokenError } = require('@carecard/jwt-read');
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
29
|
+
// Create a verification function for 'admin' role
|
|
30
|
+
const verifyAdmin = verifyJwtAndRole('admin', publicKey, throwUsedTokenError);
|
|
31
|
+
|
|
32
|
+
// In an Express controller/middleware
|
|
33
|
+
try {
|
|
34
|
+
await verifyAdmin(req, res, next);
|
|
35
|
+
// If successful, req.jwt contains { header, payload }
|
|
36
|
+
console.log(req.jwt.payload.sub);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// Handle verification error
|
|
39
|
+
}
|
|
18
40
|
```
|
|
19
41
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
,
|
|
28
|
-
payload: {
|
|
29
|
-
...
|
|
30
|
-
}
|
|
42
|
+
### Direct JWT Reading
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
const { verifyJwt, isJwtExpired } = require('@carecard/jwt-read');
|
|
46
|
+
|
|
47
|
+
const result = verifyJwt(rawJwt, publicKey);
|
|
48
|
+
if (result && !isJwtExpired(result)) {
|
|
49
|
+
console.log('JWT is valid and not expired:', result.payload);
|
|
31
50
|
}
|
|
32
51
|
```
|
|
52
|
+
|
|
53
|
+
### Role Utilities
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const { getNameOfRole, getCodeOfRole } = require('@carecard/jwt-read');
|
|
57
|
+
|
|
58
|
+
console.log(getNameOfRole('ad')); // Result: 'admin'
|
|
59
|
+
console.log(getCodeOfRole('super_admin')); // Result: 'su'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Testing
|
|
63
|
+
|
|
64
|
+
Run tests using:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
To run tests with coverage:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm run test:coverage
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To run type tests:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run test:types
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Architecture
|
|
83
|
+
|
|
84
|
+
The package is organized into several modules:
|
|
85
|
+
- `jwtLib`: Main logic for JWT verification, extraction, and Express integration.
|
|
86
|
+
- `jwtRoles`: Role mapping between internal codes and names.
|
|
87
|
+
|
|
88
|
+
All modules are exported through the main `index.js`.
|