@_mustachio/openauth 0.10.0 → 0.12.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 +45 -0
- package/dist/esm/adapters/memory/token-store.js +13 -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 +35 -28
- package/dist/esm/domain/authorize.js +7 -2
- package/dist/esm/domain/callback.js +124 -7
- package/dist/esm/domain/client-auth.js +2 -2
- package/dist/esm/domain/crypto.js +14 -14
- package/dist/esm/domain/discovery.js +46 -4
- package/dist/esm/domain/dpop.js +122 -0
- package/dist/esm/domain/id-token.js +94 -0
- package/dist/esm/domain/introspect.js +5 -1
- package/dist/esm/domain/jwt.js +30 -2
- package/dist/esm/domain/logout.js +75 -0
- package/dist/esm/domain/method-dispatch.js +27 -1
- package/dist/esm/domain/method-route.js +83 -1
- package/dist/esm/domain/par.js +51 -0
- package/dist/esm/domain/pkce.js +2 -2
- package/dist/esm/domain/refresh.js +12 -1
- package/dist/esm/domain/register.js +74 -0
- package/dist/esm/domain/revoke.js +2 -2
- package/dist/esm/domain/state-envelope.js +2 -2
- package/dist/esm/domain/token-exchange.js +1 -0
- package/dist/esm/domain/token.js +53 -12
- package/dist/esm/domain/userinfo.js +38 -3
- 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 +92 -21
- package/dist/esm/http/handlers/callback.js +7 -1
- package/dist/esm/http/handlers/end-session.js +64 -0
- package/dist/esm/http/handlers/metadata.js +7 -4
- package/dist/esm/http/handlers/method-route.js +47 -2
- package/dist/esm/http/handlers/par.js +53 -0
- package/dist/esm/http/handlers/register.js +46 -0
- package/dist/esm/http/handlers/revocation.js +2 -2
- package/dist/esm/http/handlers/token.js +36 -6
- package/dist/esm/http/handlers/userinfo.js +35 -29
- package/dist/esm/http/middleware/tenant.js +6 -6
- package/dist/esm/http/router.js +10 -0
- package/dist/esm/http/schemas/authorize.js +8 -2
- package/dist/esm/http/schemas/end-session.js +13 -0
- package/dist/esm/http/schemas/par.js +9 -0
- package/dist/esm/http/schemas/register.js +16 -0
- package/dist/esm/http/schemas/revocation.js +2 -2
- package/dist/esm/http/schemas/token.js +7 -7
- package/dist/esm/index.js +40 -34
- 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 +208 -0
- package/dist/esm/methods/saml-sp/attributes.js +75 -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 +70 -0
- package/dist/esm/methods/saml-sp/index.js +7 -0
- package/dist/esm/methods/saml-sp/metadata.js +69 -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 +49 -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/types/error.js +5 -0
- package/dist/esm/types/result.js +3 -3
- 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 +6 -1
- package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
- package/dist/types/adapters/memory/token-store.d.ts +1 -0
- package/dist/types/adapters/memory/token-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/client.d.ts +19 -0
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/domain/authorize.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/client-credentials.d.ts +2 -0
- package/dist/types/domain/client-credentials.d.ts.map +1 -1
- package/dist/types/domain/discovery.d.ts +37 -0
- package/dist/types/domain/discovery.d.ts.map +1 -1
- package/dist/types/domain/dpop.d.ts +61 -0
- package/dist/types/domain/dpop.d.ts.map +1 -0
- package/dist/types/domain/id-token.d.ts +83 -0
- package/dist/types/domain/id-token.d.ts.map +1 -0
- package/dist/types/domain/introspect.d.ts +8 -0
- package/dist/types/domain/introspect.d.ts.map +1 -1
- package/dist/types/domain/jwt.d.ts +27 -1
- package/dist/types/domain/jwt.d.ts.map +1 -1
- package/dist/types/domain/logout.d.ts +69 -0
- package/dist/types/domain/logout.d.ts.map +1 -0
- 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/par.d.ts +62 -0
- package/dist/types/domain/par.d.ts.map +1 -0
- package/dist/types/domain/refresh.d.ts +9 -0
- package/dist/types/domain/refresh.d.ts.map +1 -1
- package/dist/types/domain/register.d.ts +33 -0
- package/dist/types/domain/register.d.ts.map +1 -0
- package/dist/types/domain/token-exchange.d.ts +2 -0
- package/dist/types/domain/token-exchange.d.ts.map +1 -1
- package/dist/types/domain/token.d.ts +41 -1
- package/dist/types/domain/token.d.ts.map +1 -1
- package/dist/types/domain/userinfo.d.ts +44 -5
- package/dist/types/domain/userinfo.d.ts.map +1 -1
- 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/authorize.d.ts.map +1 -1
- package/dist/types/http/handlers/callback.d.ts.map +1 -1
- package/dist/types/http/handlers/end-session.d.ts +3 -0
- package/dist/types/http/handlers/end-session.d.ts.map +1 -0
- package/dist/types/http/handlers/metadata.d.ts +1 -1
- package/dist/types/http/handlers/metadata.d.ts.map +1 -1
- package/dist/types/http/handlers/method-route.d.ts.map +1 -1
- package/dist/types/http/handlers/par.d.ts +3 -0
- package/dist/types/http/handlers/par.d.ts.map +1 -0
- package/dist/types/http/handlers/register.d.ts +3 -0
- package/dist/types/http/handlers/register.d.ts.map +1 -0
- package/dist/types/http/handlers/token.d.ts.map +1 -1
- package/dist/types/http/handlers/userinfo.d.ts.map +1 -1
- package/dist/types/http/router.d.ts.map +1 -1
- package/dist/types/http/schemas/authorize.d.ts +40 -0
- package/dist/types/http/schemas/authorize.d.ts.map +1 -1
- package/dist/types/http/schemas/end-session.d.ts +31 -0
- package/dist/types/http/schemas/end-session.d.ts.map +1 -0
- package/dist/types/http/schemas/par.d.ts +20 -0
- package/dist/types/http/schemas/par.d.ts.map +1 -0
- package/dist/types/http/schemas/register.d.ts +42 -0
- package/dist/types/http/schemas/register.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -3
- 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 +40 -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 +704 -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 +42 -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 +81 -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 +204 -0
- package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
- package/dist/types/ports/audit-log.d.ts +40 -0
- package/dist/types/ports/audit-log.d.ts.map +1 -1
- package/dist/types/ports/session-store.d.ts +50 -0
- package/dist/types/ports/session-store.d.ts.map +1 -1
- package/dist/types/ports/token-store.d.ts +12 -0
- package/dist/types/ports/token-store.d.ts.map +1 -1
- package/dist/types/types/authorization.d.ts +23 -0
- package/dist/types/types/authorization.d.ts.map +1 -1
- package/dist/types/types/error.d.ts +9 -0
- package/dist/types/types/error.d.ts.map +1 -1
- package/dist/types/types/flow.d.ts +10 -0
- package/dist/types/types/flow.d.ts.map +1 -1
- package/dist/types/types/idp.d.ts +162 -1
- 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/tenant.d.ts +37 -0
- package/dist/types/types/tenant.d.ts.map +1 -1
- package/dist/types/types/token.d.ts +128 -0
- package/dist/types/types/token.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 +83 -1
- package/src/adapters/memory/token-store.ts +16 -0
- package/src/adapters/postgres/migrations.ts +6 -0
- package/src/adapters/postgres/session-store.ts +69 -0
- package/src/client.ts +32 -0
- package/src/domain/authorize.ts +9 -0
- package/src/domain/callback.ts +220 -7
- package/src/domain/client-credentials.ts +2 -0
- package/src/domain/discovery.ts +80 -2
- package/src/domain/dpop.ts +293 -0
- package/src/domain/id-token.ts +213 -0
- package/src/domain/introspect.ts +10 -0
- package/src/domain/jwt.ts +80 -1
- package/src/domain/logout.ts +180 -0
- package/src/domain/method-dispatch.ts +53 -1
- package/src/domain/method-route.ts +195 -1
- package/src/domain/par.ts +141 -0
- package/src/domain/refresh.ts +41 -0
- package/src/domain/register.ts +168 -0
- package/src/domain/token-exchange.ts +11 -0
- package/src/domain/token.ts +134 -8
- package/src/domain/userinfo.ts +121 -6
- package/src/http/context.ts +10 -0
- package/src/http/cookies.ts +19 -1
- package/src/http/handlers/authorize.ts +146 -24
- package/src/http/handlers/callback.ts +9 -1
- package/src/http/handlers/end-session.ts +106 -0
- package/src/http/handlers/metadata.ts +7 -2
- package/src/http/handlers/method-route.ts +75 -2
- package/src/http/handlers/par.ts +84 -0
- package/src/http/handlers/register.ts +70 -0
- package/src/http/handlers/token.ts +56 -0
- package/src/http/handlers/userinfo.ts +62 -38
- package/src/http/router.ts +19 -0
- package/src/http/schemas/authorize.ts +27 -0
- package/src/http/schemas/end-session.ts +23 -0
- package/src/http/schemas/par.ts +23 -0
- package/src/http/schemas/register.ts +29 -0
- package/src/index.ts +21 -1
- package/src/methods/saml-sp/acs.ts +399 -0
- package/src/methods/saml-sp/attributes.ts +137 -0
- package/src/methods/saml-sp/authnrequest.ts +128 -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 +118 -0
- package/src/methods/saml-sp/index.ts +34 -0
- package/src/methods/saml-sp/metadata.ts +204 -0
- package/src/methods/saml-sp/method.ts +65 -0
- package/src/methods/saml-sp/parse-idp-metadata.ts +164 -0
- package/src/methods/saml-sp/saml-instance.ts +173 -0
- package/src/methods/saml-sp/slo-initiate.ts +177 -0
- package/src/methods/saml-sp/sls.ts +306 -0
- package/src/methods/saml-sp/types.ts +203 -0
- package/src/ports/CONSISTENCY.md +77 -21
- package/src/ports/audit-log.ts +42 -0
- package/src/ports/session-store.ts +61 -0
- package/src/ports/token-store.ts +13 -0
- package/src/types/authorization.ts +25 -0
- package/src/types/error.ts +23 -0
- package/src/types/flow.ts +10 -0
- package/src/types/idp.ts +170 -0
- package/src/types/method.ts +110 -0
- package/src/types/tenant.ts +37 -0
- package/src/types/token.ts +135 -0
package/dist/esm/http/router.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { makeAuthorizeHandler } from "./handlers/authorize";
|
|
4
4
|
import { makeCallbackHandler } from "./handlers/callback";
|
|
5
|
+
import { makeEndSessionHandler } from "./handlers/end-session";
|
|
5
6
|
import { makeDiscoveryHandler, makeJwksHandler } from "./handlers/metadata";
|
|
6
7
|
import { makeMethodRouteHandler } from "./handlers/method-route";
|
|
8
|
+
import { makeParHandler } from "./handlers/par";
|
|
9
|
+
import { makeRegisterHandler } from "./handlers/register";
|
|
7
10
|
import { makeIntrospectHandler, makeRevokeHandler } from "./handlers/revocation";
|
|
8
11
|
import { makeTokenHandler } from "./handlers/token";
|
|
9
12
|
import { makeUserinfoHandler } from "./handlers/userinfo";
|
|
@@ -28,6 +31,13 @@ function buildRouter(deps) {
|
|
|
28
31
|
app.post("/userinfo", makeUserinfoHandler(deps));
|
|
29
32
|
app.post("/revoke", makeRevokeHandler(deps));
|
|
30
33
|
app.post("/introspect", makeIntrospectHandler(deps));
|
|
34
|
+
app.use("/end_session", tenantMiddleware(deps));
|
|
35
|
+
app.get("/end_session", makeEndSessionHandler(deps));
|
|
36
|
+
app.post("/end_session", makeEndSessionHandler(deps));
|
|
37
|
+
app.use("/par", tenantMiddleware(deps));
|
|
38
|
+
app.post("/par", makeParHandler(deps));
|
|
39
|
+
app.use("/register", tenantMiddleware(deps));
|
|
40
|
+
app.post("/register", makeRegisterHandler(deps));
|
|
31
41
|
return app;
|
|
32
42
|
}
|
|
33
43
|
export {
|
|
@@ -17,8 +17,14 @@ var authorizeQuerySchema = z.object({
|
|
|
17
17
|
code_challenge_method: z.literal("S256").optional(),
|
|
18
18
|
prompt: csv.optional(),
|
|
19
19
|
ui_locales: csv.optional(),
|
|
20
|
-
nonce: z.string().optional()
|
|
20
|
+
nonce: z.string().optional(),
|
|
21
|
+
claims: z.string().min(1).optional()
|
|
22
|
+
}).passthrough();
|
|
23
|
+
var authorizeRequestUriQuerySchema = z.object({
|
|
24
|
+
client_id: z.string({ required_error: "missing client_id" }).min(1, "empty client_id"),
|
|
25
|
+
request_uri: z.string({ required_error: "missing request_uri" }).min(1)
|
|
21
26
|
}).passthrough();
|
|
22
27
|
export {
|
|
23
|
-
authorizeQuerySchema
|
|
28
|
+
authorizeQuerySchema,
|
|
29
|
+
authorizeRequestUriQuerySchema
|
|
24
30
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/http/schemas/end-session.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var endSessionParamsSchema = z.object({
|
|
4
|
+
id_token_hint: z.string().optional(),
|
|
5
|
+
client_id: z.string().min(1).optional(),
|
|
6
|
+
post_logout_redirect_uri: z.string().url("post_logout_redirect_uri must be a valid URL").optional(),
|
|
7
|
+
state: z.string().optional(),
|
|
8
|
+
logout_hint: z.string().optional(),
|
|
9
|
+
ui_locales: z.string().optional()
|
|
10
|
+
}).passthrough();
|
|
11
|
+
export {
|
|
12
|
+
endSessionParamsSchema
|
|
13
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// src/http/schemas/par.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var parBodySchema = z.object({
|
|
4
|
+
client_id: z.string({ required_error: "missing client_id" }).min(1, "empty client_id"),
|
|
5
|
+
client_secret: z.string().optional()
|
|
6
|
+
}).catchall(z.string());
|
|
7
|
+
export {
|
|
8
|
+
parBodySchema
|
|
9
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/http/schemas/register.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var registerBodySchema = z.object({
|
|
4
|
+
client_name: z.string().min(1).optional(),
|
|
5
|
+
redirect_uris: z.array(z.string().url("redirect_uri must be a valid URL")).min(1, "redirect_uris must be a non-empty array"),
|
|
6
|
+
grant_types: z.array(z.string()).optional(),
|
|
7
|
+
response_types: z.array(z.string()).optional(),
|
|
8
|
+
token_endpoint_auth_method: z.enum(["none", "client_secret_basic", "client_secret_post"]).optional(),
|
|
9
|
+
scope: z.string().optional(),
|
|
10
|
+
post_logout_redirect_uris: z.array(z.string().url()).optional(),
|
|
11
|
+
sector_identifier_uri: z.string().url().optional(),
|
|
12
|
+
contacts: z.array(z.string()).optional()
|
|
13
|
+
}).passthrough();
|
|
14
|
+
export {
|
|
15
|
+
registerBodySchema
|
|
16
|
+
};
|
|
@@ -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
|
@@ -65,16 +65,19 @@ function createIdP(opts) {
|
|
|
65
65
|
...opts.callbackHostFor ? { callbackHostFor: opts.callbackHostFor } : {},
|
|
66
66
|
resolveTenant: opts.resolveTenant,
|
|
67
67
|
success: opts.success,
|
|
68
|
+
...opts.onLogout ? { onLogout: opts.onLogout } : {},
|
|
68
69
|
...opts.persistUpstreamTokens ? { persistUpstreamTokens: opts.persistUpstreamTokens } : {},
|
|
69
70
|
...opts.exchangeAudience ? { exchangeAudience: opts.exchangeAudience } : {},
|
|
70
71
|
...opts.renderPicker ? { renderPicker: opts.renderPicker } : {},
|
|
72
|
+
...opts.registerClient ? { registerClient: opts.registerClient } : {},
|
|
71
73
|
...opts.buildCustomContext ? { buildCustomContext: opts.buildCustomContext } : {},
|
|
72
74
|
clock,
|
|
73
75
|
cookieDefaults: {
|
|
74
76
|
secure: opts.cookies?.secure ?? true,
|
|
75
77
|
...opts.cookies?.domain !== undefined ? { domain: opts.cookies.domain } : {},
|
|
76
78
|
...opts.cookies?.path !== undefined ? { path: opts.cookies.path } : {}
|
|
77
|
-
}
|
|
79
|
+
},
|
|
80
|
+
...opts.customScopeClaims !== undefined ? { customScopeClaims: opts.customScopeClaims } : {}
|
|
78
81
|
};
|
|
79
82
|
const app = buildRouter(deps);
|
|
80
83
|
const fetch = async (req) => await app.fetch(req);
|
|
@@ -86,41 +89,44 @@ function createIdP(opts) {
|
|
|
86
89
|
jwks: fetch,
|
|
87
90
|
discovery: fetch,
|
|
88
91
|
revoke: fetch,
|
|
89
|
-
introspect: fetch
|
|
92
|
+
introspect: fetch,
|
|
93
|
+
endSession: fetch,
|
|
94
|
+
par: fetch,
|
|
95
|
+
register: fetch
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
export {
|
|
93
|
-
|
|
94
|
-
xFactory,
|
|
95
|
-
twitchFactory,
|
|
96
|
-
spotifyFactory,
|
|
97
|
-
slackFactory,
|
|
98
|
-
revokeAllForSubject,
|
|
99
|
-
passwordMethod,
|
|
100
|
-
passkeyMethod,
|
|
101
|
-
ok,
|
|
102
|
-
oidcFactory,
|
|
103
|
-
oauth2Factory,
|
|
104
|
-
microsoftFactory,
|
|
105
|
-
m2mMethod,
|
|
106
|
-
linkedinFactory,
|
|
107
|
-
keycloakFactory,
|
|
108
|
-
jumpcloudFactory,
|
|
109
|
-
isOk,
|
|
110
|
-
isErr,
|
|
111
|
-
googleFactory,
|
|
112
|
-
githubFactory,
|
|
113
|
-
facebookFactory,
|
|
114
|
-
err,
|
|
115
|
-
discordFactory,
|
|
116
|
-
createIdP,
|
|
117
|
-
cognitoFactory,
|
|
118
|
-
codeMethod,
|
|
119
|
-
buildOidcMethod,
|
|
120
|
-
buildOauth2Method,
|
|
121
|
-
authError,
|
|
122
|
-
asTenantId,
|
|
123
|
-
argon2idHasher,
|
|
99
|
+
DEFAULT_ARGON2ID_PARAMS,
|
|
124
100
|
appleFactory,
|
|
125
|
-
|
|
101
|
+
argon2idHasher,
|
|
102
|
+
asTenantId,
|
|
103
|
+
authError,
|
|
104
|
+
buildOauth2Method,
|
|
105
|
+
buildOidcMethod,
|
|
106
|
+
codeMethod,
|
|
107
|
+
cognitoFactory,
|
|
108
|
+
createIdP,
|
|
109
|
+
discordFactory,
|
|
110
|
+
err,
|
|
111
|
+
facebookFactory,
|
|
112
|
+
githubFactory,
|
|
113
|
+
googleFactory,
|
|
114
|
+
isErr,
|
|
115
|
+
isOk,
|
|
116
|
+
jumpcloudFactory,
|
|
117
|
+
keycloakFactory,
|
|
118
|
+
linkedinFactory,
|
|
119
|
+
m2mMethod,
|
|
120
|
+
microsoftFactory,
|
|
121
|
+
oauth2Factory,
|
|
122
|
+
oidcFactory,
|
|
123
|
+
ok,
|
|
124
|
+
passkeyMethod,
|
|
125
|
+
passwordMethod,
|
|
126
|
+
revokeAllForSubject,
|
|
127
|
+
slackFactory,
|
|
128
|
+
spotifyFactory,
|
|
129
|
+
twitchFactory,
|
|
130
|
+
xFactory,
|
|
131
|
+
yahooFactory
|
|
126
132
|
};
|
|
@@ -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,208 @@
|
|
|
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, deriveSpEntityId } from "./saml-instance";
|
|
6
|
+
var { DOMParser } = xmldom;
|
|
7
|
+
function checkRecipient(profile, acsUrl) {
|
|
8
|
+
if (typeof profile.getAssertionXml !== "function") {
|
|
9
|
+
return {
|
|
10
|
+
kind: "error",
|
|
11
|
+
error: authError.internalError("saml-sp: node-saml profile exposes no getAssertionXml(); cannot " + "perform the Recipient binding check. Refusing to authenticate.")
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
let doc;
|
|
15
|
+
try {
|
|
16
|
+
doc = new DOMParser().parseFromString(profile.getAssertionXml(), "text/xml");
|
|
17
|
+
} catch (e) {
|
|
18
|
+
return {
|
|
19
|
+
kind: "error",
|
|
20
|
+
error: authError.internalError("saml-sp: failed to parse the verified assertion for the " + "Recipient check", e)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const nodes = doc.getElementsByTagNameNS("*", "SubjectConfirmationData");
|
|
24
|
+
const recipients = [];
|
|
25
|
+
for (let i = 0;i < nodes.length; i++) {
|
|
26
|
+
const r = nodes[i]?.getAttribute("Recipient");
|
|
27
|
+
if (r)
|
|
28
|
+
recipients.push(r);
|
|
29
|
+
}
|
|
30
|
+
if (recipients.length === 0) {
|
|
31
|
+
return {
|
|
32
|
+
kind: "denied",
|
|
33
|
+
reason: "assertion has no SubjectConfirmationData/@Recipient binding it " + "to this ACS"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (!recipients.includes(acsUrl)) {
|
|
37
|
+
return {
|
|
38
|
+
kind: "denied",
|
|
39
|
+
reason: "recipient mismatch: assertion not addressed to this ACS"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function extractReplayInfo(profile) {
|
|
45
|
+
if (typeof profile.getAssertionXml !== "function")
|
|
46
|
+
return null;
|
|
47
|
+
let doc;
|
|
48
|
+
try {
|
|
49
|
+
doc = new DOMParser().parseFromString(profile.getAssertionXml(), "text/xml");
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const root = doc.documentElement;
|
|
54
|
+
const assertionId = root?.getAttribute("ID") ?? "";
|
|
55
|
+
if (!assertionId)
|
|
56
|
+
return null;
|
|
57
|
+
let earliest = null;
|
|
58
|
+
for (const tag of ["Conditions", "SubjectConfirmationData"]) {
|
|
59
|
+
const els = doc.getElementsByTagNameNS("*", tag);
|
|
60
|
+
for (let i = 0;i < els.length; i++) {
|
|
61
|
+
const v = els[i]?.getAttribute("NotOnOrAfter");
|
|
62
|
+
if (!v)
|
|
63
|
+
continue;
|
|
64
|
+
const ms = Date.parse(v);
|
|
65
|
+
if (!Number.isNaN(ms))
|
|
66
|
+
earliest = earliest === null ? ms : Math.min(earliest, ms);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { assertionId, notOnOrAfterMs: earliest };
|
|
70
|
+
}
|
|
71
|
+
async function consumeAssertion(ctx, methodId, config) {
|
|
72
|
+
const idpInitiated = ctx.flow === null;
|
|
73
|
+
let spEntityId;
|
|
74
|
+
let acsUrl;
|
|
75
|
+
if (idpInitiated) {
|
|
76
|
+
if (!config.idpInitiated) {
|
|
77
|
+
return {
|
|
78
|
+
kind: "error",
|
|
79
|
+
error: authError.internalError("saml-sp: unsolicited Response reached a method with no " + "idpInitiated config")
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!ctx.dispatch) {
|
|
83
|
+
return {
|
|
84
|
+
kind: "error",
|
|
85
|
+
error: authError.internalError("saml-sp: IdP-initiated ACS dispatched without issuer context")
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
spEntityId = deriveSpEntityId(ctx.dispatch.issuerUrl, ctx.tenant.id, methodId);
|
|
89
|
+
acsUrl = ctx.dispatch.callbackUrl;
|
|
90
|
+
} else {
|
|
91
|
+
const state = ctx.methodState;
|
|
92
|
+
if (!state || !state.spEntityId || !state.acsUrl) {
|
|
93
|
+
return {
|
|
94
|
+
kind: "error",
|
|
95
|
+
error: authError.internalError("saml-sp: ACS reached without AuthnRequest method state " + "(spEntityId / acsUrl). The flow did not originate from this method.")
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
spEntityId = state.spEntityId;
|
|
99
|
+
acsUrl = state.acsUrl;
|
|
100
|
+
}
|
|
101
|
+
let samlResponse;
|
|
102
|
+
let relayState;
|
|
103
|
+
try {
|
|
104
|
+
const form = new URLSearchParams(await ctx.request.text());
|
|
105
|
+
samlResponse = form.get("SAMLResponse");
|
|
106
|
+
relayState = form.get("RelayState");
|
|
107
|
+
} catch {
|
|
108
|
+
return {
|
|
109
|
+
kind: "error",
|
|
110
|
+
error: authError.internalError("saml-sp: ACS could not read POST body")
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (!samlResponse) {
|
|
114
|
+
return { kind: "denied", reason: "missing SAMLResponse" };
|
|
115
|
+
}
|
|
116
|
+
let saml;
|
|
117
|
+
try {
|
|
118
|
+
saml = buildSamlInstance(config, {
|
|
119
|
+
spEntityId,
|
|
120
|
+
acsUrl,
|
|
121
|
+
scratch: ctx.methodScratch,
|
|
122
|
+
...idpInitiated ? { idpInitiated: true } : {},
|
|
123
|
+
...config.allowEncryptedAssertions && config.decryptionKey ? { decryptionPvk: config.decryptionKey.privateKeyPem } : {}
|
|
124
|
+
}, Date.now());
|
|
125
|
+
} catch (e) {
|
|
126
|
+
return {
|
|
127
|
+
kind: "error",
|
|
128
|
+
error: authError.internalError(`saml-sp: cannot construct verifier: ${e instanceof Error ? e.message : String(e)}`, e)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
let profile;
|
|
132
|
+
try {
|
|
133
|
+
const result = await saml.validatePostResponseAsync({
|
|
134
|
+
SAMLResponse: samlResponse,
|
|
135
|
+
...relayState !== null ? { RelayState: relayState } : {}
|
|
136
|
+
});
|
|
137
|
+
profile = result.profile;
|
|
138
|
+
} catch (e) {
|
|
139
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
140
|
+
if (!config.allowEncryptedAssertions && msg.includes("No decryption key")) {
|
|
141
|
+
return {
|
|
142
|
+
kind: "denied",
|
|
143
|
+
reason: "encrypted assertion received but encrypted assertions are not " + "enabled for this SAML connection (set allowEncryptedAssertions " + "+ decryptionKey)"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return { kind: "denied", reason: `assertion rejected: ${msg}` };
|
|
147
|
+
}
|
|
148
|
+
if (!profile || !profile.nameID) {
|
|
149
|
+
return { kind: "denied", reason: "assertion produced no usable subject" };
|
|
150
|
+
}
|
|
151
|
+
const recipientFailure = checkRecipient(profile, acsUrl);
|
|
152
|
+
if (recipientFailure)
|
|
153
|
+
return recipientFailure;
|
|
154
|
+
if (idpInitiated) {
|
|
155
|
+
const replay = extractReplayInfo(profile);
|
|
156
|
+
if (!replay) {
|
|
157
|
+
return {
|
|
158
|
+
kind: "error",
|
|
159
|
+
error: authError.internalError("saml-sp: cannot read assertion @ID for IdP-initiated replay " + "protection. Refusing to authenticate.")
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const key = `idp-replay:${replay.assertionId}`;
|
|
163
|
+
const seen = await ctx.methodScratch.get(key);
|
|
164
|
+
if (seen.ok) {
|
|
165
|
+
return {
|
|
166
|
+
kind: "denied",
|
|
167
|
+
reason: "assertion replay detected (assertion ID already seen)"
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const skewMs = (config.clockSkewSeconds ?? 60) * 1000;
|
|
171
|
+
const now = Date.now();
|
|
172
|
+
const horizon = replay.notOnOrAfterMs !== null ? replay.notOnOrAfterMs - now + skewMs : 10 * 60000;
|
|
173
|
+
const ttlMs = Math.min(Math.max(horizon, skewMs, 60000), 24 * 60 * 60000);
|
|
174
|
+
const recorded = await ctx.methodScratch.put(key, "1", ttlMs);
|
|
175
|
+
if (!recorded.ok) {
|
|
176
|
+
return {
|
|
177
|
+
kind: "error",
|
|
178
|
+
error: authError.internalError("saml-sp: could not record assertion ID for replay protection")
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const verified = {
|
|
183
|
+
nameID: profile.nameID,
|
|
184
|
+
nameIDFormat: profile.nameIDFormat ?? "",
|
|
185
|
+
...profile.sessionIndex !== undefined ? { sessionIndex: profile.sessionIndex } : {},
|
|
186
|
+
attributes: profile.attributes ?? {},
|
|
187
|
+
responseXml: profile.getSamlResponseXml?.() ?? ""
|
|
188
|
+
};
|
|
189
|
+
const mapped = mapProfile(verified, config.attributeMapping);
|
|
190
|
+
if ("error" in mapped) {
|
|
191
|
+
return { kind: "denied", reason: mapped.error };
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
kind: "success",
|
|
195
|
+
providerSubject: mapped.providerSubject,
|
|
196
|
+
properties: mapped.properties,
|
|
197
|
+
...idpInitiated && config.idpInitiated ? {
|
|
198
|
+
unsolicitedBinding: {
|
|
199
|
+
clientId: config.idpInitiated.defaultClientId,
|
|
200
|
+
redirectUri: config.idpInitiated.defaultRedirectUri,
|
|
201
|
+
scopes: config.idpInitiated.defaultScopes ?? []
|
|
202
|
+
}
|
|
203
|
+
} : {}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
consumeAssertion
|
|
208
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
raw: { responseXml: profile.responseXml }
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
mapProfile
|
|
75
|
+
};
|
|
@@ -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, deriveSpEntityId } 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 = deriveSpEntityId(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
|
+
};
|