@_mustachio/openauth 0.11.0 → 0.13.0
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/esm/adapters/d1/index.js +18 -9
- package/dist/esm/adapters/d1/migrations.js +7 -2
- package/dist/esm/adapters/d1/session-store.js +39 -0
- package/dist/esm/adapters/d1/session.js +2 -2
- package/dist/esm/adapters/durable-object/index.js +4 -0
- package/dist/esm/adapters/durable-object/session-store.js +42 -0
- package/dist/esm/adapters/dynamo/index.js +24 -7
- package/dist/esm/adapters/dynamo/session-store.js +49 -0
- package/dist/esm/adapters/kms/index.js +6 -2
- package/dist/esm/adapters/kv/index.js +6 -2
- package/dist/esm/adapters/memory/index.js +24 -7
- package/dist/esm/adapters/memory/session-store.js +23 -0
- package/dist/esm/adapters/postgres/executor.js +2 -2
- package/dist/esm/adapters/postgres/index.js +33 -10
- package/dist/esm/adapters/postgres/migrations.js +8 -2
- package/dist/esm/adapters/postgres/session-store.js +42 -0
- package/dist/esm/client.js +27 -27
- package/dist/esm/domain/authorize.js +2 -2
- package/dist/esm/domain/callback.js +121 -7
- package/dist/esm/domain/client-auth.js +2 -2
- package/dist/esm/domain/crypto.js +14 -14
- package/dist/esm/domain/discovery.js +2 -2
- package/dist/esm/domain/dpop.js +4 -4
- package/dist/esm/domain/id-token.js +5 -5
- package/dist/esm/domain/jwt.js +4 -4
- package/dist/esm/domain/method-dispatch.js +27 -1
- package/dist/esm/domain/method-route.js +80 -1
- package/dist/esm/domain/par.js +2 -2
- package/dist/esm/domain/pkce.js +2 -2
- package/dist/esm/domain/revoke.js +2 -2
- package/dist/esm/domain/scim/discovery.js +206 -0
- package/dist/esm/domain/scim/filter.js +177 -0
- package/dist/esm/domain/scim/handle.js +338 -0
- package/dist/esm/domain/scim/patch.js +568 -0
- package/dist/esm/domain/scim/resource.js +256 -0
- package/dist/esm/domain/state-envelope.js +2 -2
- package/dist/esm/domain/token.js +5 -5
- package/dist/esm/error.js +8 -8
- package/dist/esm/http/cookies.js +19 -2
- package/dist/esm/http/errors.js +4 -4
- package/dist/esm/http/handlers/authorize.js +3 -19
- package/dist/esm/http/handlers/callback.js +7 -1
- package/dist/esm/http/handlers/metadata.js +2 -2
- package/dist/esm/http/handlers/method-route.js +47 -2
- package/dist/esm/http/handlers/revocation.js +2 -2
- package/dist/esm/http/handlers/scim.js +59 -0
- package/dist/esm/http/middleware/tenant.js +31 -6
- package/dist/esm/http/router.js +3 -0
- package/dist/esm/http/schemas/authorize.js +2 -2
- package/dist/esm/http/schemas/revocation.js +2 -2
- package/dist/esm/http/schemas/token.js +7 -7
- package/dist/esm/index.js +34 -32
- package/dist/esm/methods/oauth2-factory.js +2 -2
- package/dist/esm/methods/password-hash.js +2 -2
- package/dist/esm/methods/providers/index.js +30 -14
- package/dist/esm/methods/saml-sp/acs.js +232 -0
- package/dist/esm/methods/saml-sp/attributes.js +77 -0
- package/dist/esm/methods/saml-sp/authnrequest.js +63 -0
- package/dist/esm/methods/saml-sp/cache-provider.js +33 -0
- package/dist/esm/methods/saml-sp/cert-rotation.js +13 -0
- package/dist/esm/methods/saml-sp/factory.js +81 -0
- package/dist/esm/methods/saml-sp/index.js +7 -0
- package/dist/esm/methods/saml-sp/metadata.js +71 -0
- package/dist/esm/methods/saml-sp/method.js +29 -0
- package/dist/esm/methods/saml-sp/parse-idp-metadata.js +102 -0
- package/dist/esm/methods/saml-sp/saml-instance.js +59 -0
- package/dist/esm/methods/saml-sp/slo-initiate.js +84 -0
- package/dist/esm/methods/saml-sp/sls.js +154 -0
- package/dist/esm/methods/saml-sp/types.js +0 -0
- package/dist/esm/ports/scim-directory.js +0 -0
- package/dist/esm/types/error.js +5 -0
- package/dist/esm/types/result.js +3 -3
- package/dist/esm/types/scim.js +0 -0
- package/dist/esm/ui/forms.js +3 -3
- package/dist/esm/ui/index.js +8 -5
- package/dist/esm/ui/picker.js +2 -2
- package/dist/types/adapters/d1/migrations.d.ts.map +1 -1
- package/dist/types/adapters/d1/session-store.d.ts +3 -0
- package/dist/types/adapters/d1/session-store.d.ts.map +1 -1
- package/dist/types/adapters/durable-object/session-store.d.ts +3 -0
- package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -1
- package/dist/types/adapters/dynamo/session-store.d.ts +3 -0
- package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -1
- package/dist/types/adapters/memory/session-store.d.ts +3 -0
- package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
- package/dist/types/adapters/postgres/migrations.d.ts +1 -1
- package/dist/types/adapters/postgres/migrations.d.ts.map +1 -1
- package/dist/types/adapters/postgres/session-store.d.ts +3 -0
- package/dist/types/adapters/postgres/session-store.d.ts.map +1 -1
- package/dist/types/domain/callback.d.ts +16 -1
- package/dist/types/domain/callback.d.ts.map +1 -1
- package/dist/types/domain/method-dispatch.d.ts.map +1 -1
- package/dist/types/domain/method-route.d.ts +57 -1
- package/dist/types/domain/method-route.d.ts.map +1 -1
- package/dist/types/domain/scim/discovery.d.ts +11 -0
- package/dist/types/domain/scim/discovery.d.ts.map +1 -0
- package/dist/types/domain/scim/filter.d.ts +51 -0
- package/dist/types/domain/scim/filter.d.ts.map +1 -0
- package/dist/types/domain/scim/handle.d.ts +35 -0
- package/dist/types/domain/scim/handle.d.ts.map +1 -0
- package/dist/types/domain/scim/patch.d.ts +73 -0
- package/dist/types/domain/scim/patch.d.ts.map +1 -0
- package/dist/types/domain/scim/resource.d.ts +56 -0
- package/dist/types/domain/scim/resource.d.ts.map +1 -0
- package/dist/types/http/context.d.ts +6 -1
- package/dist/types/http/context.d.ts.map +1 -1
- package/dist/types/http/cookies.d.ts +9 -1
- package/dist/types/http/cookies.d.ts.map +1 -1
- package/dist/types/http/handlers/callback.d.ts.map +1 -1
- package/dist/types/http/handlers/method-route.d.ts.map +1 -1
- package/dist/types/http/handlers/scim.d.ts +3 -0
- package/dist/types/http/handlers/scim.d.ts.map +1 -0
- package/dist/types/http/middleware/tenant.d.ts.map +1 -1
- package/dist/types/http/router.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/methods/saml-sp/acs.d.ts +4 -0
- package/dist/types/methods/saml-sp/acs.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/attributes.d.ts +45 -0
- package/dist/types/methods/saml-sp/attributes.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/authnrequest.d.ts +4 -0
- package/dist/types/methods/saml-sp/authnrequest.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/cache-provider.d.ts +38 -0
- package/dist/types/methods/saml-sp/cache-provider.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/cert-rotation.d.ts +23 -0
- package/dist/types/methods/saml-sp/cert-rotation.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/factory.d.ts +920 -0
- package/dist/types/methods/saml-sp/factory.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/index.d.ts +24 -0
- package/dist/types/methods/saml-sp/index.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/metadata.d.ts +50 -0
- package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/method.d.ts +17 -0
- package/dist/types/methods/saml-sp/method.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts +4 -0
- package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/saml-instance.d.ts +93 -0
- package/dist/types/methods/saml-sp/saml-instance.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/slo-initiate.d.ts +4 -0
- package/dist/types/methods/saml-sp/slo-initiate.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/sls.d.ts +4 -0
- package/dist/types/methods/saml-sp/sls.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/types.d.ts +299 -0
- package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
- package/dist/types/ports/audit-log.d.ts +18 -5
- package/dist/types/ports/audit-log.d.ts.map +1 -1
- package/dist/types/ports/scim-directory.d.ts +104 -0
- package/dist/types/ports/scim-directory.d.ts.map +1 -0
- package/dist/types/ports/session-store.d.ts +23 -0
- package/dist/types/ports/session-store.d.ts.map +1 -1
- package/dist/types/types/error.d.ts +5 -0
- package/dist/types/types/error.d.ts.map +1 -1
- package/dist/types/types/idp.d.ts +83 -0
- package/dist/types/types/idp.d.ts.map +1 -1
- package/dist/types/types/method.d.ts +109 -0
- package/dist/types/types/method.d.ts.map +1 -1
- package/dist/types/types/scim.d.ts +269 -0
- package/dist/types/types/scim.d.ts.map +1 -0
- package/dist/types/types/tenant.d.ts +10 -0
- package/dist/types/types/tenant.d.ts.map +1 -1
- package/package.json +9 -1
- package/src/adapters/d1/migrations.ts +5 -0
- package/src/adapters/d1/session-store.ts +68 -0
- package/src/adapters/durable-object/session-store.ts +61 -0
- package/src/adapters/dynamo/session-store.ts +63 -0
- package/src/adapters/memory/session-store.ts +39 -0
- package/src/adapters/postgres/migrations.ts +6 -0
- package/src/adapters/postgres/session-store.ts +69 -0
- package/src/domain/callback.ts +215 -7
- package/src/domain/method-dispatch.ts +53 -1
- package/src/domain/method-route.ts +190 -1
- package/src/domain/scim/discovery.ts +262 -0
- package/src/domain/scim/filter.ts +295 -0
- package/src/domain/scim/handle.ts +597 -0
- package/src/domain/scim/patch.ts +807 -0
- package/src/domain/scim/resource.ts +358 -0
- package/src/http/context.ts +9 -0
- package/src/http/cookies.ts +19 -1
- package/src/http/handlers/authorize.ts +2 -22
- package/src/http/handlers/callback.ts +9 -1
- package/src/http/handlers/method-route.ts +72 -2
- package/src/http/handlers/scim.ts +107 -0
- package/src/http/middleware/tenant.ts +47 -1
- package/src/http/router.ts +9 -0
- package/src/index.ts +23 -0
- package/src/methods/saml-sp/acs.ts +449 -0
- package/src/methods/saml-sp/attributes.ts +146 -0
- package/src/methods/saml-sp/authnrequest.ts +129 -0
- package/src/methods/saml-sp/cache-provider.ts +76 -0
- package/src/methods/saml-sp/cert-rotation.ts +34 -0
- package/src/methods/saml-sp/factory.ts +139 -0
- package/src/methods/saml-sp/index.ts +34 -0
- package/src/methods/saml-sp/metadata.ts +215 -0
- package/src/methods/saml-sp/method.ts +65 -0
- package/src/methods/saml-sp/parse-idp-metadata.ts +158 -0
- package/src/methods/saml-sp/saml-instance.ts +210 -0
- package/src/methods/saml-sp/slo-initiate.ts +180 -0
- package/src/methods/saml-sp/sls.ts +307 -0
- package/src/methods/saml-sp/types.ts +298 -0
- package/src/ports/CONSISTENCY.md +52 -24
- package/src/ports/audit-log.ts +18 -5
- package/src/ports/scim-directory.ts +166 -0
- package/src/ports/session-store.ts +24 -0
- package/src/types/error.ts +11 -0
- package/src/types/idp.ts +86 -0
- package/src/types/method.ts +110 -0
- package/src/types/scim.ts +280 -0
- package/src/types/tenant.ts +10 -0
|
@@ -46,12 +46,12 @@ var tokenRequestSchema = z.discriminatedUnion("grant_type", [
|
|
|
46
46
|
tokenExchangeGrantSchema
|
|
47
47
|
]);
|
|
48
48
|
export {
|
|
49
|
-
|
|
50
|
-
tokenExchangeGrantSchema,
|
|
51
|
-
refreshTokenGrantSchema,
|
|
52
|
-
clientCredentialsGrantSchema,
|
|
53
|
-
authorizationCodeGrantSchema,
|
|
54
|
-
TOKEN_EXCHANGE_GRANT_TYPE,
|
|
49
|
+
ACCESS_TOKEN_TYPE,
|
|
55
50
|
REFRESH_TOKEN_TYPE,
|
|
56
|
-
|
|
51
|
+
TOKEN_EXCHANGE_GRANT_TYPE,
|
|
52
|
+
authorizationCodeGrantSchema,
|
|
53
|
+
clientCredentialsGrantSchema,
|
|
54
|
+
refreshTokenGrantSchema,
|
|
55
|
+
tokenExchangeGrantSchema,
|
|
56
|
+
tokenRequestSchema
|
|
57
57
|
};
|
package/dist/esm/index.js
CHANGED
|
@@ -58,6 +58,7 @@ function createIdP(opts) {
|
|
|
58
58
|
sessionStore: opts.sessionStore,
|
|
59
59
|
keyStore: opts.keyStore,
|
|
60
60
|
...opts.methodStore ? { methodStore: opts.methodStore } : {},
|
|
61
|
+
...opts.scimDirectory ? { scimDirectory: opts.scimDirectory } : {},
|
|
61
62
|
...auditLog ? { auditLog } : {},
|
|
62
63
|
methodCache,
|
|
63
64
|
stateKeys: opts.stateKeys,
|
|
@@ -65,6 +66,7 @@ function createIdP(opts) {
|
|
|
65
66
|
...opts.callbackHostFor ? { callbackHostFor: opts.callbackHostFor } : {},
|
|
66
67
|
resolveTenant: opts.resolveTenant,
|
|
67
68
|
success: opts.success,
|
|
69
|
+
...opts.onLogout ? { onLogout: opts.onLogout } : {},
|
|
68
70
|
...opts.persistUpstreamTokens ? { persistUpstreamTokens: opts.persistUpstreamTokens } : {},
|
|
69
71
|
...opts.exchangeAudience ? { exchangeAudience: opts.exchangeAudience } : {},
|
|
70
72
|
...opts.renderPicker ? { renderPicker: opts.renderPicker } : {},
|
|
@@ -95,37 +97,37 @@ function createIdP(opts) {
|
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
export {
|
|
98
|
-
|
|
99
|
-
xFactory,
|
|
100
|
-
twitchFactory,
|
|
101
|
-
spotifyFactory,
|
|
102
|
-
slackFactory,
|
|
103
|
-
revokeAllForSubject,
|
|
104
|
-
passwordMethod,
|
|
105
|
-
passkeyMethod,
|
|
106
|
-
ok,
|
|
107
|
-
oidcFactory,
|
|
108
|
-
oauth2Factory,
|
|
109
|
-
microsoftFactory,
|
|
110
|
-
m2mMethod,
|
|
111
|
-
linkedinFactory,
|
|
112
|
-
keycloakFactory,
|
|
113
|
-
jumpcloudFactory,
|
|
114
|
-
isOk,
|
|
115
|
-
isErr,
|
|
116
|
-
googleFactory,
|
|
117
|
-
githubFactory,
|
|
118
|
-
facebookFactory,
|
|
119
|
-
err,
|
|
120
|
-
discordFactory,
|
|
121
|
-
createIdP,
|
|
122
|
-
cognitoFactory,
|
|
123
|
-
codeMethod,
|
|
124
|
-
buildOidcMethod,
|
|
125
|
-
buildOauth2Method,
|
|
126
|
-
authError,
|
|
127
|
-
asTenantId,
|
|
128
|
-
argon2idHasher,
|
|
100
|
+
DEFAULT_ARGON2ID_PARAMS,
|
|
129
101
|
appleFactory,
|
|
130
|
-
|
|
102
|
+
argon2idHasher,
|
|
103
|
+
asTenantId,
|
|
104
|
+
authError,
|
|
105
|
+
buildOauth2Method,
|
|
106
|
+
buildOidcMethod,
|
|
107
|
+
codeMethod,
|
|
108
|
+
cognitoFactory,
|
|
109
|
+
createIdP,
|
|
110
|
+
discordFactory,
|
|
111
|
+
err,
|
|
112
|
+
facebookFactory,
|
|
113
|
+
githubFactory,
|
|
114
|
+
googleFactory,
|
|
115
|
+
isErr,
|
|
116
|
+
isOk,
|
|
117
|
+
jumpcloudFactory,
|
|
118
|
+
keycloakFactory,
|
|
119
|
+
linkedinFactory,
|
|
120
|
+
m2mMethod,
|
|
121
|
+
microsoftFactory,
|
|
122
|
+
oauth2Factory,
|
|
123
|
+
oidcFactory,
|
|
124
|
+
ok,
|
|
125
|
+
passkeyMethod,
|
|
126
|
+
passwordMethod,
|
|
127
|
+
revokeAllForSubject,
|
|
128
|
+
slackFactory,
|
|
129
|
+
spotifyFactory,
|
|
130
|
+
twitchFactory,
|
|
131
|
+
xFactory,
|
|
132
|
+
yahooFactory
|
|
131
133
|
};
|
|
@@ -1,17 +1,33 @@
|
|
|
1
|
+
// src/methods/providers/index.ts
|
|
2
|
+
import { googleFactory } from "./google";
|
|
3
|
+
import { githubFactory } from "./github";
|
|
4
|
+
import { appleFactory } from "./apple";
|
|
5
|
+
import { microsoftFactory } from "./microsoft";
|
|
6
|
+
import { discordFactory } from "./discord";
|
|
7
|
+
import { facebookFactory } from "./facebook";
|
|
8
|
+
import { linkedinFactory } from "./linkedin";
|
|
9
|
+
import { slackFactory } from "./slack";
|
|
10
|
+
import { spotifyFactory } from "./spotify";
|
|
11
|
+
import { twitchFactory } from "./twitch";
|
|
12
|
+
import { xFactory } from "./x";
|
|
13
|
+
import { yahooFactory } from "./yahoo";
|
|
14
|
+
import { jumpcloudFactory } from "./jumpcloud";
|
|
15
|
+
import { keycloakFactory } from "./keycloak";
|
|
16
|
+
import { cognitoFactory } from "./cognito";
|
|
1
17
|
export {
|
|
2
|
-
|
|
3
|
-
xFactory,
|
|
4
|
-
twitchFactory,
|
|
5
|
-
spotifyFactory,
|
|
6
|
-
slackFactory,
|
|
7
|
-
microsoftFactory,
|
|
8
|
-
linkedinFactory,
|
|
9
|
-
keycloakFactory,
|
|
10
|
-
jumpcloudFactory,
|
|
11
|
-
googleFactory,
|
|
12
|
-
githubFactory,
|
|
13
|
-
facebookFactory,
|
|
14
|
-
discordFactory,
|
|
18
|
+
appleFactory,
|
|
15
19
|
cognitoFactory,
|
|
16
|
-
|
|
20
|
+
discordFactory,
|
|
21
|
+
facebookFactory,
|
|
22
|
+
githubFactory,
|
|
23
|
+
googleFactory,
|
|
24
|
+
jumpcloudFactory,
|
|
25
|
+
keycloakFactory,
|
|
26
|
+
linkedinFactory,
|
|
27
|
+
microsoftFactory,
|
|
28
|
+
slackFactory,
|
|
29
|
+
spotifyFactory,
|
|
30
|
+
twitchFactory,
|
|
31
|
+
xFactory,
|
|
32
|
+
yahooFactory
|
|
17
33
|
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// src/methods/saml-sp/acs.ts
|
|
2
|
+
import xmldom from "@xmldom/xmldom";
|
|
3
|
+
import { authError } from "../../types/error";
|
|
4
|
+
import { mapProfile } from "./attributes";
|
|
5
|
+
import { buildSamlInstance, resolveSpEntityId } from "./saml-instance";
|
|
6
|
+
var { DOMParser } = xmldom;
|
|
7
|
+
function parseVerifiedAssertion(profile) {
|
|
8
|
+
if (typeof profile.getAssertionXml !== "function") {
|
|
9
|
+
return {
|
|
10
|
+
failure: {
|
|
11
|
+
kind: "error",
|
|
12
|
+
error: authError.internalError("saml-sp: node-saml profile exposes no getAssertionXml(); cannot " + "perform the Recipient binding check. Refusing to authenticate.")
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
return {
|
|
18
|
+
doc: new DOMParser().parseFromString(profile.getAssertionXml(), "text/xml")
|
|
19
|
+
};
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return {
|
|
22
|
+
failure: {
|
|
23
|
+
kind: "error",
|
|
24
|
+
error: authError.internalError("saml-sp: failed to parse the verified assertion", e)
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function checkRecipient(doc, acsUrl) {
|
|
30
|
+
const nodes = doc.getElementsByTagNameNS("*", "SubjectConfirmationData");
|
|
31
|
+
const recipients = [];
|
|
32
|
+
for (let i = 0;i < nodes.length; i++) {
|
|
33
|
+
const r = nodes[i]?.getAttribute("Recipient");
|
|
34
|
+
if (r)
|
|
35
|
+
recipients.push(r);
|
|
36
|
+
}
|
|
37
|
+
if (recipients.length === 0) {
|
|
38
|
+
return {
|
|
39
|
+
kind: "denied",
|
|
40
|
+
reason: "assertion has no SubjectConfirmationData/@Recipient binding it " + "to this ACS"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (!recipients.includes(acsUrl)) {
|
|
44
|
+
return {
|
|
45
|
+
kind: "denied",
|
|
46
|
+
reason: "recipient mismatch: assertion not addressed to this ACS"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function extractReplayInfo(doc) {
|
|
52
|
+
const root = doc.documentElement;
|
|
53
|
+
const assertionId = root?.getAttribute("ID") ?? "";
|
|
54
|
+
if (!assertionId)
|
|
55
|
+
return null;
|
|
56
|
+
let earliest = null;
|
|
57
|
+
for (const tag of ["Conditions", "SubjectConfirmationData"]) {
|
|
58
|
+
const els = doc.getElementsByTagNameNS("*", tag);
|
|
59
|
+
for (let i = 0;i < els.length; i++) {
|
|
60
|
+
const v = els[i]?.getAttribute("NotOnOrAfter");
|
|
61
|
+
if (!v)
|
|
62
|
+
continue;
|
|
63
|
+
const ms = Date.parse(v);
|
|
64
|
+
if (!Number.isNaN(ms))
|
|
65
|
+
earliest = earliest === null ? ms : Math.min(earliest, ms);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { assertionId, notOnOrAfterMs: earliest };
|
|
69
|
+
}
|
|
70
|
+
function extractAuthnStatement(doc) {
|
|
71
|
+
const out = {};
|
|
72
|
+
const stmt = doc.getElementsByTagNameNS("*", "AuthnStatement")[0];
|
|
73
|
+
if (!stmt)
|
|
74
|
+
return out;
|
|
75
|
+
const instant = stmt.getAttribute("AuthnInstant");
|
|
76
|
+
if (instant)
|
|
77
|
+
out.authnInstant = instant;
|
|
78
|
+
const sessionExpiry = stmt.getAttribute("SessionNotOnOrAfter");
|
|
79
|
+
if (sessionExpiry) {
|
|
80
|
+
const ms = Date.parse(sessionExpiry);
|
|
81
|
+
if (!Number.isNaN(ms))
|
|
82
|
+
out.sessionNotOnOrAfter = ms;
|
|
83
|
+
}
|
|
84
|
+
const ref = stmt.getElementsByTagNameNS("*", "AuthnContextClassRef")[0]?.textContent?.trim();
|
|
85
|
+
if (ref)
|
|
86
|
+
out.authnContextClassRef = ref;
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
async function consumeAssertion(ctx, methodId, config) {
|
|
90
|
+
const idpInitiated = ctx.flow === null;
|
|
91
|
+
let spEntityId;
|
|
92
|
+
let acsUrl;
|
|
93
|
+
if (idpInitiated) {
|
|
94
|
+
if (!config.idpInitiated) {
|
|
95
|
+
return {
|
|
96
|
+
kind: "error",
|
|
97
|
+
error: authError.internalError("saml-sp: unsolicited Response reached a method with no " + "idpInitiated config")
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (!ctx.dispatch) {
|
|
101
|
+
return {
|
|
102
|
+
kind: "error",
|
|
103
|
+
error: authError.internalError("saml-sp: IdP-initiated ACS dispatched without issuer context")
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
spEntityId = resolveSpEntityId(config, ctx.dispatch.issuerUrl, ctx.tenant.id, methodId);
|
|
107
|
+
acsUrl = ctx.dispatch.callbackUrl;
|
|
108
|
+
} else {
|
|
109
|
+
const state = ctx.methodState;
|
|
110
|
+
if (!state || !state.spEntityId || !state.acsUrl) {
|
|
111
|
+
return {
|
|
112
|
+
kind: "error",
|
|
113
|
+
error: authError.internalError("saml-sp: ACS reached without AuthnRequest method state " + "(spEntityId / acsUrl). The flow did not originate from this method.")
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
spEntityId = state.spEntityId;
|
|
117
|
+
acsUrl = state.acsUrl;
|
|
118
|
+
}
|
|
119
|
+
let samlResponse;
|
|
120
|
+
let relayState;
|
|
121
|
+
try {
|
|
122
|
+
const form = new URLSearchParams(await ctx.request.text());
|
|
123
|
+
samlResponse = form.get("SAMLResponse");
|
|
124
|
+
relayState = form.get("RelayState");
|
|
125
|
+
} catch {
|
|
126
|
+
return {
|
|
127
|
+
kind: "error",
|
|
128
|
+
error: authError.internalError("saml-sp: ACS could not read POST body")
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (!samlResponse) {
|
|
132
|
+
return { kind: "denied", reason: "missing SAMLResponse" };
|
|
133
|
+
}
|
|
134
|
+
let saml;
|
|
135
|
+
try {
|
|
136
|
+
saml = buildSamlInstance(config, {
|
|
137
|
+
spEntityId,
|
|
138
|
+
acsUrl,
|
|
139
|
+
scratch: ctx.methodScratch,
|
|
140
|
+
...idpInitiated ? { idpInitiated: true } : {},
|
|
141
|
+
...config.allowEncryptedAssertions && config.decryptionKey ? { decryptionPvk: config.decryptionKey.privateKeyPem } : {}
|
|
142
|
+
}, Date.now());
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return {
|
|
145
|
+
kind: "error",
|
|
146
|
+
error: authError.internalError(`saml-sp: cannot construct verifier: ${e instanceof Error ? e.message : String(e)}`, e)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
let profile;
|
|
150
|
+
try {
|
|
151
|
+
const result = await saml.validatePostResponseAsync({
|
|
152
|
+
SAMLResponse: samlResponse,
|
|
153
|
+
...relayState !== null ? { RelayState: relayState } : {}
|
|
154
|
+
});
|
|
155
|
+
profile = result.profile;
|
|
156
|
+
} catch (e) {
|
|
157
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
158
|
+
if (!config.allowEncryptedAssertions && msg.includes("No decryption key")) {
|
|
159
|
+
return {
|
|
160
|
+
kind: "denied",
|
|
161
|
+
reason: "encrypted assertion received but encrypted assertions are not " + "enabled for this SAML connection (set allowEncryptedAssertions " + "+ decryptionKey)"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { kind: "denied", reason: `assertion rejected: ${msg}` };
|
|
165
|
+
}
|
|
166
|
+
if (!profile || !profile.nameID) {
|
|
167
|
+
return { kind: "denied", reason: "assertion produced no usable subject" };
|
|
168
|
+
}
|
|
169
|
+
const parsed = parseVerifiedAssertion(profile);
|
|
170
|
+
if ("failure" in parsed)
|
|
171
|
+
return parsed.failure;
|
|
172
|
+
const assertionDoc = parsed.doc;
|
|
173
|
+
const recipientFailure = checkRecipient(assertionDoc, acsUrl);
|
|
174
|
+
if (recipientFailure)
|
|
175
|
+
return recipientFailure;
|
|
176
|
+
if (idpInitiated) {
|
|
177
|
+
const replay = extractReplayInfo(assertionDoc);
|
|
178
|
+
if (!replay) {
|
|
179
|
+
return {
|
|
180
|
+
kind: "error",
|
|
181
|
+
error: authError.internalError("saml-sp: cannot read assertion @ID for IdP-initiated replay " + "protection. Refusing to authenticate.")
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const key = `idp-replay:${replay.assertionId}`;
|
|
185
|
+
const seen = await ctx.methodScratch.get(key);
|
|
186
|
+
if (seen.ok) {
|
|
187
|
+
return {
|
|
188
|
+
kind: "denied",
|
|
189
|
+
reason: "assertion replay detected (assertion ID already seen)"
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const skewMs = (config.clockSkewSeconds ?? 60) * 1000;
|
|
193
|
+
const now = Date.now();
|
|
194
|
+
const horizon = replay.notOnOrAfterMs !== null ? replay.notOnOrAfterMs - now + skewMs : 10 * 60000;
|
|
195
|
+
const ttlMs = Math.min(Math.max(horizon, skewMs, 60000), 24 * 60 * 60000);
|
|
196
|
+
const recorded = await ctx.methodScratch.put(key, "1", ttlMs);
|
|
197
|
+
if (!recorded.ok) {
|
|
198
|
+
return {
|
|
199
|
+
kind: "error",
|
|
200
|
+
error: authError.internalError("saml-sp: could not record assertion ID for replay protection")
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const authnStatement = extractAuthnStatement(assertionDoc);
|
|
205
|
+
const verified = {
|
|
206
|
+
nameID: profile.nameID,
|
|
207
|
+
nameIDFormat: profile.nameIDFormat ?? "",
|
|
208
|
+
...profile.sessionIndex !== undefined ? { sessionIndex: profile.sessionIndex } : {},
|
|
209
|
+
...authnStatement,
|
|
210
|
+
attributes: profile.attributes ?? {},
|
|
211
|
+
responseXml: profile.getSamlResponseXml?.() ?? ""
|
|
212
|
+
};
|
|
213
|
+
const mapped = mapProfile(verified, config.attributeMapping);
|
|
214
|
+
if ("error" in mapped) {
|
|
215
|
+
return { kind: "denied", reason: mapped.error };
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
kind: "success",
|
|
219
|
+
providerSubject: mapped.providerSubject,
|
|
220
|
+
properties: mapped.properties,
|
|
221
|
+
...idpInitiated && config.idpInitiated ? {
|
|
222
|
+
unsolicitedBinding: {
|
|
223
|
+
clientId: config.idpInitiated.defaultClientId,
|
|
224
|
+
redirectUri: config.idpInitiated.defaultRedirectUri,
|
|
225
|
+
scopes: config.idpInitiated.defaultScopes ?? []
|
|
226
|
+
}
|
|
227
|
+
} : {}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
consumeAssertion
|
|
232
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/methods/saml-sp/attributes.ts
|
|
2
|
+
var NAME_ID_FORMAT_BY_URN = {
|
|
3
|
+
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent": "persistent",
|
|
4
|
+
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient": "transient",
|
|
5
|
+
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress": "emailAddress",
|
|
6
|
+
"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified": "unspecified"
|
|
7
|
+
};
|
|
8
|
+
function normalizeNameIdFormat(urn) {
|
|
9
|
+
return NAME_ID_FORMAT_BY_URN[urn] ?? "unspecified";
|
|
10
|
+
}
|
|
11
|
+
function scalar(v) {
|
|
12
|
+
if (typeof v === "string")
|
|
13
|
+
return v;
|
|
14
|
+
if (Array.isArray(v) && v.length > 0 && typeof v[0] === "string") {
|
|
15
|
+
return v[0];
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
function multi(v) {
|
|
20
|
+
if (typeof v === "string")
|
|
21
|
+
return v;
|
|
22
|
+
if (Array.isArray(v)) {
|
|
23
|
+
const strs = v.filter((x) => typeof x === "string");
|
|
24
|
+
return strs.length > 0 ? strs : undefined;
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
function resolveRef(ref, profile) {
|
|
29
|
+
if (ref.source === "nameId")
|
|
30
|
+
return profile.nameID;
|
|
31
|
+
return multi(profile.attributes[ref.name]);
|
|
32
|
+
}
|
|
33
|
+
function mapProfile(profile, mapping) {
|
|
34
|
+
const subjectRef = mapping.subject ?? { source: "nameId" };
|
|
35
|
+
const subjectVal = scalar(resolveRef(subjectRef, profile));
|
|
36
|
+
if (!subjectVal) {
|
|
37
|
+
return {
|
|
38
|
+
error: subjectRef.source === "nameId" ? "assertion NameID is empty; cannot derive subject" : `subject attribute "${subjectRef.name}" missing from assertion`
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const attributes = {};
|
|
42
|
+
const put = (key, ref) => {
|
|
43
|
+
if (!ref)
|
|
44
|
+
return;
|
|
45
|
+
const v = resolveRef(ref, profile);
|
|
46
|
+
if (v !== undefined)
|
|
47
|
+
attributes[key] = v;
|
|
48
|
+
};
|
|
49
|
+
put("email", mapping.email);
|
|
50
|
+
put("name", mapping.name);
|
|
51
|
+
put("groups", mapping.groups);
|
|
52
|
+
if (mapping.emailVerified) {
|
|
53
|
+
attributes["emailVerified"] = mapping.emailVerified.value ? "true" : "false";
|
|
54
|
+
}
|
|
55
|
+
for (const [key, ref] of Object.entries(mapping.custom ?? {})) {
|
|
56
|
+
put(key, ref);
|
|
57
|
+
}
|
|
58
|
+
const authnInstant = profile.authnInstant ? Date.parse(profile.authnInstant) : Date.now();
|
|
59
|
+
return {
|
|
60
|
+
providerSubject: subjectVal,
|
|
61
|
+
properties: {
|
|
62
|
+
nameId: {
|
|
63
|
+
value: profile.nameID,
|
|
64
|
+
format: normalizeNameIdFormat(profile.nameIDFormat)
|
|
65
|
+
},
|
|
66
|
+
attributes,
|
|
67
|
+
...profile.sessionIndex !== undefined ? { sessionIndex: profile.sessionIndex } : {},
|
|
68
|
+
authnInstant: Number.isNaN(authnInstant) ? Date.now() : authnInstant,
|
|
69
|
+
...profile.sessionNotOnOrAfter !== undefined ? { sessionNotOnOrAfter: profile.sessionNotOnOrAfter } : {},
|
|
70
|
+
...profile.authnContextClassRef !== undefined ? { authnContextClassRef: profile.authnContextClassRef } : {},
|
|
71
|
+
raw: { responseXml: profile.responseXml }
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
mapProfile
|
|
77
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// src/methods/saml-sp/authnrequest.ts
|
|
2
|
+
import { authError } from "../../types/error";
|
|
3
|
+
import { isErr } from "../../types/result";
|
|
4
|
+
import { buildSamlInstance, resolveSpEntityId } from "./saml-instance";
|
|
5
|
+
async function buildAuthnRequestRedirect(ctx, methodId, config) {
|
|
6
|
+
if (!ctx.dispatch) {
|
|
7
|
+
return {
|
|
8
|
+
kind: "error",
|
|
9
|
+
error: authError.internalError("saml-sp: dispatch missing on /authorize")
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
if (config.signAuthnRequest && !config.signingKey) {
|
|
13
|
+
return {
|
|
14
|
+
kind: "error",
|
|
15
|
+
error: authError.internalError("saml-sp: signAuthnRequest is true but signingKey is missing " + "(config schema should have rejected this).")
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const spEntityId = resolveSpEntityId(config, ctx.dispatch.issuerUrl, ctx.tenant.id, methodId);
|
|
19
|
+
const acsUrl = ctx.dispatch.callbackUrl;
|
|
20
|
+
const probe = await ctx.methodScratch.put("authnrequest-scratch-probe", "1", 1000);
|
|
21
|
+
if (isErr(probe)) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "error",
|
|
24
|
+
error: authError.internalError("saml-sp: the configured SessionStore adapter does not support " + "methodScratch (saveScratch/readScratch/deleteScratch), which " + "SAML SP requires for InResponseTo replay protection. Deploy " + "SAML against an adapter that implements the scratch trio.")
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
let redirectUrl;
|
|
28
|
+
try {
|
|
29
|
+
const saml = buildSamlInstance(config, {
|
|
30
|
+
spEntityId,
|
|
31
|
+
acsUrl,
|
|
32
|
+
scratch: ctx.methodScratch,
|
|
33
|
+
...config.signAuthnRequest && config.signingKey ? {
|
|
34
|
+
signing: {
|
|
35
|
+
privateKeyPem: config.signingKey.privateKeyPem,
|
|
36
|
+
certPem: config.signingKey.certPem
|
|
37
|
+
}
|
|
38
|
+
} : {}
|
|
39
|
+
}, Date.now());
|
|
40
|
+
redirectUrl = await saml.getAuthorizeUrlAsync(ctx.dispatch.state, undefined, {});
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return {
|
|
43
|
+
kind: "error",
|
|
44
|
+
error: authError.internalError(`saml-sp: failed to build AuthnRequest: ${e instanceof Error ? e.message : String(e)}`, e)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
kind: "challenge",
|
|
49
|
+
response: new Response(null, {
|
|
50
|
+
status: 302,
|
|
51
|
+
headers: { location: redirectUrl }
|
|
52
|
+
}),
|
|
53
|
+
saveMethodState: {
|
|
54
|
+
relayState: ctx.dispatch.state,
|
|
55
|
+
issuedAt: Date.now(),
|
|
56
|
+
spEntityId,
|
|
57
|
+
acsUrl
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
buildAuthnRequestRedirect
|
|
63
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/methods/saml-sp/cache-provider.ts
|
|
2
|
+
import { isOk } from "../../types/result";
|
|
3
|
+
var KEY_PREFIX = "saml-inresponseto:";
|
|
4
|
+
function methodScratchCacheProvider(scratch, ttlMs) {
|
|
5
|
+
return {
|
|
6
|
+
async saveAsync(key, value) {
|
|
7
|
+
const createdAt = Date.now();
|
|
8
|
+
const item = { value, createdAt };
|
|
9
|
+
const r = await scratch.put(`${KEY_PREFIX}${key}`, JSON.stringify(item), ttlMs);
|
|
10
|
+
return isOk(r) ? item : null;
|
|
11
|
+
},
|
|
12
|
+
async getAsync(key) {
|
|
13
|
+
const r = await scratch.get(`${KEY_PREFIX}${key}`);
|
|
14
|
+
if (!isOk(r))
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
const item = JSON.parse(r.value);
|
|
18
|
+
return item.value;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
async removeAsync(key) {
|
|
24
|
+
if (key === null)
|
|
25
|
+
return null;
|
|
26
|
+
const r = await scratch.delete(`${KEY_PREFIX}${key}`);
|
|
27
|
+
return isOk(r) ? key : null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
methodScratchCacheProvider
|
|
33
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/methods/saml-sp/cert-rotation.ts
|
|
2
|
+
function selectActiveCertPems(certs, nowMs) {
|
|
3
|
+
return certs.filter((c) => {
|
|
4
|
+
if (c.notBefore !== undefined && nowMs < c.notBefore)
|
|
5
|
+
return false;
|
|
6
|
+
if (c.notAfter !== undefined && nowMs >= c.notAfter)
|
|
7
|
+
return false;
|
|
8
|
+
return true;
|
|
9
|
+
}).map((c) => c.pem);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
selectActiveCertPems
|
|
13
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/methods/saml-sp/factory.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { buildSamlSpMethod } from "./method";
|
|
4
|
+
var attributeRefSchema = z.union([
|
|
5
|
+
z.object({ source: z.literal("nameId") }),
|
|
6
|
+
z.object({
|
|
7
|
+
source: z.literal("attribute"),
|
|
8
|
+
name: z.string().min(1),
|
|
9
|
+
format: z.string().optional()
|
|
10
|
+
})
|
|
11
|
+
]);
|
|
12
|
+
var attributeMappingSchema = z.object({
|
|
13
|
+
subject: attributeRefSchema.optional(),
|
|
14
|
+
email: attributeRefSchema.optional(),
|
|
15
|
+
emailVerified: z.object({ source: z.literal("literal"), value: z.boolean() }).optional(),
|
|
16
|
+
name: attributeRefSchema.optional(),
|
|
17
|
+
groups: attributeRefSchema.optional(),
|
|
18
|
+
custom: z.record(attributeRefSchema).optional()
|
|
19
|
+
});
|
|
20
|
+
var signingCertSchema = z.object({
|
|
21
|
+
pem: z.string().min(1),
|
|
22
|
+
notBefore: z.number().optional(),
|
|
23
|
+
notAfter: z.number().optional()
|
|
24
|
+
});
|
|
25
|
+
var idpSchema = z.object({
|
|
26
|
+
entityId: z.string().min(1),
|
|
27
|
+
ssoUrl: z.string().url(),
|
|
28
|
+
sloUrl: z.string().url().optional(),
|
|
29
|
+
nameIdFormat: z.enum(["persistent", "transient", "emailAddress", "unspecified"]).optional(),
|
|
30
|
+
signingCerts: z.array(signingCertSchema).min(1)
|
|
31
|
+
});
|
|
32
|
+
var idpInitiatedSchema = z.object({
|
|
33
|
+
defaultClientId: z.string().min(1),
|
|
34
|
+
defaultRedirectUri: z.string().url(),
|
|
35
|
+
defaultScopes: z.array(z.string()).optional()
|
|
36
|
+
});
|
|
37
|
+
var samlSpConfigSchema = z.object({
|
|
38
|
+
idp: idpSchema,
|
|
39
|
+
attributeMapping: attributeMappingSchema,
|
|
40
|
+
signAuthnRequest: z.boolean().optional(),
|
|
41
|
+
signingKey: z.object({
|
|
42
|
+
privateKeyPem: z.string().min(1),
|
|
43
|
+
certPem: z.string().min(1)
|
|
44
|
+
}).optional(),
|
|
45
|
+
allowEncryptedAssertions: z.boolean().optional(),
|
|
46
|
+
decryptionKey: z.object({
|
|
47
|
+
privateKeyPem: z.string().min(1),
|
|
48
|
+
certPem: z.string().min(1)
|
|
49
|
+
}).optional(),
|
|
50
|
+
idpInitiated: idpInitiatedSchema.optional(),
|
|
51
|
+
clockSkewSeconds: z.number().int().nonnegative().optional(),
|
|
52
|
+
spEntityId: z.string().min(1).optional(),
|
|
53
|
+
forceAuthn: z.boolean().optional(),
|
|
54
|
+
requestedAuthnContext: z.object({
|
|
55
|
+
classRefs: z.array(z.string().min(1)).min(1),
|
|
56
|
+
comparison: z.enum(["exact", "minimum", "maximum", "better"]).optional()
|
|
57
|
+
}).optional(),
|
|
58
|
+
requireSignedAssertion: z.boolean().optional(),
|
|
59
|
+
requireSignedResponse: z.boolean().optional()
|
|
60
|
+
}).refine((c) => !c.signAuthnRequest || c.signingKey !== undefined, {
|
|
61
|
+
message: "signingKey is required when signAuthnRequest is true",
|
|
62
|
+
path: ["signingKey"]
|
|
63
|
+
}).refine((c) => !c.allowEncryptedAssertions || c.decryptionKey !== undefined, {
|
|
64
|
+
message: "decryptionKey is required when allowEncryptedAssertions is true",
|
|
65
|
+
path: ["decryptionKey"]
|
|
66
|
+
}).refine((c) => (c.requireSignedAssertion ?? true) || c.requireSignedResponse === true, {
|
|
67
|
+
message: "requireSignedAssertion may only be false when requireSignedResponse " + "is true — at least one signature is mandatory",
|
|
68
|
+
path: ["requireSignedAssertion"]
|
|
69
|
+
});
|
|
70
|
+
var samlSpFactory = {
|
|
71
|
+
kind: "saml-sp",
|
|
72
|
+
configSchema: samlSpConfigSchema,
|
|
73
|
+
build: async ({
|
|
74
|
+
id,
|
|
75
|
+
kind,
|
|
76
|
+
config
|
|
77
|
+
}) => buildSamlSpMethod(id, kind, config)
|
|
78
|
+
};
|
|
79
|
+
export {
|
|
80
|
+
samlSpFactory
|
|
81
|
+
};
|