scimgateway 5.5.0 → 5.5.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/README.md +8 -3
- package/lib/scimgateway.ts +15 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -746,7 +746,7 @@ Example Entra ID (plugin-entra-id) using federated credentials:
|
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
748
|
// Note: Federated credentials defined for the application in Entra ID must match the corresponding `issuer`, `subject`, and `name`
|
|
749
|
-
// example issuer: "https://scimgateway.my-company.com/oauth" and must be reachable from the internet
|
|
749
|
+
// example issuer: "https://scimgateway.my-company.com/oauth" and base URL must be reachable from the internet
|
|
750
750
|
// exampole name: "plugin-entra-id"
|
|
751
751
|
|
|
752
752
|
|
|
@@ -1491,6 +1491,12 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1491
1491
|
|
|
1492
1492
|
## Change log
|
|
1493
1493
|
|
|
1494
|
+
### v5.5.1
|
|
1495
|
+
|
|
1496
|
+
[Fixed]
|
|
1497
|
+
|
|
1498
|
+
- 401 Unauthorized response did include scim-formatted error message when using `helper-rest` and authentication `PassThrough`. 401 should not include scim-formatted error message
|
|
1499
|
+
|
|
1494
1500
|
### v5.5.0
|
|
1495
1501
|
|
|
1496
1502
|
[Improved]
|
|
@@ -1518,7 +1524,7 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1518
1524
|
"options": {
|
|
1519
1525
|
"tenantIdGUID": "11111111-2222-3333-4444-555555555555",
|
|
1520
1526
|
"fedCred": {
|
|
1521
|
-
"issuer": "https://scimgateway.my-company.com/oauth
|
|
1527
|
+
"issuer": "https://scimgateway.my-company.com/oauth",
|
|
1522
1528
|
"subject": "99999999-8888-7777-6666-555555555555",
|
|
1523
1529
|
"name": "plugin-entra-id"
|
|
1524
1530
|
}
|
|
@@ -1529,7 +1535,6 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
|
|
|
1529
1535
|
|
|
1530
1536
|
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.
|
|
1531
1537
|
|
|
1532
|
-
|
|
1533
1538
|
### v5.4.4
|
|
1534
1539
|
|
|
1535
1540
|
[Improved]
|
package/lib/scimgateway.ts
CHANGED
|
@@ -586,7 +586,7 @@ export class ScimGateway {
|
|
|
586
586
|
}
|
|
587
587
|
|
|
588
588
|
if (ctx.response.status && (ctx.response.status < 200 || ctx.response.status > 299)) {
|
|
589
|
-
if (ctx.response.status === 401 && !ctx.request.headers.
|
|
589
|
+
if (ctx.response.status === 401 && !ctx.request.headers.has('authorization')) {
|
|
590
590
|
logger.warn(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] ${ellapsed} ${ctx.ip} ${userName} ${ctx.response.status} ${ctx.request.method} ${ctx.request.url} Inbound=${JSON.stringify(ctx.request.body)} Outbound=${outbound}`, { baseEntity: ctx?.routeObj?.baseEntity })
|
|
591
591
|
} else if (ctx.response.status === 404) {
|
|
592
592
|
logger.warn(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] ${ellapsed} ${ctx.ip} ${userName} ${ctx.response.status} ${ctx.request.method} ${ctx.request.url} Inbound=${JSON.stringify(ctx.request.body)} Outbound=${outbound}`, { baseEntity: ctx?.routeObj?.baseEntity })
|
|
@@ -597,11 +597,10 @@ export class ScimGateway {
|
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
// start auth methods - used by auth
|
|
600
|
-
const basic = async (baseEntity: string, method: string, authType: string, authToken: string
|
|
600
|
+
const basic = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
|
|
601
601
|
return await new Promise((resolve, reject) => { // basic auth
|
|
602
602
|
if (!found.Basic) return resolve(false)
|
|
603
603
|
if (authType !== 'Basic' || !authToken) return resolve(false)
|
|
604
|
-
if (found.PassThrough && this.authPassThroughAllowed && !path.endsWith('/logger')) return resolve(false) // Auth PassThrough browser logon dialog support
|
|
605
604
|
const [userName, userPassword] = (Buffer.from(authToken, 'base64').toString() || '').split(':')
|
|
606
605
|
if (!userName || !userPassword) return resolve(false)
|
|
607
606
|
const arr = this.config.scimgateway.auth.basic
|
|
@@ -819,7 +818,7 @@ export class ScimGateway {
|
|
|
819
818
|
const obj = this.config.scimgateway.auth.passThrough
|
|
820
819
|
if (obj.baseEntities) {
|
|
821
820
|
if (Array.isArray(obj.baseEntities) && obj.baseEntities.length > 0) {
|
|
822
|
-
if (!
|
|
821
|
+
if (!obj.baseEntities.includes(baseEntity)) throw new Error(`baseEntity=${baseEntity} not allowed for passThrough according to passThrough configuration baseEntitites=${obj.baseEntities}`)
|
|
823
822
|
}
|
|
824
823
|
}
|
|
825
824
|
if (obj.readOnly === true && method !== 'GET') throw new Error('only allowing readOnly for passThrough according to passThrough configuration readOnly=true')
|
|
@@ -834,7 +833,7 @@ export class ScimGateway {
|
|
|
834
833
|
try {
|
|
835
834
|
// authenticate
|
|
836
835
|
arrResolve = await Promise.all([
|
|
837
|
-
basic(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken
|
|
836
|
+
basic(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
|
|
838
837
|
bearerToken(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
|
|
839
838
|
bearerJwtAzure(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
|
|
840
839
|
bearerJwt(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
|
|
@@ -1100,11 +1099,12 @@ export class ScimGateway {
|
|
|
1100
1099
|
if (pwErrCount < 3) {
|
|
1101
1100
|
pwErrCount += 1
|
|
1102
1101
|
} else { // delay brute force attempts
|
|
1103
|
-
|
|
1102
|
+
const delay = (this.config.scimgateway.idleTimeout || 120) - 5
|
|
1103
|
+
logger.error(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] [oauth] ${ctx.origin + ctx.path} ${errDescr} => delaying response with ${delay} seconds to prevent brute force`)
|
|
1104
1104
|
await new Promise((resolve) => {
|
|
1105
1105
|
setTimeout(() => {
|
|
1106
1106
|
resolve(ctx)
|
|
1107
|
-
}, 1000 *
|
|
1107
|
+
}, 1000 * delay)
|
|
1108
1108
|
})
|
|
1109
1109
|
ctx.response.status = 401
|
|
1110
1110
|
return
|
|
@@ -2766,6 +2766,14 @@ export class ScimGateway {
|
|
|
2766
2766
|
if (!ctx.response.body) {
|
|
2767
2767
|
ctx.response.body = 'Unauthorized'
|
|
2768
2768
|
ctx.response.headers.set('content-type', 'text/plain')
|
|
2769
|
+
} else {
|
|
2770
|
+
try {
|
|
2771
|
+
const b = JSON.parse(ctx.response.body)
|
|
2772
|
+
if (b.schemas) { // 401 - do not return scim formatted error message e.g., using PassThrough
|
|
2773
|
+
ctx.response.body = 'Unauthorized'
|
|
2774
|
+
ctx.response.headers.set('content-type', 'text/plain')
|
|
2775
|
+
}
|
|
2776
|
+
} catch (err) { void 0 }
|
|
2769
2777
|
}
|
|
2770
2778
|
break
|
|
2771
2779
|
case 403:
|
package/package.json
CHANGED