@villedemontreal/jwt-validator 5.9.3 → 5.10.1
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/dist/scripts/showCoverage.js.map +1 -1
- package/dist/scripts/testUnits.js.map +1 -1
- package/dist/scripts/watch.js.map +1 -1
- package/dist/src/config/configs.js.map +1 -1
- package/dist/src/config/init.js +2 -3
- package/dist/src/config/init.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/jwtValidator.js.map +1 -1
- package/dist/src/jwtValidator.test.js.map +1 -1
- package/dist/src/middleware/jwtMiddleware.js.map +1 -1
- package/dist/src/middleware/tokenTransformationMiddleware.js.map +1 -1
- package/dist/src/models/customError.js +2 -3
- package/dist/src/models/customError.js.map +1 -1
- package/dist/src/models/gluuUserType.js +1 -1
- package/dist/src/models/gluuUserType.js.map +1 -1
- package/dist/src/models/identities.d.ts +523 -0
- package/dist/src/models/identities.js +57 -0
- package/dist/src/models/identities.js.map +1 -0
- package/dist/src/models/publicKey.d.ts +0 -1
- package/dist/src/models/publicKey.js +1 -1
- package/dist/src/models/publicKey.js.map +1 -1
- package/dist/src/repositories/cachedPublicKeyRepository.js.map +1 -1
- package/dist/src/repositories/publicKeyRepository.js.map +1 -1
- package/dist/src/userValidator.js.map +1 -1
- package/dist/src/userValidator.test.js.map +1 -1
- package/dist/src/utils/createIdentityFromJwt.d.ts +39 -0
- package/dist/src/utils/createIdentityFromJwt.js +464 -0
- package/dist/src/utils/createIdentityFromJwt.js.map +1 -0
- package/dist/src/utils/createIdentityFromJwt.test.d.ts +1 -0
- package/dist/src/utils/createIdentityFromJwt.test.js +1433 -0
- package/dist/src/utils/createIdentityFromJwt.test.js.map +1 -0
- package/dist/src/utils/jwtMock.js.map +1 -1
- package/dist/src/utils/logger.js +2 -3
- package/dist/src/utils/logger.js.map +1 -1
- package/dist/src/utils/testingConfigurations.js +1 -2
- package/dist/src/utils/testingConfigurations.js.map +1 -1
- package/package.json +30 -30
- package/src/index.ts +1 -0
- package/src/models/identities.ts +621 -0
- package/src/utils/createIdentityFromJwt.test.ts +1595 -0
- package/src/utils/createIdentityFromJwt.ts +540 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccountProfile,
|
|
3
|
+
AnonymousIdentity,
|
|
4
|
+
CitizenIdentity,
|
|
5
|
+
EmployeeIdentity,
|
|
6
|
+
ExternalUserIdentity,
|
|
7
|
+
GenericUserIdentity,
|
|
8
|
+
GuestUserIdentity,
|
|
9
|
+
Identity,
|
|
10
|
+
ServiceAccountIdentity,
|
|
11
|
+
UnknownIdentity,
|
|
12
|
+
UnknownUserIdentity,
|
|
13
|
+
UserServiceAccountIdentity,
|
|
14
|
+
} from '../models/identities';
|
|
15
|
+
|
|
16
|
+
const usernameClaimName = 'userName';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* creates a specific type of Identity from the submitted JWT.
|
|
20
|
+
* @param jwt specifies the JWT issued by TokenAPI
|
|
21
|
+
* @returns a specific type of Identity
|
|
22
|
+
* @description The purpose of this function is to classify the type of user behind the access token and select the right claim
|
|
23
|
+
* for the unique ID of the account, as we need the most stable ID as possible.
|
|
24
|
+
*
|
|
25
|
+
* There is a displayName attribute when you need to display the identity in a GUI.
|
|
26
|
+
*
|
|
27
|
+
* Also, we provide a toString() function to format a verbose representation of the identity, used to audit as much information as possible.
|
|
28
|
+
* @example
|
|
29
|
+
* const identity = createIdentityFromJwt(req.jwt);
|
|
30
|
+
* product.name = newName;
|
|
31
|
+
* product.modifiedBy = identity.id; // udoejo3
|
|
32
|
+
* @example
|
|
33
|
+
* const identity = createIdentityFromJwt(req.jwt);
|
|
34
|
+
* console.log(`Order created by "${identity}"`); // Order created by "user:employee:udoejo3:John DOE:john.doe@montreal.ca:100674051:421408000000:vdm"
|
|
35
|
+
* @example
|
|
36
|
+
* const identity = createIdentityFromJwt(req.jwt);
|
|
37
|
+
* res.send({ message: `Welcome back ${identity.displayName}`}); // { message: "Welcome back John DOE" }
|
|
38
|
+
* @example
|
|
39
|
+
* const identity = createIdentityFromJwt(req.jwt);
|
|
40
|
+
* if (identity.type === 'user' && identity.attributes.type === 'employee') {
|
|
41
|
+
* const employeeRecord = await employeeAPI.findEmployee(identity.attributes.registrationNumber);
|
|
42
|
+
* } else {
|
|
43
|
+
* throw new Error(`Expected an employee but received "${identity}"`);
|
|
44
|
+
* }
|
|
45
|
+
* @example
|
|
46
|
+
* const identity = createIdentityFromJwt(req.jwt);
|
|
47
|
+
* if (identity.type === 'user') {
|
|
48
|
+
* if (identity.attributes.email) {
|
|
49
|
+
* emailService.send(identity.attributes.email, 'Welcome', 'Bla bla...');
|
|
50
|
+
* } else {
|
|
51
|
+
* throw new Error(`User "${identity}" has no email`);
|
|
52
|
+
* }
|
|
53
|
+
* }
|
|
54
|
+
*/
|
|
55
|
+
export function createIdentityFromJwt(jwt: any): Identity {
|
|
56
|
+
if (jwt === null || jwt === undefined) {
|
|
57
|
+
throw new Error('"jwt" parameter is required');
|
|
58
|
+
}
|
|
59
|
+
const issuer = getStringClaim(jwt, 'iss');
|
|
60
|
+
const realm = getStringClaim(jwt, 'realm');
|
|
61
|
+
const aud = getStringClaim(jwt, 'aud');
|
|
62
|
+
const sub = getStringClaim(jwt, 'sub');
|
|
63
|
+
const oid = getOptionalStringClaim(jwt, 'oid');
|
|
64
|
+
const env = getOptionalStringClaim(jwt, 'env');
|
|
65
|
+
const userType = getOptionalStringClaim(jwt, 'userType') ?? 'citizen';
|
|
66
|
+
const accessTokenIssuer = getOptionalStringClaim(jwt, 'accessTokenIssuer');
|
|
67
|
+
const isGenericUser = jwt.isGenericAccount;
|
|
68
|
+
|
|
69
|
+
//----------< Anonymous user >-----------------------------------------
|
|
70
|
+
if (userType === 'anonymous') {
|
|
71
|
+
const type = 'anonymous';
|
|
72
|
+
if (realm !== 'anonymous') {
|
|
73
|
+
throw new Error(`${type}: expected token to belong to the "anonymous" realm`);
|
|
74
|
+
}
|
|
75
|
+
const username = getStringClaim(jwt, usernameClaimName, type);
|
|
76
|
+
const result: AnonymousIdentity = {
|
|
77
|
+
type,
|
|
78
|
+
id: username,
|
|
79
|
+
displayName: getStringClaim(jwt, 'name', type),
|
|
80
|
+
attributes: {
|
|
81
|
+
type: 'anonymous',
|
|
82
|
+
username,
|
|
83
|
+
},
|
|
84
|
+
source: {
|
|
85
|
+
aud,
|
|
86
|
+
issuer,
|
|
87
|
+
accessTokenIssuer,
|
|
88
|
+
env,
|
|
89
|
+
realm,
|
|
90
|
+
claim: usernameClaimName,
|
|
91
|
+
internalId: sub,
|
|
92
|
+
},
|
|
93
|
+
toString(this: AnonymousIdentity) {
|
|
94
|
+
return encodeComponents(this.type, this.id, this.displayName);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
//----------< Client Service account >-----------------------------------------
|
|
100
|
+
if (userType === 'client') {
|
|
101
|
+
const type = 'service-account';
|
|
102
|
+
const subType = 'client';
|
|
103
|
+
const result: ServiceAccountIdentity = {
|
|
104
|
+
type,
|
|
105
|
+
id: aud, // Gluu clientID or Azure appId
|
|
106
|
+
displayName: getStringClaim(jwt, 'displayName', type, subType),
|
|
107
|
+
attributes: {
|
|
108
|
+
type: subType,
|
|
109
|
+
},
|
|
110
|
+
source: {
|
|
111
|
+
aud,
|
|
112
|
+
issuer,
|
|
113
|
+
accessTokenIssuer,
|
|
114
|
+
env,
|
|
115
|
+
realm,
|
|
116
|
+
claim: 'aud',
|
|
117
|
+
internalId: oid ?? sub,
|
|
118
|
+
},
|
|
119
|
+
toString(this: ServiceAccountIdentity) {
|
|
120
|
+
return encodeComponents(this.type, this.attributes.type, this.id, this.displayName);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
//----------< User Service account >-----------------------------------------
|
|
126
|
+
if (userType === 'serviceAccount') {
|
|
127
|
+
const type = 'service-account';
|
|
128
|
+
const subType = 'user';
|
|
129
|
+
const username = getStringClaim(jwt, usernameClaimName, type, subType);
|
|
130
|
+
const result: UserServiceAccountIdentity = {
|
|
131
|
+
type,
|
|
132
|
+
id: username,
|
|
133
|
+
displayName: getStringClaim(jwt, 'name', type, subType),
|
|
134
|
+
attributes: {
|
|
135
|
+
type: subType,
|
|
136
|
+
username,
|
|
137
|
+
},
|
|
138
|
+
source: {
|
|
139
|
+
aud,
|
|
140
|
+
issuer,
|
|
141
|
+
accessTokenIssuer,
|
|
142
|
+
env,
|
|
143
|
+
realm,
|
|
144
|
+
claim: usernameClaimName,
|
|
145
|
+
internalId: sub,
|
|
146
|
+
},
|
|
147
|
+
toString(this: UserServiceAccountIdentity) {
|
|
148
|
+
return encodeComponents(this.type, this.attributes.type, this.id, this.displayName);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
//----------< Citizen >-----------------------------------------
|
|
154
|
+
if (userType === 'citizen') {
|
|
155
|
+
const type = 'user';
|
|
156
|
+
const subType = 'citizen';
|
|
157
|
+
if (realm !== 'citizens') {
|
|
158
|
+
throw new Error(`${type}:${subType}: expected token to belong to the "citizens" realm`);
|
|
159
|
+
}
|
|
160
|
+
const result: CitizenIdentity = {
|
|
161
|
+
type,
|
|
162
|
+
id: getStringClaim(jwt, 'mtlIdentityId', type, subType),
|
|
163
|
+
displayName: getStringClaim(jwt, 'name', type, subType),
|
|
164
|
+
attributes: {
|
|
165
|
+
type: subType,
|
|
166
|
+
username: getStringClaim(jwt, usernameClaimName, type, subType),
|
|
167
|
+
email: getStringClaim(jwt, 'email', type, subType),
|
|
168
|
+
firstName: getStringClaim(jwt, 'givenName', type, subType),
|
|
169
|
+
lastName: getStringClaim(jwt, 'familyName', type, subType),
|
|
170
|
+
},
|
|
171
|
+
source: {
|
|
172
|
+
aud,
|
|
173
|
+
issuer,
|
|
174
|
+
accessTokenIssuer,
|
|
175
|
+
env,
|
|
176
|
+
realm,
|
|
177
|
+
claim: 'mtlIdentityId',
|
|
178
|
+
internalId: oid ?? sub,
|
|
179
|
+
},
|
|
180
|
+
toString(this: CitizenIdentity) {
|
|
181
|
+
return encodeComponents(
|
|
182
|
+
this.type,
|
|
183
|
+
this.attributes.type,
|
|
184
|
+
this.id,
|
|
185
|
+
this.displayName,
|
|
186
|
+
this.attributes.email
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
//----------< Generic user >-----------------------------------------
|
|
193
|
+
if (isGenericUser === true) {
|
|
194
|
+
const type = 'user';
|
|
195
|
+
const subType = 'generic-user';
|
|
196
|
+
const username = getStringClaim(jwt, usernameClaimName, type, subType);
|
|
197
|
+
const result: GenericUserIdentity = {
|
|
198
|
+
type,
|
|
199
|
+
id: username,
|
|
200
|
+
displayName: getStringClaim(jwt, 'name', type, subType),
|
|
201
|
+
attributes: {
|
|
202
|
+
type: 'generic',
|
|
203
|
+
username,
|
|
204
|
+
email: getOptionalStringClaim(jwt, 'email'),
|
|
205
|
+
department: getOptionalStringClaim(jwt, 'department'),
|
|
206
|
+
firstName: getStringClaim(jwt, 'givenName', type, subType),
|
|
207
|
+
lastName: getStringClaim(jwt, 'familyName', type, subType),
|
|
208
|
+
accountProfile: getAccountProfile(jwt),
|
|
209
|
+
},
|
|
210
|
+
source: {
|
|
211
|
+
aud,
|
|
212
|
+
issuer,
|
|
213
|
+
accessTokenIssuer,
|
|
214
|
+
env,
|
|
215
|
+
realm,
|
|
216
|
+
claim: usernameClaimName,
|
|
217
|
+
internalId: oid ?? sub,
|
|
218
|
+
},
|
|
219
|
+
toString(this: GenericUserIdentity) {
|
|
220
|
+
return encodeComponents(
|
|
221
|
+
this.type,
|
|
222
|
+
this.attributes.type,
|
|
223
|
+
this.id,
|
|
224
|
+
this.displayName,
|
|
225
|
+
this.attributes.email,
|
|
226
|
+
this.attributes.department,
|
|
227
|
+
this.attributes.accountProfile
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
//----------< Employee >-----------------------------------------
|
|
234
|
+
if (userType === 'employee' && isEmployee(jwt)) {
|
|
235
|
+
const type = 'user';
|
|
236
|
+
const subType = 'employee';
|
|
237
|
+
if (realm !== 'employees') {
|
|
238
|
+
throw new Error(`${type}:${subType}: expected token to belong to the "employees" realm`);
|
|
239
|
+
}
|
|
240
|
+
const username = getStringClaim(jwt, usernameClaimName, type, subType);
|
|
241
|
+
const result: EmployeeIdentity = {
|
|
242
|
+
type,
|
|
243
|
+
id: username,
|
|
244
|
+
displayName: getStringClaim(jwt, 'name', type, subType),
|
|
245
|
+
attributes: {
|
|
246
|
+
type: subType,
|
|
247
|
+
email: getStringClaim(jwt, 'email', type, subType),
|
|
248
|
+
username,
|
|
249
|
+
registrationNumber: getStringClaim(jwt, 'employeeNumber', type, subType),
|
|
250
|
+
department: getStringClaim(jwt, 'department', type, subType),
|
|
251
|
+
firstName: getStringClaim(jwt, 'givenName', type, subType),
|
|
252
|
+
lastName: getStringClaim(jwt, 'familyName', type, subType),
|
|
253
|
+
accountProfile: getAccountProfile(jwt),
|
|
254
|
+
},
|
|
255
|
+
source: {
|
|
256
|
+
aud,
|
|
257
|
+
issuer,
|
|
258
|
+
accessTokenIssuer,
|
|
259
|
+
env,
|
|
260
|
+
realm,
|
|
261
|
+
claim: usernameClaimName,
|
|
262
|
+
internalId: oid ?? sub,
|
|
263
|
+
},
|
|
264
|
+
toString(this: EmployeeIdentity) {
|
|
265
|
+
return encodeComponents(
|
|
266
|
+
this.type,
|
|
267
|
+
this.attributes.type,
|
|
268
|
+
this.id,
|
|
269
|
+
this.displayName,
|
|
270
|
+
this.attributes.email,
|
|
271
|
+
this.attributes.registrationNumber,
|
|
272
|
+
this.attributes.department,
|
|
273
|
+
this.attributes.accountProfile
|
|
274
|
+
);
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
//----------< External user >-----------------------------------------
|
|
280
|
+
if (userType === 'employee' && isExternalUser(jwt)) {
|
|
281
|
+
const type = 'user';
|
|
282
|
+
const subType = 'external';
|
|
283
|
+
if (realm !== 'employees') {
|
|
284
|
+
throw new Error(`${type}:${subType}: expected token to belong to the "employees" realm`);
|
|
285
|
+
}
|
|
286
|
+
const username = getStringClaim(jwt, usernameClaimName, type, subType);
|
|
287
|
+
const result: ExternalUserIdentity = {
|
|
288
|
+
type,
|
|
289
|
+
id: username,
|
|
290
|
+
displayName: getStringClaim(jwt, 'name', type, subType),
|
|
291
|
+
attributes: {
|
|
292
|
+
type: subType,
|
|
293
|
+
email: getOptionalStringClaim(jwt, 'email'),
|
|
294
|
+
username,
|
|
295
|
+
department: getOptionalStringClaim(jwt, 'department'),
|
|
296
|
+
firstName: getStringClaim(jwt, 'givenName', type, subType),
|
|
297
|
+
lastName: getStringClaim(jwt, 'familyName', type, subType),
|
|
298
|
+
accountProfile: getAccountProfile(jwt),
|
|
299
|
+
},
|
|
300
|
+
source: {
|
|
301
|
+
aud,
|
|
302
|
+
issuer,
|
|
303
|
+
accessTokenIssuer,
|
|
304
|
+
env,
|
|
305
|
+
realm,
|
|
306
|
+
claim: usernameClaimName,
|
|
307
|
+
internalId: oid ?? sub,
|
|
308
|
+
},
|
|
309
|
+
toString(this: ExternalUserIdentity) {
|
|
310
|
+
return encodeComponents(
|
|
311
|
+
this.type,
|
|
312
|
+
this.attributes.type,
|
|
313
|
+
this.id,
|
|
314
|
+
this.displayName,
|
|
315
|
+
this.attributes.email,
|
|
316
|
+
this.attributes.department,
|
|
317
|
+
this.attributes.accountProfile
|
|
318
|
+
);
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
//----------< Guest user >-----------------------------------------
|
|
324
|
+
if (isGuestUser(jwt)) {
|
|
325
|
+
const type = 'user';
|
|
326
|
+
const subType = 'guest-user';
|
|
327
|
+
const username = getStringClaim(jwt, usernameClaimName, type, subType);
|
|
328
|
+
const result: GuestUserIdentity = {
|
|
329
|
+
type,
|
|
330
|
+
id: username,
|
|
331
|
+
displayName: getStringClaim(jwt, 'name', type, subType),
|
|
332
|
+
attributes: {
|
|
333
|
+
type: 'guest',
|
|
334
|
+
email: getStringClaim(jwt, 'email', type, subType),
|
|
335
|
+
username,
|
|
336
|
+
department: getOptionalStringClaim(jwt, 'department'),
|
|
337
|
+
firstName: getOptionalStringClaim(jwt, 'givenName'),
|
|
338
|
+
lastName: getOptionalStringClaim(jwt, 'familyName'),
|
|
339
|
+
accountProfile: getAccountProfile(jwt),
|
|
340
|
+
},
|
|
341
|
+
source: {
|
|
342
|
+
aud,
|
|
343
|
+
issuer,
|
|
344
|
+
accessTokenIssuer,
|
|
345
|
+
env,
|
|
346
|
+
realm,
|
|
347
|
+
claim: usernameClaimName,
|
|
348
|
+
internalId: oid ?? sub,
|
|
349
|
+
},
|
|
350
|
+
toString(this: GuestUserIdentity) {
|
|
351
|
+
return encodeComponents(
|
|
352
|
+
this.type,
|
|
353
|
+
this.attributes.type,
|
|
354
|
+
realm,
|
|
355
|
+
this.id,
|
|
356
|
+
this.displayName,
|
|
357
|
+
this.attributes.email
|
|
358
|
+
);
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
//----------< Unknown user type >-----------------------------------------
|
|
364
|
+
const username = getOptionalStringClaim(jwt, usernameClaimName);
|
|
365
|
+
const email = getOptionalStringClaim(jwt, 'email');
|
|
366
|
+
if (username || email) {
|
|
367
|
+
const type = 'user';
|
|
368
|
+
const subType = 'unknown';
|
|
369
|
+
const claim = username ? usernameClaimName : 'email';
|
|
370
|
+
const result: UnknownUserIdentity = {
|
|
371
|
+
type,
|
|
372
|
+
id: getStringClaim(jwt, claim, type, subType),
|
|
373
|
+
displayName: getOptionalStringClaim(jwt, 'name') ?? email ?? username,
|
|
374
|
+
attributes: {
|
|
375
|
+
type: subType,
|
|
376
|
+
email: getOptionalStringClaim(jwt, 'email'),
|
|
377
|
+
username,
|
|
378
|
+
registrationNumber: getOptionalStringClaim(jwt, 'employeeNumber'),
|
|
379
|
+
department: getOptionalStringClaim(jwt, 'department'),
|
|
380
|
+
firstName: getOptionalStringClaim(jwt, 'givenName'),
|
|
381
|
+
lastName: getOptionalStringClaim(jwt, 'familyName'),
|
|
382
|
+
accountProfile: getAccountProfile(jwt),
|
|
383
|
+
},
|
|
384
|
+
source: {
|
|
385
|
+
aud,
|
|
386
|
+
issuer,
|
|
387
|
+
accessTokenIssuer,
|
|
388
|
+
env,
|
|
389
|
+
realm,
|
|
390
|
+
claim: claim,
|
|
391
|
+
internalId: oid ?? sub,
|
|
392
|
+
},
|
|
393
|
+
toString(this: UnknownUserIdentity) {
|
|
394
|
+
return encodeComponents(
|
|
395
|
+
this.type,
|
|
396
|
+
this.attributes.type,
|
|
397
|
+
this.id,
|
|
398
|
+
this.displayName,
|
|
399
|
+
this.attributes.email,
|
|
400
|
+
this.attributes.registrationNumber,
|
|
401
|
+
this.attributes.department,
|
|
402
|
+
this.attributes.accountProfile
|
|
403
|
+
);
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
//----------< Unknown identity type >-----------------------------------------
|
|
409
|
+
const result: UnknownIdentity = {
|
|
410
|
+
type: 'unknown',
|
|
411
|
+
id: sub,
|
|
412
|
+
displayName: getOptionalStringClaim(jwt, 'name') ?? 'unknown',
|
|
413
|
+
attributes: {
|
|
414
|
+
type: 'unknown',
|
|
415
|
+
},
|
|
416
|
+
source: {
|
|
417
|
+
aud,
|
|
418
|
+
issuer,
|
|
419
|
+
accessTokenIssuer,
|
|
420
|
+
env,
|
|
421
|
+
realm,
|
|
422
|
+
claim: 'sub',
|
|
423
|
+
internalId: oid ?? sub,
|
|
424
|
+
},
|
|
425
|
+
toString(this: UnknownIdentity) {
|
|
426
|
+
return encodeComponents(this.type, this.id, this.displayName);
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function getOptionalStringClaim(jwt: any, name: string): string | undefined {
|
|
433
|
+
const result = jwt[name];
|
|
434
|
+
if (result === undefined || result === null) {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
if (typeof result !== 'string') {
|
|
438
|
+
throw new Error(`Expected claim '${name}' to contain a string but received: ${result}`);
|
|
439
|
+
}
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function getStringClaim(
|
|
444
|
+
jwt: any,
|
|
445
|
+
name: string,
|
|
446
|
+
identityType?: string,
|
|
447
|
+
identitySubType?: string
|
|
448
|
+
): string | undefined {
|
|
449
|
+
const result = getOptionalStringClaim(jwt, name);
|
|
450
|
+
if (!result) {
|
|
451
|
+
const subType = identitySubType ? `${identitySubType}: ` : '';
|
|
452
|
+
const prefix = (identityType ? `${identityType}: ` : '') + subType;
|
|
453
|
+
throw new Error(`${prefix}expected to find the "${name}" claim in the JWT`);
|
|
454
|
+
}
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function encodeComponents(...components: string[]): string {
|
|
459
|
+
return components.map((x) => (x ?? '').replace(':', '_')).join(':');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function getAccountProfile(jwt: any): AccountProfile {
|
|
463
|
+
const email = getOptionalStringClaim(jwt, 'email');
|
|
464
|
+
if (email) {
|
|
465
|
+
if (email.endsWith('@spvm.qc.ca')) {
|
|
466
|
+
return 'spvm';
|
|
467
|
+
}
|
|
468
|
+
if (email.endsWith('.adm@lavilledemontreal.omnicrosoft.com')) {
|
|
469
|
+
return 'vdm-admin';
|
|
470
|
+
}
|
|
471
|
+
if (email.toLocaleLowerCase().endsWith('.adm@montrealville.omnicrosoft.com')) {
|
|
472
|
+
return 'vdm-admin';
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return 'vdm';
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function isValidCodeU(code: string): boolean {
|
|
479
|
+
const re = /^u[a-z0-9]{4,22}$/gi;
|
|
480
|
+
return re.test(code);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function isEmployee(jwt: any): boolean {
|
|
484
|
+
const username = getOptionalStringClaim(jwt, usernameClaimName);
|
|
485
|
+
if (!username) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
const employeeNumber = getOptionalStringClaim(jwt, 'employeeNumber');
|
|
489
|
+
const department = getOptionalStringClaim(jwt, 'department');
|
|
490
|
+
const name = getOptionalStringClaim(jwt, 'name');
|
|
491
|
+
const firstName = getOptionalStringClaim(jwt, 'givenName');
|
|
492
|
+
const lastName = getOptionalStringClaim(jwt, 'familyName');
|
|
493
|
+
const isCodeU = isValidCodeU(username);
|
|
494
|
+
const hasEmployeeNumber = !!employeeNumber;
|
|
495
|
+
const hasDepartment = !!department;
|
|
496
|
+
const hasNames = !!name && !!firstName && !!lastName;
|
|
497
|
+
return isCodeU && hasEmployeeNumber && hasDepartment && hasNames;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function isValidCodeX(code: string): boolean {
|
|
501
|
+
const re = /^x[a-z0-9]{4,22}$/gi;
|
|
502
|
+
return re.test(code);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function isExternalUser(jwt: any): boolean {
|
|
506
|
+
const username = getOptionalStringClaim(jwt, usernameClaimName);
|
|
507
|
+
if (!username) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
const name = getOptionalStringClaim(jwt, 'name');
|
|
511
|
+
const firstName = getOptionalStringClaim(jwt, 'givenName');
|
|
512
|
+
const lastName = getOptionalStringClaim(jwt, 'familyName');
|
|
513
|
+
const hasNames = !!name && !!firstName && !!lastName;
|
|
514
|
+
if (!hasNames) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
const email = getOptionalStringClaim(jwt, 'email');
|
|
518
|
+
const isCodeX = isValidCodeX(username);
|
|
519
|
+
const isExtEmail = email && email.toLocaleLowerCase().includes('.ext@');
|
|
520
|
+
return isCodeX || isExtEmail;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function isGuestUser(jwt: any): boolean {
|
|
524
|
+
const username = getOptionalStringClaim(jwt, usernameClaimName);
|
|
525
|
+
if (!username) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
const email = getOptionalStringClaim(jwt, 'email');
|
|
529
|
+
if (!email) {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
const name = getOptionalStringClaim(jwt, 'name');
|
|
533
|
+
if (!name) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
return (
|
|
537
|
+
username.endsWith('#EXT#@lavilledemontreal.omnicrosoft.com') ||
|
|
538
|
+
username.endsWith('#EXT#@MontrealVille.onmicrosoft.com')
|
|
539
|
+
);
|
|
540
|
+
}
|