@cosmicdrift/kumiko-framework 0.383.1 → 0.385.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/agents/examples/custom-fields-basic.md +13 -3
- package/agents/examples/designer-demo.md +6 -2
- package/agents/examples/i18n.md +36 -1
- package/agents/features.md +4 -0
- package/dist/api/auth-rate-limiters.d.ts +8 -0
- package/dist/api/auth-rate-limiters.js +105 -0
- package/dist/api/auth-routes-context.d.ts +34 -0
- package/dist/api/auth-routes-context.js +192 -0
- package/dist/api/auth-routes-invite.d.ts +2 -0
- package/dist/api/auth-routes-invite.js +171 -0
- package/dist/api/auth-routes-login.d.ts +2 -0
- package/dist/api/auth-routes-login.js +103 -0
- package/dist/api/auth-routes-mfa.d.ts +4 -0
- package/dist/api/auth-routes-mfa.js +211 -0
- package/dist/api/auth-routes-session.d.ts +2 -0
- package/dist/api/auth-routes-session.js +144 -0
- package/dist/api/auth-routes-shared.d.ts +33 -0
- package/dist/api/auth-routes-shared.js +81 -0
- package/dist/api/auth-routes-signup.d.ts +2 -0
- package/dist/api/auth-routes-signup.js +75 -0
- package/dist/api/auth-routes-token-flows.d.ts +4 -0
- package/dist/api/auth-routes-token-flows.js +91 -0
- package/dist/api/auth-routes-types.d.ts +76 -0
- package/dist/api/auth-routes-types.js +1 -0
- package/dist/api/auth-routes.d.ts +5 -85
- package/dist/api/auth-routes.js +20 -1095
- package/dist/config/index.d.ts +1 -1
- package/dist/crypto/pii-ciphertext-format.js +1 -1
- package/dist/db/blind-index-cleanup.js +1 -1
- package/dist/db/dialect.js +1 -1
- package/dist/db/distinct-on.d.ts +9 -0
- package/dist/db/distinct-on.js +28 -0
- package/dist/db/event-store-executor-write.js +2 -2
- package/dist/db/migrate-runner.js +4 -4
- package/dist/db/pg-error.js +0 -12
- package/dist/db/queries/event-consumer.js +3 -3
- package/dist/db/queries/postgres-array-literal.js +2 -2
- package/dist/db/query.js +48 -20
- package/dist/db/tenant-db.js +1 -1
- package/dist/engine/boot-validator/i18n-keys.d.ts +2 -0
- package/dist/engine/boot-validator/i18n-keys.js +15 -1
- package/dist/engine/boot-validator/index.d.ts +4 -0
- package/dist/engine/boot-validator/index.js +3 -1
- package/dist/engine/boot-validator/pii-retention.js +6 -7
- package/dist/engine/boot-validator/screens-actions.d.ts +67 -0
- package/dist/engine/boot-validator/screens-actions.js +488 -0
- package/dist/engine/boot-validator/screens-dashboard.d.ts +15 -0
- package/dist/engine/boot-validator/screens-dashboard.js +467 -0
- package/dist/engine/boot-validator/screens-forms.d.ts +24 -0
- package/dist/engine/boot-validator/screens-forms.js +516 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +47 -0
- package/dist/engine/boot-validator/screens-lists.js +319 -0
- package/dist/engine/boot-validator/screens.d.ts +4 -21
- package/dist/engine/boot-validator/screens.js +26 -1799
- package/dist/engine/build-app-schema.js +1 -1
- package/dist/engine/factories.d.ts +3 -3
- package/dist/engine/steps/read-find-one.js +1 -1
- package/dist/engine/types/index.d.ts +1 -1
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/files/file-ref-entity.d.ts +6 -126
- package/dist/files/file-ref-table.d.ts +6 -126
- package/dist/files/file-routes.js +39 -16
- package/dist/i18n/required-surface-keys.d.ts +22 -2
- package/dist/i18n/required-surface-keys.js +91 -3
- package/dist/jobs/job-runner-helpers.d.ts +45 -0
- package/dist/jobs/job-runner-helpers.js +281 -0
- package/dist/jobs/job-runner-schedulers.d.ts +23 -0
- package/dist/jobs/job-runner-schedulers.js +106 -0
- package/dist/jobs/job-runner-types.d.ts +65 -0
- package/dist/jobs/job-runner-types.js +2 -0
- package/dist/jobs/job-runner.d.ts +4 -75
- package/dist/jobs/job-runner.js +7 -384
- package/dist/pipeline/dispatch-shared.js +2 -2
- package/dist/pipeline/event-dispatcher-admin.d.ts +15 -8
- package/dist/pipeline/event-dispatcher-admin.js +28 -7
- package/dist/pipeline/event-dispatcher.d.ts +2 -2
- package/dist/pipeline/event-dispatcher.js +3 -4
- package/dist/pipeline/index.d.ts +2 -2
- package/dist/pipeline/index.js +1 -1
- package/dist/schema-cli.js +5 -0
- package/package.json +3 -3
- package/src/changes.json +39 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Context, Hono } from "hono";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import type { Dispatcher } from "../pipeline/dispatcher.ts";
|
|
4
|
+
export type PublicAuthErrorStatus = 400 | 401 | 403 | 422 | 429 | 500;
|
|
5
|
+
export declare const SignupRequestBody: z.ZodObject<{
|
|
6
|
+
email: z.ZodEmail;
|
|
7
|
+
handover: z.ZodOptional<z.ZodObject<{
|
|
8
|
+
entityType: z.ZodString;
|
|
9
|
+
token: z.ZodString;
|
|
10
|
+
}, z.core.$strip>>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export declare function isUnknownHandlerError(e: unknown): boolean;
|
|
13
|
+
export type PendingMfaStep = {
|
|
14
|
+
kind: "mfa-challenge";
|
|
15
|
+
challengeToken: string;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "mfa-setup-required";
|
|
18
|
+
preauthSetupToken: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function pendingMfaStepResponse(c: Context, step: PendingMfaStep, landing?: {
|
|
21
|
+
readonly landingPath?: string;
|
|
22
|
+
}): Response;
|
|
23
|
+
export declare function registerTokenRequestRoute(opts: {
|
|
24
|
+
api: Hono;
|
|
25
|
+
dispatcher: Dispatcher;
|
|
26
|
+
path: string;
|
|
27
|
+
requestHandler: string;
|
|
28
|
+
schema?: typeof RequestTokenBody | typeof SignupRequestBody;
|
|
29
|
+
}): void;
|
|
30
|
+
declare const RequestTokenBody: z.ZodObject<{
|
|
31
|
+
email: z.ZodEmail;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { createAnonymousUser } from "../engine/system-user.js";
|
|
3
|
+
import { SYSTEM_TENANT_ID } from "../engine/types/index.js";
|
|
4
|
+
import { NotFoundError } from "../errors/index.js";
|
|
5
|
+
// Cross-device try-first handover (kumiko-framework#3035 follow-up,
|
|
6
|
+
// offlot-app#454): an optional tenant-handover grant the signup-request
|
|
7
|
+
// route forwards to the handler for server-side verification and binding —
|
|
8
|
+
// the grant itself never appears anywhere else (URL, mail, log, response).
|
|
9
|
+
export const SignupRequestBody = z.object({
|
|
10
|
+
email: z.email(),
|
|
11
|
+
handover: z
|
|
12
|
+
.object({
|
|
13
|
+
entityType: z.string().min(1).max(200),
|
|
14
|
+
token: z.string().min(1).max(4096),
|
|
15
|
+
})
|
|
16
|
+
.optional(),
|
|
17
|
+
});
|
|
18
|
+
// Shape guard for "handler not registered" — the only legitimate reason to
|
|
19
|
+
// fall back to a single-tenant reply on /auth/tenants or /auth/switch-tenant.
|
|
20
|
+
// Every other error (DB down, revoker throws, access denied, …) has to
|
|
21
|
+
// propagate — otherwise we'd silently paper over outages.
|
|
22
|
+
export function isUnknownHandlerError(e) {
|
|
23
|
+
if (!(e instanceof NotFoundError))
|
|
24
|
+
return false;
|
|
25
|
+
// @cast-boundary error-details — KumikoError.details shape is per-error
|
|
26
|
+
const details = e.details;
|
|
27
|
+
return details?.entity === "handler";
|
|
28
|
+
}
|
|
29
|
+
export function pendingMfaStepResponse(c, step, landing = {}) {
|
|
30
|
+
if (step.kind === "mfa-setup-required") {
|
|
31
|
+
return c.json({
|
|
32
|
+
isSuccess: true,
|
|
33
|
+
mfaSetupRequired: true,
|
|
34
|
+
preauthSetupToken: step.preauthSetupToken,
|
|
35
|
+
...landing,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return c.json({
|
|
39
|
+
isSuccess: true,
|
|
40
|
+
mfaRequired: true,
|
|
41
|
+
challengeToken: step.challengeToken,
|
|
42
|
+
...landing,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// --- shared route builders for token flows ---------------------------------
|
|
46
|
+
// Password-reset, email-verification and signup share the exact same request
|
|
47
|
+
// HTTP-shape: parse body → dispatch the request handler → always-200 (no
|
|
48
|
+
// enumeration). The handler mints the token AND dispatches the magic-link mail
|
|
49
|
+
// via delivery (ctx.notify); the route never sees the token. confirm-route
|
|
50
|
+
// validates the token + does the state change → typed failure or 200. Keeping
|
|
51
|
+
// the silent-success invariant in one place means changing how the framework
|
|
52
|
+
// handles "invalid_body" on a public token endpoint is one edit, not three.
|
|
53
|
+
export function registerTokenRequestRoute(opts) {
|
|
54
|
+
const body = opts.schema ?? RequestTokenBody;
|
|
55
|
+
opts.api.post(opts.path, async (c) => {
|
|
56
|
+
const raw = await c.req.json().catch(() => null);
|
|
57
|
+
const parsed = body.safeParse(raw);
|
|
58
|
+
// Malformed body → silent success. A probing client mustn't learn
|
|
59
|
+
// anything from the shape of their input.
|
|
60
|
+
if (!parsed.success)
|
|
61
|
+
return c.json({ isSuccess: true });
|
|
62
|
+
// The handler dispatches the magic-link mail via delivery before returning.
|
|
63
|
+
// Handler-level failures (misconfiguration, e.g. no RateLimitResolver) stay
|
|
64
|
+
// out of the response so it is uniform for unknown vs. known emails, but
|
|
65
|
+
// they are logged — otherwise the mail silently never goes out. No email in
|
|
66
|
+
// the log line (PII).
|
|
67
|
+
const result = await opts.dispatcher.write(opts.requestHandler, parsed.data, createAnonymousUser(SYSTEM_TENANT_ID));
|
|
68
|
+
// rate_limited is an expected, attacker-triggerable outcome; logging it
|
|
69
|
+
// would flood the sink and bury real misconfiguration errors.
|
|
70
|
+
if (!result.isSuccess && result.error.code !== "rate_limited") {
|
|
71
|
+
console.error(`[kumiko] token request handler "${opts.requestHandler}" failed: ${result.error.code}`);
|
|
72
|
+
}
|
|
73
|
+
return c.json({ isSuccess: true });
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Shared request-body shape for request-password-reset + request-email-
|
|
77
|
+
// verification. Extracted so the two flows stay in sync when the schema
|
|
78
|
+
// gains optional fields (locale, deviceId, …).
|
|
79
|
+
const RequestTokenBody = z.object({
|
|
80
|
+
email: z.email(),
|
|
81
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { createAnonymousUser } from "../engine/system-user.js";
|
|
3
|
+
import { SYSTEM_TENANT_ID } from "../engine/types/index.js";
|
|
4
|
+
import { Routes } from "./api-constants.js";
|
|
5
|
+
import { pendingMfaStepResponse, registerTokenRequestRoute, SignupRequestBody, } from "./auth-routes-shared.js";
|
|
6
|
+
const SignupConfirmBody = z.object({
|
|
7
|
+
token: z.string().min(1),
|
|
8
|
+
password: z.string().min(8).max(200),
|
|
9
|
+
});
|
|
10
|
+
export function registerSignupRoutes(ctx) {
|
|
11
|
+
const { api, dispatcher, config, mintSessionAndRespond, landingPathFragment } = ctx;
|
|
12
|
+
// Signup confirm mints the session (auto-login) unless the MFA gate applies;
|
|
13
|
+
// then it answers with the MFA step only, no session or cookies.
|
|
14
|
+
if (config.signup) {
|
|
15
|
+
const sg = config.signup;
|
|
16
|
+
registerTokenRequestRoute({
|
|
17
|
+
api,
|
|
18
|
+
dispatcher,
|
|
19
|
+
path: Routes.authSignupRequest,
|
|
20
|
+
requestHandler: sg.requestHandler,
|
|
21
|
+
schema: SignupRequestBody,
|
|
22
|
+
});
|
|
23
|
+
api.post(Routes.authSignupConfirm, async (c) => {
|
|
24
|
+
const raw = await c.req.json().catch(() => null);
|
|
25
|
+
const parsed = SignupConfirmBody.safeParse(raw);
|
|
26
|
+
if (!parsed.success) {
|
|
27
|
+
return c.json({ isSuccess: false, error: "invalid_body" }, 400);
|
|
28
|
+
}
|
|
29
|
+
const result = await dispatcher.write(sg.confirmHandler, parsed.data, createAnonymousUser(SYSTEM_TENANT_ID));
|
|
30
|
+
if (!result.isSuccess) {
|
|
31
|
+
// 422 for invalid_signup_token (handler-level UnprocessableError).
|
|
32
|
+
// @cast-boundary engine-payload — KumikoError.httpStatus narrows to the http-status union
|
|
33
|
+
const status = result.error.httpStatus;
|
|
34
|
+
return c.json({ isSuccess: false, error: result.error }, status);
|
|
35
|
+
}
|
|
36
|
+
// @cast-boundary engine-payload — generic dispatcher.write result for signup-confirm
|
|
37
|
+
const data = result.data;
|
|
38
|
+
if (data.kind !== "auth-session") {
|
|
39
|
+
// No session yet, but the account and a claimed handover exist: hand
|
|
40
|
+
// the signup landing to the client so the login that follows lands
|
|
41
|
+
// where a signup without the MFA gate would have.
|
|
42
|
+
return pendingMfaStepResponse(c, data, landingPathFragment({ flow: "signup", ...data.signup }));
|
|
43
|
+
}
|
|
44
|
+
// Session creation + JWT sign + cookies — see mintSessionAndRespond.
|
|
45
|
+
const token = await mintSessionAndRespond(c, data.session);
|
|
46
|
+
const signupOutcome = {
|
|
47
|
+
roles: data.session.roles,
|
|
48
|
+
tenantId: data.session.tenantId,
|
|
49
|
+
tenantKey: data.tenantKey,
|
|
50
|
+
...(data.handover !== undefined && { handover: data.handover }),
|
|
51
|
+
};
|
|
52
|
+
const landingPath = landingPathFragment({ flow: "signup", ...signupOutcome });
|
|
53
|
+
return c.json({
|
|
54
|
+
isSuccess: true,
|
|
55
|
+
token,
|
|
56
|
+
user: {
|
|
57
|
+
id: data.session.id,
|
|
58
|
+
tenantId: data.session.tenantId,
|
|
59
|
+
roles: data.session.roles,
|
|
60
|
+
},
|
|
61
|
+
// tenantKey for the post-signup redirect to /<tenantKey>/. Unlike the
|
|
62
|
+
// login response, which only needs `tenants[]` (the user picks in the
|
|
63
|
+
// switcher), signup knows exactly ONE membership — the frontend UI uses
|
|
64
|
+
// it directly as the redirect target.
|
|
65
|
+
tenantKey: data.tenantKey,
|
|
66
|
+
// Cross-device try-first handover (kumiko-framework#3035 follow-up):
|
|
67
|
+
// present only when signup-confirm actually claimed a bound grant —
|
|
68
|
+
// absent, not null, so a client that doesn't know the field sees
|
|
69
|
+
// nothing unusual.
|
|
70
|
+
...(data.handover !== undefined && { handover: data.handover }),
|
|
71
|
+
...landingPath,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AuthRouteContext } from "./auth-routes-context.ts";
|
|
2
|
+
export declare function registerPasswordResetRoutes(ctx: AuthRouteContext): void;
|
|
3
|
+
export declare function registerEmailVerificationRoutes(ctx: AuthRouteContext): void;
|
|
4
|
+
export declare function registerAccountUnlockRoutes(ctx: AuthRouteContext): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { createAnonymousUser } from "../engine/system-user.js";
|
|
3
|
+
import { SYSTEM_TENANT_ID } from "../engine/types/index.js";
|
|
4
|
+
import { Routes } from "./api-constants.js";
|
|
5
|
+
import { registerTokenRequestRoute } from "./auth-routes-shared.js";
|
|
6
|
+
const ResetPasswordBody = z.object({
|
|
7
|
+
token: z.string().min(1),
|
|
8
|
+
newPassword: z.string().min(8).max(200),
|
|
9
|
+
});
|
|
10
|
+
const VerifyEmailBody = z.object({
|
|
11
|
+
token: z.string().min(1),
|
|
12
|
+
});
|
|
13
|
+
function registerTokenConfirmRoute(opts) {
|
|
14
|
+
opts.api.post(opts.path, async (c) => {
|
|
15
|
+
const raw = await c.req.json().catch(() => null);
|
|
16
|
+
const parsed = opts.schema.safeParse(raw);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
return c.json({ isSuccess: false, error: "invalid_body" }, 400);
|
|
19
|
+
}
|
|
20
|
+
const result = await opts.dispatcher.write(opts.confirmHandler, parsed.data, createAnonymousUser(SYSTEM_TENANT_ID));
|
|
21
|
+
if (!result.isSuccess) {
|
|
22
|
+
const status = result.error.httpStatus; // @cast-boundary engine-payload
|
|
23
|
+
return c.json({ isSuccess: false, error: result.error }, status);
|
|
24
|
+
}
|
|
25
|
+
return c.json({ isSuccess: true });
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function registerPasswordResetRoutes(ctx) {
|
|
29
|
+
const { api, dispatcher, config } = ctx;
|
|
30
|
+
// POST /auth/request-password-reset + /auth/reset-password — public.
|
|
31
|
+
// Silent-success on request (no enumeration), typed failure on confirm.
|
|
32
|
+
// Rate-limit covered via config.rateLimit.auth (Sprint G.5 L2, /auth/*).
|
|
33
|
+
if (config.passwordReset) {
|
|
34
|
+
const pr = config.passwordReset;
|
|
35
|
+
registerTokenRequestRoute({
|
|
36
|
+
api,
|
|
37
|
+
dispatcher,
|
|
38
|
+
path: Routes.authRequestPasswordReset,
|
|
39
|
+
requestHandler: pr.requestHandler,
|
|
40
|
+
});
|
|
41
|
+
registerTokenConfirmRoute({
|
|
42
|
+
api,
|
|
43
|
+
dispatcher,
|
|
44
|
+
path: Routes.authResetPassword,
|
|
45
|
+
confirmHandler: pr.confirmHandler,
|
|
46
|
+
schema: ResetPasswordBody,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function registerEmailVerificationRoutes(ctx) {
|
|
51
|
+
const { api, dispatcher, config } = ctx;
|
|
52
|
+
// Email-verification mirrors password-reset.
|
|
53
|
+
if (config.emailVerification) {
|
|
54
|
+
const ev = config.emailVerification;
|
|
55
|
+
registerTokenRequestRoute({
|
|
56
|
+
api,
|
|
57
|
+
dispatcher,
|
|
58
|
+
path: Routes.authRequestEmailVerification,
|
|
59
|
+
requestHandler: ev.requestHandler,
|
|
60
|
+
});
|
|
61
|
+
registerTokenConfirmRoute({
|
|
62
|
+
api,
|
|
63
|
+
dispatcher,
|
|
64
|
+
path: Routes.authVerifyEmail,
|
|
65
|
+
confirmHandler: ev.confirmHandler,
|
|
66
|
+
schema: VerifyEmailBody,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function registerAccountUnlockRoutes(ctx) {
|
|
71
|
+
const { api, dispatcher, config } = ctx;
|
|
72
|
+
// Account-unlock mirrors email-verification (token-only confirm body) —
|
|
73
|
+
// clears the Redis lockout state instead of an entity field, see
|
|
74
|
+
// confirm-account-unlock.write.ts.
|
|
75
|
+
if (config.accountUnlock) {
|
|
76
|
+
const au = config.accountUnlock;
|
|
77
|
+
registerTokenRequestRoute({
|
|
78
|
+
api,
|
|
79
|
+
dispatcher,
|
|
80
|
+
path: Routes.authRequestAccountUnlock,
|
|
81
|
+
requestHandler: au.requestHandler,
|
|
82
|
+
});
|
|
83
|
+
registerTokenConfirmRoute({
|
|
84
|
+
api,
|
|
85
|
+
dispatcher,
|
|
86
|
+
path: Routes.authConfirmAccountUnlock,
|
|
87
|
+
confirmHandler: au.confirmHandler,
|
|
88
|
+
schema: VerifyEmailBody,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { SessionUser } from "../engine/types/index.ts";
|
|
2
|
+
import type { AuthSessionChecker, TokenVerifier } from "./auth-middleware.ts";
|
|
3
|
+
import type { ClientIpResolver } from "./client-ip.ts";
|
|
4
|
+
import type { PostAuthLandingResolver } from "./post-auth-landing.ts";
|
|
5
|
+
export type LoginRateLimiter = {
|
|
6
|
+
check(key: string): Promise<boolean>;
|
|
7
|
+
reset(key: string): Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export type SessionMetadata = {
|
|
10
|
+
readonly ip: string;
|
|
11
|
+
readonly userAgent: string;
|
|
12
|
+
};
|
|
13
|
+
export type SessionCreator = (user: SessionUser, meta: SessionMetadata) => Promise<string>;
|
|
14
|
+
export type SessionRevoker = (sid: string) => Promise<void>;
|
|
15
|
+
export type SessionMassRevoker = (userId: string) => Promise<number>;
|
|
16
|
+
export type SessionChecker = AuthSessionChecker;
|
|
17
|
+
export type AuthRoutesConfig = {
|
|
18
|
+
membershipQuery: string;
|
|
19
|
+
userQuery?: string;
|
|
20
|
+
loginHandler?: string;
|
|
21
|
+
loginErrorStatusMap?: Readonly<Record<string, number>>;
|
|
22
|
+
loginRateLimit?: LoginRateLimiter | null;
|
|
23
|
+
mfaVerifyHandler?: string;
|
|
24
|
+
switchTenantMfaGateHandler?: string;
|
|
25
|
+
mfaVerifyErrorStatusMap?: Readonly<Record<string, number>>;
|
|
26
|
+
mfaVerifyRateLimit?: LoginRateLimiter | null;
|
|
27
|
+
mfaPreauthEnableStartHandler?: string;
|
|
28
|
+
mfaPreauthEnableStartErrorStatusMap?: Readonly<Record<string, number>>;
|
|
29
|
+
mfaPreauthEnableStartRateLimit?: LoginRateLimiter | null;
|
|
30
|
+
mfaPreauthConfirmHandler?: string;
|
|
31
|
+
mfaPreauthConfirmErrorStatusMap?: Readonly<Record<string, number>>;
|
|
32
|
+
mfaPreauthConfirmRateLimit?: LoginRateLimiter | null;
|
|
33
|
+
sessionCreator?: SessionCreator;
|
|
34
|
+
sessionRevoker?: SessionRevoker;
|
|
35
|
+
sessionChecker?: SessionChecker;
|
|
36
|
+
tokenVerifier?: TokenVerifier;
|
|
37
|
+
patRateLimiter?: LoginRateLimiter;
|
|
38
|
+
resolveTenantLifecycleStatus?: import("./auth-middleware.ts").TenantLifecycleStatusResolver;
|
|
39
|
+
passwordReset?: PasswordResetConfig;
|
|
40
|
+
emailVerification?: EmailVerificationConfig;
|
|
41
|
+
accountUnlock?: AccountUnlockConfig;
|
|
42
|
+
signup?: SignupConfig;
|
|
43
|
+
invite?: InviteConfig;
|
|
44
|
+
cookieSameSite?: "lax" | "strict";
|
|
45
|
+
cookieDomain?: string;
|
|
46
|
+
secureCookies?: boolean;
|
|
47
|
+
retiredCookieDomains?: readonly string[];
|
|
48
|
+
allowedOrigins?: readonly string[];
|
|
49
|
+
publicOrigin?: string;
|
|
50
|
+
unsafeSkipOriginCheck?: boolean;
|
|
51
|
+
trustedProxyHops?: number;
|
|
52
|
+
postAuthLanding?: PostAuthLandingResolver;
|
|
53
|
+
resolver?: ClientIpResolver;
|
|
54
|
+
};
|
|
55
|
+
export type PasswordResetConfig = {
|
|
56
|
+
requestHandler: string;
|
|
57
|
+
confirmHandler: string;
|
|
58
|
+
};
|
|
59
|
+
export type EmailVerificationConfig = {
|
|
60
|
+
requestHandler: string;
|
|
61
|
+
confirmHandler: string;
|
|
62
|
+
};
|
|
63
|
+
export type AccountUnlockConfig = {
|
|
64
|
+
requestHandler: string;
|
|
65
|
+
confirmHandler: string;
|
|
66
|
+
};
|
|
67
|
+
export type InviteConfig = {
|
|
68
|
+
readonly acceptHandler: string;
|
|
69
|
+
readonly acceptWithLoginHandler: string;
|
|
70
|
+
readonly signupCompleteHandler: string;
|
|
71
|
+
readonly infoHandler?: string;
|
|
72
|
+
};
|
|
73
|
+
export type SignupConfig = {
|
|
74
|
+
requestHandler: string;
|
|
75
|
+
confirmHandler: string;
|
|
76
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,89 +1,9 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import type { Redis } from "ioredis";
|
|
3
|
-
import { type SessionUser } from "../engine/types/index.ts";
|
|
1
|
+
import type { Hono } from "hono";
|
|
4
2
|
import type { Dispatcher } from "../pipeline/dispatcher.ts";
|
|
5
|
-
import
|
|
6
|
-
import { type ClientIpResolver } from "./client-ip.ts";
|
|
3
|
+
import type { AuthRoutesConfig } from "./auth-routes-types.ts";
|
|
7
4
|
import type { JwtHelper } from "./jwt.ts";
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
reset(key: string): Promise<void>;
|
|
12
|
-
};
|
|
13
|
-
export type SessionMetadata = {
|
|
14
|
-
readonly ip: string;
|
|
15
|
-
readonly userAgent: string;
|
|
16
|
-
};
|
|
17
|
-
export type SessionCreator = (user: SessionUser, meta: SessionMetadata) => Promise<string>;
|
|
18
|
-
export type SessionRevoker = (sid: string) => Promise<void>;
|
|
19
|
-
export type SessionMassRevoker = (userId: string) => Promise<number>;
|
|
20
|
-
export type SessionChecker = AuthSessionChecker;
|
|
21
|
-
export type { AuthSessionStatus };
|
|
5
|
+
export type { AuthSessionStatus } from "./auth-middleware.ts";
|
|
6
|
+
export { createInMemoryLoginRateLimiter, createRedisLoginRateLimiter, inMemoryLoginRateLimiterFallback, } from "./auth-rate-limiters.ts";
|
|
7
|
+
export type { AccountUnlockConfig, AuthRoutesConfig, EmailVerificationConfig, InviteConfig, LoginRateLimiter, PasswordResetConfig, SessionChecker, SessionCreator, SessionMassRevoker, SessionMetadata, SessionRevoker, SignupConfig, } from "./auth-routes-types.ts";
|
|
22
8
|
export declare function assertSessionCallbacksPaired(auth: Pick<AuthRoutesConfig, "sessionCreator" | "sessionRevoker" | "sessionChecker"> | undefined): void;
|
|
23
|
-
export type AuthRoutesConfig = {
|
|
24
|
-
membershipQuery: string;
|
|
25
|
-
userQuery?: string;
|
|
26
|
-
loginHandler?: string;
|
|
27
|
-
loginErrorStatusMap?: Readonly<Record<string, number>>;
|
|
28
|
-
loginRateLimit?: LoginRateLimiter | null;
|
|
29
|
-
mfaVerifyHandler?: string;
|
|
30
|
-
switchTenantMfaGateHandler?: string;
|
|
31
|
-
mfaVerifyErrorStatusMap?: Readonly<Record<string, number>>;
|
|
32
|
-
mfaVerifyRateLimit?: LoginRateLimiter | null;
|
|
33
|
-
mfaPreauthEnableStartHandler?: string;
|
|
34
|
-
mfaPreauthEnableStartErrorStatusMap?: Readonly<Record<string, number>>;
|
|
35
|
-
mfaPreauthEnableStartRateLimit?: LoginRateLimiter | null;
|
|
36
|
-
mfaPreauthConfirmHandler?: string;
|
|
37
|
-
mfaPreauthConfirmErrorStatusMap?: Readonly<Record<string, number>>;
|
|
38
|
-
mfaPreauthConfirmRateLimit?: LoginRateLimiter | null;
|
|
39
|
-
sessionCreator?: SessionCreator;
|
|
40
|
-
sessionRevoker?: SessionRevoker;
|
|
41
|
-
sessionChecker?: SessionChecker;
|
|
42
|
-
tokenVerifier?: TokenVerifier;
|
|
43
|
-
patRateLimiter?: LoginRateLimiter;
|
|
44
|
-
resolveTenantLifecycleStatus?: import("./auth-middleware.ts").TenantLifecycleStatusResolver;
|
|
45
|
-
passwordReset?: PasswordResetConfig;
|
|
46
|
-
emailVerification?: EmailVerificationConfig;
|
|
47
|
-
accountUnlock?: AccountUnlockConfig;
|
|
48
|
-
signup?: SignupConfig;
|
|
49
|
-
invite?: InviteConfig;
|
|
50
|
-
cookieSameSite?: "lax" | "strict";
|
|
51
|
-
cookieDomain?: string;
|
|
52
|
-
secureCookies?: boolean;
|
|
53
|
-
retiredCookieDomains?: readonly string[];
|
|
54
|
-
allowedOrigins?: readonly string[];
|
|
55
|
-
publicOrigin?: string;
|
|
56
|
-
unsafeSkipOriginCheck?: boolean;
|
|
57
|
-
trustedProxyHops?: number;
|
|
58
|
-
postAuthLanding?: PostAuthLandingResolver;
|
|
59
|
-
resolver?: ClientIpResolver;
|
|
60
|
-
};
|
|
61
|
-
export type PasswordResetConfig = {
|
|
62
|
-
requestHandler: string;
|
|
63
|
-
confirmHandler: string;
|
|
64
|
-
};
|
|
65
|
-
export type EmailVerificationConfig = {
|
|
66
|
-
requestHandler: string;
|
|
67
|
-
confirmHandler: string;
|
|
68
|
-
};
|
|
69
|
-
export type AccountUnlockConfig = {
|
|
70
|
-
requestHandler: string;
|
|
71
|
-
confirmHandler: string;
|
|
72
|
-
};
|
|
73
|
-
export type InviteConfig = {
|
|
74
|
-
readonly acceptHandler: string;
|
|
75
|
-
readonly acceptWithLoginHandler: string;
|
|
76
|
-
readonly signupCompleteHandler: string;
|
|
77
|
-
readonly infoHandler?: string;
|
|
78
|
-
};
|
|
79
|
-
export type SignupConfig = {
|
|
80
|
-
requestHandler: string;
|
|
81
|
-
confirmHandler: string;
|
|
82
|
-
};
|
|
83
|
-
export declare function inMemoryLoginRateLimiterFallback(): LoginRateLimiter;
|
|
84
|
-
export declare function createInMemoryLoginRateLimiter(maxAttempts?: number, windowMs?: number, { maxEntries, sweepThreshold, }?: {
|
|
85
|
-
maxEntries?: number;
|
|
86
|
-
sweepThreshold?: number;
|
|
87
|
-
}): LoginRateLimiter;
|
|
88
|
-
export declare function createRedisLoginRateLimiter(redis: Redis, maxAttempts?: number, windowMs?: number, namespace?: string): LoginRateLimiter;
|
|
89
9
|
export declare function createAuthRoutes(dispatcher: Dispatcher, jwt: JwtHelper, config: AuthRoutesConfig): Hono;
|