scimgateway 5.5.1 → 5.5.3
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 +29 -10
- package/config/docker/.dockerignore +27 -0
- package/config/docker/DataDockerfile +1 -1
- package/config/docker/Dockerfile +4 -2
- package/lib/helper-rest.ts +20 -23
- package/lib/plugin-mssql.ts +2 -0
- package/lib/scimgateway.ts +25 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Latest news:
|
|
|
42
42
|
|
|
43
43
|
## Overview
|
|
44
44
|
|
|
45
|
-
SCIM Gateway facilitates user management using the standardized REST-based SCIM 1.1 or 2.0 protocol, offering easier, more powerful, and consistent provisioning while avoiding vendor lock-in. Acting as a translator for incoming SCIM requests, the gateway seamlessly enables CRUD functionality (create, read, update, and delete) for users and groups. By implementing endpoint-specific protocols, it ensures provisioning across diverse destinations. With the gateway, your destinations
|
|
45
|
+
SCIM Gateway facilitates user management using the standardized REST-based SCIM 1.1 or 2.0 protocol, offering easier, more powerful, and consistent provisioning while avoiding vendor lock-in. Acting as a translator for incoming SCIM requests, the gateway seamlessly enables CRUD functionality (create, read, update, and delete) for users and groups. By implementing endpoint-specific protocols, it ensures provisioning across diverse destinations. With the gateway, your destinations become SCIM-compatible interfaces, streamlining integration and simplifying user management.
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|

|
|
@@ -738,16 +738,16 @@ Example Entra ID (plugin-entra-id) using federated credentials:
|
|
|
738
738
|
"options": {
|
|
739
739
|
"tenantIdGUID": "<tenantId>",
|
|
740
740
|
"fedCred": {
|
|
741
|
-
"issuer": "<https://FQDN-scimgateway
|
|
741
|
+
"issuer": "<https://FQDN-scimgateway>",
|
|
742
742
|
"subject": "<entra id application object id - client id>",
|
|
743
743
|
"name": "<entra id federated credentials unique name>"
|
|
744
744
|
}
|
|
745
745
|
}
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
|
-
|
|
749
|
-
// example issuer: "https://scimgateway.my-company.com
|
|
750
|
-
//
|
|
748
|
+
// Note, fedCred configuration must match corresponding configuration in Entra ID Application - Certificates & Secrets - Federated credentials - scenario "Other issuer"
|
|
749
|
+
// example issuer: "https://scimgateway.my-company.com" note, this scimgateway base URL must be reachable from the internet
|
|
750
|
+
// example name: "plugin-entra-id"
|
|
751
751
|
|
|
752
752
|
|
|
753
753
|
Example using general OAuth:
|
|
@@ -972,8 +972,6 @@ On Linux systems we may also run SCIM Gateway as a Docker image (using docker-co
|
|
|
972
972
|
**docker-ce
|
|
973
973
|
docker-compose**
|
|
974
974
|
|
|
975
|
-
|
|
976
|
-
|
|
977
975
|
- Install SCIM Gateway within your own package and copy provided docker files:
|
|
978
976
|
|
|
979
977
|
mkdir /opt/my-scimgateway
|
|
@@ -988,6 +986,7 @@ docker-compose**
|
|
|
988
986
|
**DataDockerfile** <== Handles volume mapping
|
|
989
987
|
**docker-compose-debug.yml** <== Debugging
|
|
990
988
|
**docker-compose-mssql.yml** <== Example including MSSQL docker image
|
|
989
|
+
**.dockerignore** <== Files to exclude from the build context
|
|
991
990
|
|
|
992
991
|
- Create a scimgateway user on your Linux VM.
|
|
993
992
|
|
|
@@ -1491,6 +1490,26 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1491
1490
|
|
|
1492
1491
|
## Change log
|
|
1493
1492
|
|
|
1493
|
+
### v5.5.3
|
|
1494
|
+
|
|
1495
|
+
[Fixed]
|
|
1496
|
+
- Docker - fixed `docker build` error introduced in v5.5.0 (using bun.lock instead of binary bun.lockb)
|
|
1497
|
+
|
|
1498
|
+
[Improved]
|
|
1499
|
+
- plugin-mssql - attribute externalId included
|
|
1500
|
+
- .dockerignore - new docker configuration file, contains files to be excluded from the build context
|
|
1501
|
+
|
|
1502
|
+
### v5.5.2
|
|
1503
|
+
|
|
1504
|
+
[Improved]
|
|
1505
|
+
|
|
1506
|
+
- Entra ID Federated Identity Credentials introduced in v5.5.0, the issuer configuration should be scimgateway base URL
|
|
1507
|
+
old: `"issuer": "<https://FQDN-scimgateway>/oauth"`
|
|
1508
|
+
new: `"issuer": "<https://FQDN-scimgateway>"`
|
|
1509
|
+
|
|
1510
|
+
Change log v5.5.0 have been corrected with the new issuer having base URL only
|
|
1511
|
+
|
|
1512
|
+
|
|
1494
1513
|
### v5.5.1
|
|
1495
1514
|
|
|
1496
1515
|
[Fixed]
|
|
@@ -1510,7 +1529,7 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1510
1529
|
"options": {
|
|
1511
1530
|
"tenantIdGUID": "<Entra ID tenantIdGUID",
|
|
1512
1531
|
"fedCred": {
|
|
1513
|
-
"issuer": "<https://FQDN-scimgateway
|
|
1532
|
+
"issuer": "<https://FQDN-scimgateway>",
|
|
1514
1533
|
"subject": "<entra id application object id - client id>",
|
|
1515
1534
|
"name": "<entra id federated credentials unique name>"
|
|
1516
1535
|
}
|
|
@@ -1524,14 +1543,14 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1524
1543
|
"options": {
|
|
1525
1544
|
"tenantIdGUID": "11111111-2222-3333-4444-555555555555",
|
|
1526
1545
|
"fedCred": {
|
|
1527
|
-
"issuer": "https://scimgateway.my-company.com
|
|
1546
|
+
"issuer": "https://scimgateway.my-company.com",
|
|
1528
1547
|
"subject": "99999999-8888-7777-6666-555555555555",
|
|
1529
1548
|
"name": "plugin-entra-id"
|
|
1530
1549
|
}
|
|
1531
1550
|
}
|
|
1532
1551
|
}
|
|
1533
1552
|
|
|
1534
|
-
Note: Federated credentials defined for the application in Entra ID must match the corresponding `issuer`, `subject`, and `name` values defined in the SCIM Gateway endpoint configuration. An example of this can be using `plugin-entra-id` and other plugins that interact with endpoints or applications protected by Entra ID.
|
|
1553
|
+
Note: Federated credentials (scenario "Other issuer") defined for the application in Entra ID must match the corresponding `issuer`, `subject`, and `name` values defined in the SCIM Gateway endpoint configuration. An example of this can be using `plugin-entra-id` and other plugins that interact with endpoints or applications protected by Entra ID.
|
|
1535
1554
|
|
|
1536
1555
|
Also note: SCIM Gateway must be reachable from the internet (as defined by the `issuer` URL). This requires allowing inbound internet communication — or alternatively, Azure Relay can be used for outbound-only communication.
|
|
1537
1556
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
dist/
|
|
3
|
+
client_deploy/
|
|
4
|
+
typings/
|
|
5
|
+
/typings.json
|
|
6
|
+
/jsconfig.json
|
|
7
|
+
/npm-debug.log
|
|
8
|
+
/uml.txt
|
|
9
|
+
/.dockerignore
|
|
10
|
+
/docker-compose*.yml
|
|
11
|
+
/Dockerfile
|
|
12
|
+
/DataDockerfile
|
|
13
|
+
/sqlserver_data/
|
|
14
|
+
/node_modules/
|
|
15
|
+
/.vscode/
|
|
16
|
+
/dbinit/
|
|
17
|
+
/logs/
|
|
18
|
+
/config/docker
|
|
19
|
+
/config/approles
|
|
20
|
+
/config/resources
|
|
21
|
+
/eslint.config.js
|
|
22
|
+
/.travis.yml
|
|
23
|
+
/.gitignore
|
|
24
|
+
/.gitattributes
|
|
25
|
+
/.git/
|
|
26
|
+
/.github/
|
|
27
|
+
/test/
|
package/config/docker/Dockerfile
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# Thanks to Charles Watson <cwatsonx@costco.com> and Jeffrey Gilbert for the base of Docker implementation and inspiration.
|
|
2
|
+
#
|
|
1
3
|
# Depending on your system you may need to prefix the commands below with sudo.
|
|
2
4
|
#
|
|
3
5
|
# To build: docker build --force-rm=true -t <projectName>:1.0.0 .
|
|
@@ -14,7 +16,7 @@
|
|
|
14
16
|
FROM oven/bun:slim AS base
|
|
15
17
|
|
|
16
18
|
# Declare who maintains this Dockerfile
|
|
17
|
-
LABEL maintainer="
|
|
19
|
+
LABEL maintainer="https://elshaug.xyz"
|
|
18
20
|
|
|
19
21
|
# Add a Process ID 1 Safety Net. Specific to debian.
|
|
20
22
|
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init
|
|
@@ -25,7 +27,7 @@ WORKDIR /home/scimgateway
|
|
|
25
27
|
ENV NODE_HOME=/home/scimgateway
|
|
26
28
|
|
|
27
29
|
# Add your project info
|
|
28
|
-
ADD ./package.json ./bun.
|
|
30
|
+
ADD ./package.json ./bun.lock $NODE_HOME
|
|
29
31
|
|
|
30
32
|
# Install dependencies (exclude test stuff for dependencies)
|
|
31
33
|
RUN . ~/.bashrc && cd $NODE_HOME && bun install --production --frozen-lockfile
|
package/lib/helper-rest.ts
CHANGED
|
@@ -154,27 +154,9 @@ export class HelperRest {
|
|
|
154
154
|
|
|
155
155
|
if (tenantIdGUID) { // Microsoft Entra ID
|
|
156
156
|
if (this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.issuer) { // federated credentials
|
|
157
|
-
const name = JSON.stringify(this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.name) // ensure not using none valid json key
|
|
158
|
-
if (!this.scimgateway.jwk) this.scimgateway.jwk = {}
|
|
159
|
-
if (!this.scimgateway.jwk[baseEntity]) this.scimgateway.jwk[baseEntity] = {}
|
|
160
|
-
if (!this.scimgateway.jwk[baseEntity][name]) {
|
|
161
|
-
const { publicKey, privateKey } = await jose.generateKeyPair('RS256')
|
|
162
|
-
this.scimgateway.jwk[baseEntity][name] = { publicKey, privateKey }
|
|
163
|
-
const ttl = 5 * 60 // 5 minutes
|
|
164
|
-
;(async () => {
|
|
165
|
-
// rotate - delete JWK after 5 minutes, will be regenerated on next token request
|
|
166
|
-
// entra id only lookup well-known uri and corresponding jwks_uri on token request validation if kid not found in entra cached JWKS
|
|
167
|
-
setTimeout(async () => {
|
|
168
|
-
delete this.scimgateway.jwk[baseEntity][name]
|
|
169
|
-
}, ttl * 1000)
|
|
170
|
-
})()
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
this.scimgateway.jwk[baseEntity].issuer = this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.issuer // updates .well-known
|
|
174
|
-
|
|
175
157
|
const now = Date.now()
|
|
176
158
|
const jwtPayload: jose.JWTPayload = {
|
|
177
|
-
iss: this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.issuer, // entra id federated credentials issuer e.g. https://scimgateway.my-company.com
|
|
159
|
+
iss: this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.issuer, // entra id federated credentials issuer - scimgateway base URL, e.g. https://scimgateway.my-company.com
|
|
178
160
|
sub: this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.subject, // entra id application object id - client id
|
|
179
161
|
name: this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.name, // entra id federated credentials unique name e.g. plugin-entra-id
|
|
180
162
|
aud: 'api://AzureADTokenExchange', // entra id federated credentials audience
|
|
@@ -188,7 +170,8 @@ export class HelperRest {
|
|
|
188
170
|
...jwtPayload,
|
|
189
171
|
}
|
|
190
172
|
|
|
191
|
-
const
|
|
173
|
+
const { publicKey, privateKey } = await jose.generateKeyPair('RS256')
|
|
174
|
+
const jwk = await jose.exportJWK(publicKey)
|
|
192
175
|
const kid = createHash('sha256') // kid required for JWKS
|
|
193
176
|
.update(JSON.stringify(jwk))
|
|
194
177
|
.digest('base64url')
|
|
@@ -206,8 +189,22 @@ export class HelperRest {
|
|
|
206
189
|
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
|
|
207
190
|
client_assertion: await new jose.SignJWT(jwtClaims)
|
|
208
191
|
.setProtectedHeader(jwtHeaders)
|
|
209
|
-
.sign(
|
|
192
|
+
.sign(privateKey),
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// keep JWK for 5 minutes, will be regenerated on next token request
|
|
196
|
+
// entra id only lookup well-known uri and corresponding jwks_uri on token request validation if kid not found in entra cached JWKS
|
|
197
|
+
if (!this.scimgateway.jwk) this.scimgateway.jwk = {}
|
|
198
|
+
if (!this.scimgateway.jwk[kid]) {
|
|
199
|
+
this.scimgateway.jwk[kid] = { publicKey, privateKey }
|
|
200
|
+
const ttl = 5 * 60
|
|
201
|
+
;(async () => {
|
|
202
|
+
setTimeout(async () => {
|
|
203
|
+
delete this.scimgateway.jwk[kid]
|
|
204
|
+
}, ttl * 1000)
|
|
205
|
+
})()
|
|
210
206
|
}
|
|
207
|
+
this.scimgateway.jwk.issuer = this.config_entity[baseEntity]?.connection?.auth?.options?.fedCred?.issuer // all baseEntities should use same issuer
|
|
211
208
|
} else { // standard certificate
|
|
212
209
|
if (!this.config_entity[baseEntity]?.connection?.auth?.options?.tls?.cert) {
|
|
213
210
|
throw new Error(`auth type '${this.config_entity[baseEntity]?.connection?.auth?.type}' - missing options.tls.key/cert configuration`)
|
|
@@ -918,13 +915,13 @@ export class HelperRest {
|
|
|
918
915
|
* }
|
|
919
916
|
*
|
|
920
917
|
* // Microsoft Entra ID - using Federated credentials
|
|
921
|
-
* // Note, fedCred configuration must match corresponding configuration in Entra ID Application -
|
|
918
|
+
* // Note, fedCred configuration must match corresponding configuration in Entra ID Application - Certificates & Secrets - Federated credentials - scenario "Other issuer"
|
|
922
919
|
* {
|
|
923
920
|
* "type": "oauthJwtBearer",
|
|
924
921
|
* "options": {
|
|
925
922
|
* "tenantIdGUID": "<Entra ID tenantIdGUID",
|
|
926
923
|
* "fedCred": {
|
|
927
|
-
* "issuer": "<https://FQDN-scimgateway
|
|
924
|
+
* "issuer": "<https://FQDN-scimgateway", // scimgateway base URL, e.g. https://scimgateway.my-company.com
|
|
928
925
|
* "subject": "<entra id application object id - client id>",
|
|
929
926
|
* "name": "<entra id federated credentials unique name>" // e.g. plugin-entra-id
|
|
930
927
|
* }
|
package/lib/plugin-mssql.ts
CHANGED
|
@@ -121,6 +121,7 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
121
121
|
for (const user of users) {
|
|
122
122
|
const scimUser = {
|
|
123
123
|
id: user.UserID.value ? user.UserID.value : undefined,
|
|
124
|
+
externalId: user.UserID.value ? user.UserID.value : undefined,
|
|
124
125
|
userName: user.UserID.value ? user.UserID.value : undefined,
|
|
125
126
|
active: user.Enabled.value === 'true' || false,
|
|
126
127
|
name: {
|
|
@@ -292,6 +293,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
|
|
|
292
293
|
for (const group of groups) {
|
|
293
294
|
const scimGroup: Record<string, any> = {
|
|
294
295
|
id: group.GroupID.value ? group.GroupID.value : undefined,
|
|
296
|
+
externalId: group.GroupID.value ? group.GroupID.value : undefined,
|
|
295
297
|
displayName: group.GroupID.value ? group.GroupID.value : undefined,
|
|
296
298
|
active: group.Enabled.value === 'true' || false,
|
|
297
299
|
members: [],
|
package/lib/scimgateway.ts
CHANGED
|
@@ -333,15 +333,17 @@ export class ScimGateway {
|
|
|
333
333
|
pluginDir = '.' // only support running binary in current directory (path to binary can't be found)
|
|
334
334
|
configDir = './config'
|
|
335
335
|
}
|
|
336
|
-
const configFile = path.join(
|
|
336
|
+
const configFile = path.join(configDir, `${pluginName}.json`) // config name prefix same as pluging name prefix
|
|
337
337
|
const gwName = path.basename(fileURLToPath(import.meta.url)).split('.')[0] // prefix of current file - using fileURLToPath because using "__filename" is not supported by nodejs typescript
|
|
338
338
|
const gwPath = path.dirname(fileURLToPath(import.meta.url))
|
|
339
339
|
|
|
340
340
|
this.config = {}
|
|
341
341
|
// exposed outside class
|
|
342
|
+
this.gwName = gwName
|
|
342
343
|
this.pluginName = pluginName
|
|
343
344
|
this.configDir = configDir
|
|
344
345
|
this.configFile = configFile
|
|
346
|
+
this.authPassThroughAllowed = false // set to true by plugin if using Auth PassThrough
|
|
345
347
|
this.countries = (() => {
|
|
346
348
|
try {
|
|
347
349
|
return JSON.parse(fs.readFileSync(path.join(gwPath, 'countries.json')).toString())
|
|
@@ -382,14 +384,7 @@ export class ScimGateway {
|
|
|
382
384
|
logger.error(`${gwName}[${pluginName}] stopping...`)
|
|
383
385
|
throw (new Error('Using exception to stop further asynchronous code execution (ensure synchronous logger flush to logfile and exit program), please ignore this one...'))
|
|
384
386
|
}
|
|
385
|
-
|
|
386
387
|
this.logger = logger
|
|
387
|
-
// exposed to plugin
|
|
388
|
-
this.gwName = gwName
|
|
389
|
-
this.pluginName = pluginName
|
|
390
|
-
this.configDir = configDir
|
|
391
|
-
this.configFile = configFile
|
|
392
|
-
this.authPassThroughAllowed = false // set to true by plugin if using Auth PassThrough
|
|
393
388
|
|
|
394
389
|
const oAuthTokenExpire = 3600 // seconds
|
|
395
390
|
let pwErrCount = 0
|
|
@@ -486,7 +481,7 @@ export class ScimGateway {
|
|
|
486
481
|
getMethod: 'getAppRoles',
|
|
487
482
|
}
|
|
488
483
|
/** handlers supported url paths */
|
|
489
|
-
const handlers = ['users', 'groups', 'bulk', 'serviceplans', 'approles', 'api', 'schemas', 'resourcetypes', 'serviceproviderconfig', 'serviceproviderconfigs', 'oauth', 'logger']
|
|
484
|
+
const handlers = ['users', 'groups', 'bulk', 'serviceplans', 'approles', 'api', 'schemas', 'resourcetypes', 'serviceproviderconfig', 'serviceproviderconfigs', 'oauth', '.well-known', 'logger']
|
|
490
485
|
|
|
491
486
|
try {
|
|
492
487
|
if (!fs.existsSync(configDir + '/wsdls')) fs.mkdirSync(configDir + '/wsdls')
|
|
@@ -972,53 +967,44 @@ export class ScimGateway {
|
|
|
972
967
|
)
|
|
973
968
|
}
|
|
974
969
|
|
|
975
|
-
// oauth well-known:
|
|
970
|
+
// oauth well-known: /.well-known/openid-configuration
|
|
976
971
|
// this.jwk is managed by helper-rest oauthJwtBearer - Entra ID Federated Identity
|
|
977
|
-
// {issuer: <scimgateway-baseUrl
|
|
978
|
-
// example issuer: https://scimgateway.my-company.com
|
|
972
|
+
// { issuer: <scimgateway-baseUrl>, kid: { privateKey, publicKey } }
|
|
973
|
+
// example issuer: https://scimgateway.my-company.com
|
|
979
974
|
const getHandlerOauthWellKnown = async (ctx: Context) => {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
if (!this.jwk || !this.jwk[baseEntity] || !this.jwk[baseEntity].issuer) {
|
|
975
|
+
logger.debug(`${gwName}[${pluginName}] [oauth] .well-known request`)
|
|
976
|
+
if (!this.jwk || (Object.keys(this.jwk).length < 1)) {
|
|
984
977
|
ctx.response.body = '{}'
|
|
985
978
|
ctx.response.status = 200
|
|
986
979
|
return ctx
|
|
987
980
|
}
|
|
988
|
-
|
|
989
|
-
const issuer = this.jwk[baseEntity].issuer // dynamic set by helper-rest oauthJwtBearer e.g. 'https://scimgateway.my-company.com/oauth'
|
|
981
|
+
const issuer = this.jwk.issuer
|
|
990
982
|
let body = {
|
|
991
983
|
issuer,
|
|
992
|
-
jwks_uri: issuer + '/
|
|
984
|
+
jwks_uri: issuer + '/.well-known/jwks.json',
|
|
993
985
|
}
|
|
994
986
|
ctx.response.body = JSON.stringify(body)
|
|
995
987
|
ctx.response.status = 200
|
|
996
988
|
}
|
|
997
989
|
|
|
998
|
-
// oauth JWKS: /
|
|
990
|
+
// oauth JWKS: /.well-known/jwks.json
|
|
999
991
|
// this.jwk is managed by helper-rest oauthJwtBearer - Entra ID Federated Identity
|
|
1000
|
-
// {issuer: <scimgateway-baseUrl
|
|
1001
|
-
const
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
if (!this.jwk || !this.jwk[baseEntity]) {
|
|
992
|
+
// { issuer: <scimgateway-baseUrl>, kid: { privateKey, publicKey } }
|
|
993
|
+
const getHandlerOauthJwks = async (ctx: Context) => {
|
|
994
|
+
logger.debug(`${gwName}[${pluginName}] [oauth] jwks_uri request`)
|
|
995
|
+
if (!this.jwk || (Object.keys(this.jwk).length < 1)) {
|
|
1006
996
|
ctx.response.body = '{"keys":[]}'
|
|
1007
997
|
ctx.response.status = 200
|
|
1008
998
|
return ctx
|
|
1009
999
|
}
|
|
1010
|
-
|
|
1011
1000
|
const keys: Array<Record<string, any>> = []
|
|
1012
|
-
for (const
|
|
1013
|
-
const keyObj = this.jwk[
|
|
1014
|
-
if (typeof keyObj !== 'object' || keyObj === null) continue
|
|
1015
|
-
const jwk = await jose.exportJWK(this.jwk[
|
|
1016
|
-
jwk.kid =
|
|
1017
|
-
.update(JSON.stringify(jwk))
|
|
1018
|
-
.digest('base64url')
|
|
1001
|
+
for (const kid in this.jwk) {
|
|
1002
|
+
const keyObj = this.jwk[kid]
|
|
1003
|
+
if (typeof keyObj !== 'object' || keyObj === null) continue
|
|
1004
|
+
const jwk = await jose.exportJWK(this.jwk[kid].publicKey)
|
|
1005
|
+
jwk.kid = kid // needed for JWKS
|
|
1019
1006
|
keys.push(jwk)
|
|
1020
1007
|
}
|
|
1021
|
-
|
|
1022
1008
|
let body = {
|
|
1023
1009
|
keys,
|
|
1024
1010
|
}
|
|
@@ -2673,13 +2659,13 @@ export class ScimGateway {
|
|
|
2673
2659
|
return ctx
|
|
2674
2660
|
}
|
|
2675
2661
|
}
|
|
2676
|
-
if (ctx.request.method === 'GET' && ctx.path.endsWith('
|
|
2662
|
+
if (ctx.request.method === 'GET' && ctx.path.endsWith('/.well-known/openid-configuration')) {
|
|
2677
2663
|
await getHandlerOauthWellKnown(ctx)
|
|
2678
2664
|
if (!ctx.response.status) ctx.response.status = 404
|
|
2679
2665
|
return ctx
|
|
2680
2666
|
}
|
|
2681
|
-
if (ctx.request.method === 'GET' && ctx.path.endsWith('/
|
|
2682
|
-
await
|
|
2667
|
+
if (ctx.request.method === 'GET' && ctx.path.endsWith('/.well-known/jwks.json')) {
|
|
2668
|
+
await getHandlerOauthJwks(ctx)
|
|
2683
2669
|
if (!ctx.response.status) ctx.response.status = 404
|
|
2684
2670
|
return ctx
|
|
2685
2671
|
}
|
|
@@ -3093,8 +3079,8 @@ export class ScimGateway {
|
|
|
3093
3079
|
let request = new Request(new URL(req.url ?? '', `${protocol}://${req.headers.host}`), {
|
|
3094
3080
|
method: req.method,
|
|
3095
3081
|
headers: new Headers(req.headers as any),
|
|
3082
|
+
// @ts-expect-error ignore incompatible types
|
|
3096
3083
|
body: body,
|
|
3097
|
-
// @ts-expect-error duplex not defined in RequestInit interface
|
|
3098
3084
|
duplex: body ? 'half' : undefined,
|
|
3099
3085
|
}) as Request & { raw: IncomingMessage }
|
|
3100
3086
|
request.raw = req
|
package/package.json
CHANGED