scimgateway 5.4.3 → 5.4.4
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 +27 -4
- package/bun.lock +22 -0
- package/config/plugin-api.json +1 -0
- package/config/plugin-entra-id.json +1 -0
- package/config/plugin-ldap.json +1 -0
- package/config/plugin-loki.json +1 -0
- package/config/plugin-mongodb.json +1 -0
- package/config/plugin-mssql.json +1 -0
- package/config/plugin-saphana.json +1 -0
- package/config/plugin-scim.json +1 -0
- package/config/plugin-soap.json +1 -0
- package/lib/scimgateway.ts +66 -26
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ Validated through IdP's:
|
|
|
16
16
|
|
|
17
17
|
Latest news:
|
|
18
18
|
|
|
19
|
+
- External JWKS (JSON Web Key Set) is now supported by JWT Authentication. These are public and typically frequent rotated by modern identity providers
|
|
19
20
|
- [Azure Relay](https://learn.microsoft.com/en-us/azure/azure-relay/relay-what-is-it) is now supported for secure and hassle-free outbound communication — with just one minute of configuration
|
|
20
21
|
- [ETag](https://datatracker.ietf.org/doc/html/rfc7644#section-3.14) is now supported
|
|
21
22
|
- [Bulk Operations](https://datatracker.ietf.org/doc/html/rfc7644#section-3.7) is now supported
|
|
@@ -417,7 +418,7 @@ Definitions in `endpoint` object are customized according to our plugin code. Pl
|
|
|
417
418
|
|
|
418
419
|
- **auth.bearerJwtAzure** - Array of one or more JWT used by Azure SyncFabric. **tenantIdGUID** must be set to Entra ID Tenant ID.
|
|
419
420
|
|
|
420
|
-
- **auth.bearerJwt** - Array of one or more standard JWT objects. Using **secret** or **
|
|
421
|
+
- **auth.bearerJwt** - Array of one or more standard JWT objects. Using **secret**, **publicKey** or **wellKnownUri** for signature verification. publicKey should be set to the filename of public key or certificate pem-file located in `<package-root>\config\certs` or absolute path being used. Clear text secret will become encrypted when gateway is started. For JWKS (JSON Web Key Set), the **wellKnownUri** must be set to identity provider well-known URI which will be used for lookup the jwks_uri key. **options.issuer** should normally be set for validation when using secret or publicKey. Other options may also be included according to jsonwebtoken npm package definition.
|
|
421
422
|
|
|
422
423
|
- **auth.bearerOAuth** - Array of one or more Client Credentials OAuth configuration objects. **`clientId`** and **`clientSecret`** are mandatory. clientSecret value will become encrypted when gateway is started. OAuth token request url is **/oauth/token** e.g. `http://localhost:8880/oauth/token`
|
|
423
424
|
|
|
@@ -816,7 +817,7 @@ const messageHandler = async (message: string) => {
|
|
|
816
817
|
console.log(message)
|
|
817
818
|
}
|
|
818
819
|
|
|
819
|
-
async function
|
|
820
|
+
async function startup() {
|
|
820
821
|
while (true) {
|
|
821
822
|
try {
|
|
822
823
|
const resp = await fetch(url, { headers });
|
|
@@ -847,7 +848,7 @@ async function startSSE() {
|
|
|
847
848
|
}
|
|
848
849
|
}
|
|
849
850
|
|
|
850
|
-
|
|
851
|
+
startup()
|
|
851
852
|
```
|
|
852
853
|
|
|
853
854
|
### Configuration notes - Azure Relay
|
|
@@ -1466,7 +1467,29 @@ In code editor (e.g., Visual Studio Code), method details and documentation are
|
|
|
1466
1467
|
MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
1467
1468
|
|
|
1468
1469
|
|
|
1469
|
-
## Change log
|
|
1470
|
+
## Change log
|
|
1471
|
+
|
|
1472
|
+
### v5.4.4
|
|
1473
|
+
|
|
1474
|
+
[Improved]
|
|
1475
|
+
|
|
1476
|
+
- External JWKS (JSON Web Key Set) is now supported by JWT Authentication. These are public and typically frequent rotated by modern identity providers
|
|
1477
|
+
|
|
1478
|
+
JKWS is enabled by setting scimgateway.auth.bearerJwt[].wellKnownUri to the identity provider's well-known URI
|
|
1479
|
+
|
|
1480
|
+
Keycloak example:
|
|
1481
|
+
|
|
1482
|
+
auth: {
|
|
1483
|
+
"bearerJwt": [
|
|
1484
|
+
{
|
|
1485
|
+
"wellKnownUri": "https://keycloak.example.com/realms/example-realm/.well-known/openid-configuration",
|
|
1486
|
+
"options": {
|
|
1487
|
+
...
|
|
1488
|
+
},
|
|
1489
|
+
...
|
|
1490
|
+
}
|
|
1491
|
+
]
|
|
1492
|
+
}
|
|
1470
1493
|
|
|
1471
1494
|
### v5.4.3
|
|
1472
1495
|
|
package/bun.lock
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"hyco-https": "^1.4.5",
|
|
18
18
|
"is-in-subnet": "^4.0.1",
|
|
19
19
|
"jsonwebtoken": "^9.0.2",
|
|
20
|
+
"jwk-to-pem": "^2.0.7",
|
|
20
21
|
"ldapjs": "^3.0.7",
|
|
21
22
|
"lokijs": "^1.5.12",
|
|
22
23
|
"mongodb": "^6.16.0",
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
"@types/bun": "latest",
|
|
32
33
|
"@types/dot-object": "^2.1.6",
|
|
33
34
|
"@types/jsonwebtoken": "^9.0.9",
|
|
35
|
+
"@types/jwk-to-pem": "^2.0.3",
|
|
34
36
|
"@types/node": "latest",
|
|
35
37
|
"@types/nodemailer": "^6.4.17",
|
|
36
38
|
"@types/passport": "^1.0.17",
|
|
@@ -163,6 +165,8 @@
|
|
|
163
165
|
|
|
164
166
|
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="],
|
|
165
167
|
|
|
168
|
+
"@types/jwk-to-pem": ["@types/jwk-to-pem@2.0.3", "", {}, "sha512-I/WFyFgk5GrNbkpmt14auGO3yFK1Wt4jXzkLuI+fDBNtO5ZI2rbymyGd6bKzfSBEuyRdM64ZUwxU1+eDcPSOEQ=="],
|
|
169
|
+
|
|
166
170
|
"@types/ldapjs": ["@types/ldapjs@3.0.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-E2Tn1ltJDYBsidOT9QG4engaQeQzRQ9aYNxVmjCkD33F7cIeLPgrRDXAYs0O35mK2YDU20c/+ZkNjeAPRGLM0Q=="],
|
|
167
171
|
|
|
168
172
|
"@types/lokijs": ["@types/lokijs@1.5.14", "", {}, "sha512-4Fic47BX3Qxr8pd12KT6/T1XWU8dOlJBIp1jGoMbaDbiEvdv50rAii+B3z1b/J2pvMywcVP+DBPGP5/lgLOKGA=="],
|
|
@@ -235,6 +239,8 @@
|
|
|
235
239
|
|
|
236
240
|
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
|
237
241
|
|
|
242
|
+
"asn1.js": ["asn1.js@5.4.1", "", { "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0", "safer-buffer": "^2.1.0" } }, "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA=="],
|
|
243
|
+
|
|
238
244
|
"assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="],
|
|
239
245
|
|
|
240
246
|
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
|
|
@@ -253,10 +259,14 @@
|
|
|
253
259
|
|
|
254
260
|
"bl": ["bl@6.1.0", "", { "dependencies": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^4.2.0" } }, "sha512-ClDyJGQkc8ZtzdAAbAwBmhMSpwN/sC9HA8jxdYm6nVUbCfZbe2mgza4qh7AuEYyEPB/c4Kznf9s66bnsKMQDjw=="],
|
|
255
261
|
|
|
262
|
+
"bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],
|
|
263
|
+
|
|
256
264
|
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
257
265
|
|
|
258
266
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
|
259
267
|
|
|
268
|
+
"brorand": ["brorand@1.1.0", "", {}, "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="],
|
|
269
|
+
|
|
260
270
|
"bson": ["bson@6.10.4", "", {}, "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="],
|
|
261
271
|
|
|
262
272
|
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
|
@@ -315,6 +325,8 @@
|
|
|
315
325
|
|
|
316
326
|
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
|
317
327
|
|
|
328
|
+
"elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="],
|
|
329
|
+
|
|
318
330
|
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
|
319
331
|
|
|
320
332
|
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
|
@@ -399,8 +411,12 @@
|
|
|
399
411
|
|
|
400
412
|
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
|
401
413
|
|
|
414
|
+
"hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="],
|
|
415
|
+
|
|
402
416
|
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
|
403
417
|
|
|
418
|
+
"hmac-drbg": ["hmac-drbg@1.0.1", "", { "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg=="],
|
|
419
|
+
|
|
404
420
|
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
|
405
421
|
|
|
406
422
|
"https": ["https@1.0.0", "", {}, "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="],
|
|
@@ -463,6 +479,8 @@
|
|
|
463
479
|
|
|
464
480
|
"jwa": ["jwa@1.4.2", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw=="],
|
|
465
481
|
|
|
482
|
+
"jwk-to-pem": ["jwk-to-pem@2.0.7", "", { "dependencies": { "asn1.js": "^5.3.0", "elliptic": "^6.6.1", "safe-buffer": "^5.0.1" } }, "sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ=="],
|
|
483
|
+
|
|
466
484
|
"jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
|
467
485
|
|
|
468
486
|
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
|
@@ -507,6 +525,10 @@
|
|
|
507
525
|
|
|
508
526
|
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
|
509
527
|
|
|
528
|
+
"minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="],
|
|
529
|
+
|
|
530
|
+
"minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="],
|
|
531
|
+
|
|
510
532
|
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
511
533
|
|
|
512
534
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
package/config/plugin-api.json
CHANGED
package/config/plugin-ldap.json
CHANGED
package/config/plugin-loki.json
CHANGED
package/config/plugin-mssql.json
CHANGED
package/config/plugin-scim.json
CHANGED
package/config/plugin-soap.json
CHANGED
package/lib/scimgateway.ts
CHANGED
|
@@ -21,6 +21,7 @@ import dot from 'dot-object'
|
|
|
21
21
|
import nodemailer from 'nodemailer'
|
|
22
22
|
import fs from 'node:fs'
|
|
23
23
|
import path from 'node:path'
|
|
24
|
+
import jwkToPem from 'jwk-to-pem'
|
|
24
25
|
import * as jwt from 'jsonwebtoken'
|
|
25
26
|
import * as utils from './utils.ts'
|
|
26
27
|
import * as utilsScim from './utils-scim.ts'
|
|
@@ -37,7 +38,6 @@ export class ScimGateway {
|
|
|
37
38
|
private getMemberOf: any
|
|
38
39
|
private getAppRoles: any
|
|
39
40
|
private pub: any
|
|
40
|
-
private Nonce = new utils.TimerMapCache(2000)
|
|
41
41
|
// @ts-expect-error: has no initializer
|
|
42
42
|
private helperRest: HelperRest
|
|
43
43
|
/** pluginName is the name of plugin e.g., plugin-loki */
|
|
@@ -670,37 +670,78 @@ export class ScimGateway {
|
|
|
670
670
|
})
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
673
|
+
const getJwksPemKey = async (kid: string, wellKnownUri: string): Promise<Array<any>> => { // retrieves "JSON Web Key Set" from well-known jwks_uri and returns the public key that corresponds with the access token kid value
|
|
674
|
+
if (!this.helperRest) this.helperRest = this.newHelperRest()
|
|
675
|
+
let res
|
|
676
|
+
try { // get issuer and jwks_uri from well-knonw uri
|
|
677
|
+
res = await this.helperRest.doRequest('undefined', 'GET', wellKnownUri)
|
|
678
|
+
} catch (err: any) {
|
|
679
|
+
logger.error(`${gwName}[${pluginName}] JWKS wellKnownUri=${wellKnownUri} error: ${err.message}`)
|
|
680
|
+
return []
|
|
681
|
+
}
|
|
682
|
+
if (!res?.body) return []
|
|
683
|
+
const issuer = res.body.issuer
|
|
684
|
+
const jwks_uri = res.body.jwks_uri
|
|
685
|
+
if (!issuer || !jwks_uri) {
|
|
686
|
+
logger.error(`${gwName}[${pluginName}] JWKS wellKnownUri=${wellKnownUri} error: found issuer=${issuer} and jwks_uri=${jwks_uri} - both should be found`)
|
|
687
|
+
return []
|
|
688
|
+
}
|
|
689
|
+
// JWKS
|
|
690
|
+
try { // get jwks that correspods with kid from jwks_uri
|
|
691
|
+
res = await this.helperRest.doRequest('undefined', 'GET', jwks_uri)
|
|
692
|
+
} catch (err: any) {
|
|
693
|
+
logger.error(`${gwName}[${pluginName}] JWKS jwks_uri=${jwks_uri} error: ${err.message}`)
|
|
694
|
+
return []
|
|
695
|
+
}
|
|
696
|
+
if (!res || !Array.isArray(res?.body?.keys)) return []
|
|
697
|
+
const keys = res.body.keys.filter((k: any) => k.kid === kid)
|
|
698
|
+
if (keys.length !== 1) return []
|
|
699
|
+
try {
|
|
700
|
+
return [jwkToPem(keys[0]), issuer, keys[0].alg]
|
|
701
|
+
} catch (err: any) {
|
|
702
|
+
return []
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const jwtVerify = async (baseEntity: string, method: string, el: Record<string, any>, authToken: string, kid?: string): Promise<boolean> => { // used by bearerJwt
|
|
707
|
+
try {
|
|
708
|
+
jwt.verify(authToken, (el.secret) ? el.secret : el.publicKeyContent, el.options)
|
|
709
|
+
if (Array.isArray(el?.baseEntities) && el.baseEntities.length > 0) {
|
|
710
|
+
if (!el.baseEntities.includes(baseEntity)) return false
|
|
711
|
+
}
|
|
712
|
+
if (el.readOnly === true && method !== 'GET') return false
|
|
713
|
+
return true // authorization OK
|
|
714
|
+
} catch (err: any) {
|
|
715
|
+
if (!el.wellKnownUri) throw new Error(`JWT error: ${err.message}`)
|
|
716
|
+
// using wellKnownUri - JWKS and external public certificate - try once again we might have an updated certificate (key)
|
|
717
|
+
if (!kid) throw new Error(`JWKS error: missing kid`)
|
|
718
|
+
const [pemKey, issuer, alg] = await getJwksPemKey(kid, el.wellKnownUri)
|
|
719
|
+
if (!pemKey) throw new Error('JWKS error: no external public certificate found')
|
|
720
|
+
el.publicKeyContent = pemKey
|
|
721
|
+
if (!el.options) el.options = {}
|
|
722
|
+
if (issuer) el.options.issuer = issuer
|
|
723
|
+
if (alg) el.options.algorithms = [alg]
|
|
724
|
+
try {
|
|
725
|
+
jwt.verify(authToken, el.publicKeyContent, el.options)
|
|
726
|
+
if (Array.isArray(el?.baseEntities) && el.baseEntities.length > 0) {
|
|
727
|
+
if (!el.baseEntities.includes(baseEntity)) return false
|
|
686
728
|
}
|
|
687
|
-
|
|
688
|
-
|
|
729
|
+
if (el.readOnly === true && method !== 'GET') return false
|
|
730
|
+
return true // authorization OK
|
|
731
|
+
} catch (err: any) {
|
|
732
|
+
throw new Error(`JWKS error: ${err.message}`)
|
|
733
|
+
}
|
|
734
|
+
}
|
|
689
735
|
}
|
|
690
736
|
|
|
691
737
|
const bearerJwt = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
|
|
692
738
|
if (authType !== 'Bearer' || !found.BearerJwt) return false // no standard jwt bearer token
|
|
693
739
|
const jtoken: any = jwt.decode(authToken, { complete: true })
|
|
694
|
-
if (jtoken
|
|
740
|
+
if (!jtoken) return false
|
|
695
741
|
if (jtoken?.payload['iss'] && jtoken?.payload['iss'].indexOf('https://sts.windows.net') === 0) return false // azure - handled by bearerJwtAzure
|
|
696
|
-
const promises: any = []
|
|
697
742
|
const arr = this.config.scimgateway.auth.bearerJwt
|
|
698
743
|
for (let i = 0; i < arr.length; i++) {
|
|
699
|
-
|
|
700
|
-
}
|
|
701
|
-
const arrResolve = await Promise.all(promises).catch((err) => { throw (err) })
|
|
702
|
-
for (const i in arrResolve) {
|
|
703
|
-
if (arrResolve[i]) return true
|
|
744
|
+
if (await jwtVerify(baseEntity, method, arr[i], authToken, jtoken?.header?.kid) === true) return true
|
|
704
745
|
}
|
|
705
746
|
throw new Error('JWT authentication failed')
|
|
706
747
|
}
|
|
@@ -786,7 +827,7 @@ export class ScimGateway {
|
|
|
786
827
|
])
|
|
787
828
|
.catch((err) => { throw (err) })
|
|
788
829
|
for (const i in arrResolve) {
|
|
789
|
-
if (arrResolve[i]) return true // auth OK - continue with routes
|
|
830
|
+
if (arrResolve[i] === true) return true // auth OK - continue with routes
|
|
790
831
|
}
|
|
791
832
|
// all false - invalid auth method or missing pluging config
|
|
792
833
|
let err: Error
|
|
@@ -827,7 +868,6 @@ export class ScimGateway {
|
|
|
827
868
|
}
|
|
828
869
|
return false
|
|
829
870
|
}
|
|
830
|
-
return false
|
|
831
871
|
}
|
|
832
872
|
|
|
833
873
|
const ipAllowList = (ipAddr: string): boolean => {
|
|
@@ -3554,7 +3594,7 @@ Content-Transfer-Encoding: quoted-printable
|
|
|
3554
3594
|
if (lastKey === 'password' && key.startsWith('scimgateway.auth.basic')) foundBasic = true
|
|
3555
3595
|
else if (lastKey === 'token' && key.startsWith('scimgateway.auth.bearerToken')) foundBearerToken = true
|
|
3556
3596
|
else if (lastKey === 'tenantIdGUID' && key.startsWith('scimgateway.auth.bearerJwtAzure')) foundBearerJwtAzure = true
|
|
3557
|
-
else if (lastKey === 'secret' && key.startsWith('scimgateway.auth.bearerJwt')) foundBearerJwt = true
|
|
3597
|
+
else if ((lastKey === 'publicKey' || lastKey === 'secret' || lastKey === 'wellKnownUri') && key.startsWith('scimgateway.auth.bearerJwt')) foundBearerJwt = true
|
|
3558
3598
|
else if (lastKey === 'clientSecret' && key.startsWith('scimgateway.auth.bearerOAuth')) foundBearerOAuth = true
|
|
3559
3599
|
|
|
3560
3600
|
// certificate full path
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scimgateway",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
|
|
6
6
|
"author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"hyco-https": "^1.4.5",
|
|
49
49
|
"is-in-subnet": "^4.0.1",
|
|
50
50
|
"jsonwebtoken": "^9.0.2",
|
|
51
|
+
"jwk-to-pem": "^2.0.7",
|
|
51
52
|
"ldapjs": "^3.0.7",
|
|
52
53
|
"lokijs": "^1.5.12",
|
|
53
54
|
"mongodb": "^6.16.0",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"@types/bun": "latest",
|
|
66
67
|
"@types/dot-object": "^2.1.6",
|
|
67
68
|
"@types/jsonwebtoken": "^9.0.9",
|
|
69
|
+
"@types/jwk-to-pem": "^2.0.3",
|
|
68
70
|
"@types/node": "latest",
|
|
69
71
|
"@types/nodemailer": "^6.4.17",
|
|
70
72
|
"@types/passport": "^1.0.17",
|