@pattern-js/mod-identity 0.4.0 → 0.5.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/deliver.d.ts +6 -0
- package/dist/deliver.d.ts.map +1 -1
- package/dist/deliver.js +32 -5
- package/dist/deliver.js.map +1 -1
- package/dist/frontend.d.ts +7 -4
- package/dist/frontend.d.ts.map +1 -1
- package/dist/frontend.js +20 -63
- package/dist/frontend.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +10 -3
- package/dist/mod.js.map +1 -1
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +86 -13
- package/dist/ops.js.map +1 -1
- package/dist/options.d.ts +24 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +4 -0
- package/dist/options.js.map +1 -1
- package/dist/pages/html.d.ts.map +1 -1
- package/dist/pages/html.js +15 -0
- package/dist/pages/html.js.map +1 -1
- package/dist/pages/login.d.ts +14 -2
- package/dist/pages/login.d.ts.map +1 -1
- package/dist/pages/login.js +21 -3
- package/dist/pages/login.js.map +1 -1
- package/dist/service.d.ts +44 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +67 -4
- package/dist/service.js.map +1 -1
- package/dist/store/memory.d.ts.map +1 -1
- package/dist/store/memory.js +0 -0
- package/dist/store/memory.js.map +1 -1
- package/dist/store/migrations.d.ts.map +1 -1
- package/dist/store/migrations.js +8 -0
- package/dist/store/migrations.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +28 -2
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/types.d.ts +24 -0
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/types.js.map +1 -1
- package/dist/tokens.d.ts +13 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +20 -2
- package/dist/tokens.js.map +1 -1
- package/dist/user-page.d.ts +13 -0
- package/dist/user-page.d.ts.map +1 -0
- package/dist/user-page.js +204 -0
- package/dist/user-page.js.map +1 -0
- package/dist/workflows.d.ts +2 -1
- package/dist/workflows.d.ts.map +1 -1
- package/dist/workflows.js +13 -4
- package/dist/workflows.js.map +1 -1
- package/docs/index.md +75 -11
- package/docs/ops/identity.code.callback.md +17 -0
- package/docs/ops/identity.token.callback.md +3 -1
- package/docs/ops/identity.users.setRoles.md +5 -4
- package/package.json +5 -5
package/dist/deliver.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export interface DeliverInput {
|
|
|
19
19
|
origin?: string | null;
|
|
20
20
|
/** Token expiry (ms epoch) — turns "expires soon" into "valid for 7 days" in the copy. */
|
|
21
21
|
expiresAt?: number;
|
|
22
|
+
/**
|
|
23
|
+
* The short sign-in code bound to the same token (raw digits, e.g. "482913").
|
|
24
|
+
* Rides the hook payload so custom templates can render it — the PWA path,
|
|
25
|
+
* where the link opens in the wrong cookie jar and the user types this instead.
|
|
26
|
+
*/
|
|
27
|
+
code?: string;
|
|
22
28
|
}
|
|
23
29
|
export interface DeliverResult {
|
|
24
30
|
delivered: boolean;
|
package/dist/deliver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../src/deliver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../src/deliver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,eAAO,MAAM,kBAAkB,0BAA0B,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,mGAAmG;IACnG,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,0FAA0F;AAC1F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAExE;AAwED,wBAAsB,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CA+B9F"}
|
package/dist/deliver.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* printed to the server console — which is precisely the zero-config dev
|
|
9
9
|
* login, and the same primitive bootstrap uses.
|
|
10
10
|
*/
|
|
11
|
+
import { formatCode } from "./tokens.js";
|
|
11
12
|
export const DELIVER_TOKEN_HOOK = "identity.deliverToken";
|
|
12
13
|
/** Best-effort absolute URL — `origin` (from the trigger's `url`) + the callback path. */
|
|
13
14
|
export function absoluteUrl(path, origin) {
|
|
@@ -43,8 +44,9 @@ function humanTtl(expiresAt) {
|
|
|
43
44
|
* every channel subscribed to the hook (email, SMS, chat…) gets sensible
|
|
44
45
|
* wording for free, and a forked delivery workflow may still write its own.
|
|
45
46
|
*/
|
|
46
|
-
function copyFor(purpose, ttl) {
|
|
47
|
-
const
|
|
47
|
+
function copyFor(purpose, ttl, code) {
|
|
48
|
+
const what = code ? "link and code are" : "link is";
|
|
49
|
+
const expiry = ttl ? `The ${what} single-use and valid for ${ttl}.` : `The ${what} single-use and expire${code ? "" : "s"} soon.`;
|
|
48
50
|
switch (purpose) {
|
|
49
51
|
case "invite":
|
|
50
52
|
return {
|
|
@@ -52,14 +54,37 @@ function copyFor(purpose, ttl) {
|
|
|
52
54
|
message: `You've been invited to join. Open the link below to accept the invitation — your account is created on the spot, then you sign in for the first time. ${expiry}`,
|
|
53
55
|
};
|
|
54
56
|
case "login":
|
|
55
|
-
return {
|
|
57
|
+
return {
|
|
58
|
+
subject: "Your sign-in link",
|
|
59
|
+
message: code
|
|
60
|
+
? // The code is the installed-app path: the link would open in the
|
|
61
|
+
// system browser, but a typed code signs in wherever the user is.
|
|
62
|
+
`Open the link below to sign in — or enter the code ${formatCode(code)} where you requested it. ${expiry}`
|
|
63
|
+
: `Open the link below to sign in. ${expiry}`,
|
|
64
|
+
};
|
|
56
65
|
default:
|
|
57
66
|
return { subject: `Your ${purpose} link`, message: `Open the link below to continue. ${expiry}` };
|
|
58
67
|
}
|
|
59
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* In production an emailed link must not be built from the request: the Host
|
|
71
|
+
* header is client-controlled, so a forged one would mail a victim a VALID
|
|
72
|
+
* token pointing at someone else's domain. Dev keeps the request origin
|
|
73
|
+
* (localhost just works); production requires PATTERN_PUBLIC_URL.
|
|
74
|
+
*/
|
|
75
|
+
function linkOrigin(ctx, requestOrigin) {
|
|
76
|
+
const configured = configuredOrigin(ctx);
|
|
77
|
+
if (configured)
|
|
78
|
+
return configured;
|
|
79
|
+
const env = ctx.env?.NODE_ENV ?? process.env.NODE_ENV;
|
|
80
|
+
if (env === "production") {
|
|
81
|
+
throw new Error("mod-identity: PATTERN_PUBLIC_URL is required in production — emailed links are not built from the request's Host header, which a client can forge. Set it to your public origin (https://app.example.com).");
|
|
82
|
+
}
|
|
83
|
+
return requestOrigin;
|
|
84
|
+
}
|
|
60
85
|
export async function deliverToken(ctx, input) {
|
|
61
|
-
const url = absoluteUrl(input.path,
|
|
62
|
-
const { subject, message } = copyFor(input.purpose, humanTtl(input.expiresAt));
|
|
86
|
+
const url = absoluteUrl(input.path, linkOrigin(ctx, input.origin));
|
|
87
|
+
const { subject, message } = copyFor(input.purpose, humanTtl(input.expiresAt), input.code);
|
|
63
88
|
const payload = {
|
|
64
89
|
email: input.email,
|
|
65
90
|
url,
|
|
@@ -67,6 +92,7 @@ export async function deliverToken(ctx, input) {
|
|
|
67
92
|
delivered: false,
|
|
68
93
|
subject,
|
|
69
94
|
message,
|
|
95
|
+
...(input.code !== undefined ? { code: input.code } : {}),
|
|
70
96
|
...(input.expiresAt !== undefined ? { expiresAt: input.expiresAt } : {}),
|
|
71
97
|
};
|
|
72
98
|
let result;
|
|
@@ -82,6 +108,7 @@ export async function deliverToken(ctx, input) {
|
|
|
82
108
|
if (!delivered) {
|
|
83
109
|
console.log(`\n[pattern] ✉ ${input.purpose} link for ${input.email}\n` +
|
|
84
110
|
`[pattern] ${url}\n` +
|
|
111
|
+
(input.code ? `[pattern] code: ${formatCode(input.code)}\n` : "") +
|
|
85
112
|
`[pattern] (deliver these yourself by subscribing a workflow to the "${DELIVER_TOKEN_HOOK}" hook)\n`);
|
|
86
113
|
}
|
|
87
114
|
return { delivered, url };
|
package/dist/deliver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliver.js","sourceRoot":"","sources":["../src/deliver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"deliver.js","sourceRoot":"","sources":["../src/deliver.ts"],"names":[],"mappings":"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"}
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @pattern-js/mod-identity — admin screens
|
|
2
|
+
* @pattern-js/mod-identity — admin screens.
|
|
3
3
|
*
|
|
4
|
-
* Users / Invite / Sessions under
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Users / Invite / Sessions under the "Administration" section — Tier-1 declarative
|
|
5
|
+
* (zero build) except the user DETAILS page, which is Tier-2: a roles editor
|
|
6
|
+
* needs interactivity the declarative kinds can't express. Every view and
|
|
7
|
+
* action names its own dedicated route (see `./admin-routes.ts`) — there is no
|
|
8
|
+
* generic op invoker; the routes are admin-scope-gated and the ops re-check
|
|
9
|
+
* scopes too.
|
|
7
10
|
*/
|
|
8
11
|
import type { FrontendContribution } from "@pattern-js/core";
|
|
9
12
|
export declare function identityFrontend(): FrontendContribution;
|
package/dist/frontend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../src/frontend.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"frontend.d.ts","sourceRoot":"","sources":["../src/frontend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAI7D,wBAAgB,gBAAgB,IAAI,oBAAoB,CA4KvD"}
|
package/dist/frontend.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @pattern-js/mod-identity — admin screens
|
|
2
|
+
* @pattern-js/mod-identity — admin screens.
|
|
3
3
|
*
|
|
4
|
-
* Users / Invite / Sessions under
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Users / Invite / Sessions under the "Administration" section — Tier-1 declarative
|
|
5
|
+
* (zero build) except the user DETAILS page, which is Tier-2: a roles editor
|
|
6
|
+
* needs interactivity the declarative kinds can't express. Every view and
|
|
7
|
+
* action names its own dedicated route (see `./admin-routes.ts`) — there is no
|
|
8
|
+
* generic op invoker; the routes are admin-scope-gated and the ops re-check
|
|
9
|
+
* scopes too.
|
|
7
10
|
*/
|
|
8
11
|
import { PATHS } from "./admin-routes.js";
|
|
12
|
+
import { USER_PAGE_REMOTE } from "./user-page.js";
|
|
9
13
|
export function identityFrontend() {
|
|
10
14
|
return {
|
|
11
15
|
menu: [
|
|
12
|
-
{ category: "
|
|
13
|
-
{ category: "
|
|
14
|
-
{ category: "
|
|
15
|
-
{ category: "
|
|
16
|
+
{ category: "Administration", label: "Users", icon: "users", path: "/x/identity/users", order: 10 },
|
|
17
|
+
{ category: "Administration", label: "Invites", icon: "user-plus", path: "/x/identity/invite", order: 20 },
|
|
18
|
+
{ category: "Administration", label: "Sessions", icon: "key-round", path: "/x/identity/sessions", order: 30 },
|
|
19
|
+
{ category: "Administration", label: "API tokens", icon: "key-square", path: "/x/identity/api-tokens", order: 40 },
|
|
16
20
|
],
|
|
17
21
|
pages: [
|
|
18
22
|
{
|
|
@@ -40,60 +44,13 @@ export function identityFrontend() {
|
|
|
40
44
|
},
|
|
41
45
|
},
|
|
42
46
|
{
|
|
43
|
-
// The user details page:
|
|
44
|
-
//
|
|
47
|
+
// The user details page, Tier-2: identity card + actions, a roles
|
|
48
|
+
// editor that knows the roles map (and warns that saving signs the
|
|
49
|
+
// user out), sessions, run stats — and the subscription panel when
|
|
50
|
+
// mod-billing is around (duck-typed off its customers route).
|
|
45
51
|
path: "/x/identity/users/:userId",
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
// Editing roles needs an input, which a row action can't carry —
|
|
50
|
-
// so it lives here as a form (the page's :userId fills the route).
|
|
51
|
-
title: "Set roles",
|
|
52
|
-
view: {
|
|
53
|
-
kind: "form",
|
|
54
|
-
schema: {
|
|
55
|
-
type: "object",
|
|
56
|
-
properties: {
|
|
57
|
-
roles: {
|
|
58
|
-
type: "string",
|
|
59
|
-
description: 'Comma-separated — REPLACES the current set, e.g. "admin" (empty = plain user). Ends the user\'s sessions.',
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
route: { method: "POST", path: PATHS.userSetRoles },
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
title: "Runs by workflow (recent window)",
|
|
68
|
-
view: {
|
|
69
|
-
kind: "table",
|
|
70
|
-
route: { method: "GET", path: PATHS.userRunStats },
|
|
71
|
-
columns: [
|
|
72
|
-
{ key: "workflow", label: "Workflow" },
|
|
73
|
-
{ key: "runs", label: "Runs" },
|
|
74
|
-
{ key: "errors", label: "Errors" },
|
|
75
|
-
{ key: "avg ms", label: "Avg ms" },
|
|
76
|
-
{ key: "last run", label: "Last run" },
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
title: "Sessions",
|
|
82
|
-
view: {
|
|
83
|
-
kind: "table",
|
|
84
|
-
route: { method: "GET", path: PATHS.userSessions },
|
|
85
|
-
columns: [
|
|
86
|
-
{ key: "status", label: "Status", format: "badge" },
|
|
87
|
-
{ key: "created", label: "Created", format: "date" },
|
|
88
|
-
{ key: "lastSeen", label: "Last seen", format: "date" },
|
|
89
|
-
{ key: "userAgent", label: "Device" },
|
|
90
|
-
],
|
|
91
|
-
rowActions: [
|
|
92
|
-
{ label: "Revoke", route: { method: "DELETE", path: PATHS.session }, args: { sessionId: "id" }, icon: "log-out", confirm: true },
|
|
93
|
-
],
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
],
|
|
52
|
+
title: "User",
|
|
53
|
+
module: USER_PAGE_REMOTE,
|
|
97
54
|
},
|
|
98
55
|
{
|
|
99
56
|
// Invites: send (form) + the sent list with statuses, one page.
|
|
@@ -198,10 +155,10 @@ export function identityFrontend() {
|
|
|
198
155
|
},
|
|
199
156
|
],
|
|
200
157
|
commands: [
|
|
201
|
-
{ id: "identity.invite", label: "Invite user…", group: "
|
|
158
|
+
{ id: "identity.invite", label: "Invite user…", group: "Administration", icon: "user-plus", path: "/x/identity/invite" },
|
|
202
159
|
{ id: "identity.whoami", label: "Who am I?", group: "Access", icon: "user", route: { method: "GET", path: PATHS.whoami } },
|
|
203
160
|
],
|
|
204
|
-
// Lives on the admin's Settings page (
|
|
161
|
+
// Lives on the admin's Settings page (Administration → Settings), with the other knobs.
|
|
205
162
|
settings: [
|
|
206
163
|
{
|
|
207
164
|
id: "identity",
|
package/dist/frontend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend.js","sourceRoot":"","sources":["../src/frontend.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"frontend.js","sourceRoot":"","sources":["../src/frontend.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { identityMod } from "./mod.js";
|
|
8
8
|
export { default } from "./mod.js";
|
|
9
|
-
export { resolveOptions, type IdentityOptions, type ResolvedIdentityOptions } from "./options.js";
|
|
10
|
-
export { DefaultIdentityService, API_TOKEN_PREFIX, API_TOKEN_SCOPES, type ApiTokenScope, type FindOrCreateInput, type IdentityService, type IssuedToken, type LoginMethod, type MintedApiToken, type MintedSession, type ResolvedSession, } from "./service.js";
|
|
9
|
+
export { resolveOptions, type IdentityOptions, type RateBudget, type ResolvedIdentityOptions } from "./options.js";
|
|
10
|
+
export { DefaultIdentityService, API_TOKEN_PREFIX, API_TOKEN_SCOPES, MAX_CODE_ATTEMPTS, type ApiTokenScope, type FindOrCreateInput, type IdentityService, type IssuedToken, type LoginMethod, type MintedApiToken, type MintedSession, type ResolvedSession, } from "./service.js";
|
|
11
11
|
export { identityService, IDENTITY_SERVICE } from "./service-key.js";
|
|
12
12
|
export { sessionAuthProvider, SESSION_PROVIDER_NAME } from "./auth-provider.js";
|
|
13
13
|
export { apiTokenAuthProvider, API_TOKEN_PROVIDER_NAME } from "./api-token-provider.js";
|
|
14
14
|
export { deliverToken, absoluteUrl, DELIVER_TOKEN_HOOK, type DeliverInput, type DeliverResult } from "./deliver.js";
|
|
15
15
|
export { parseCookies, serializeSessionCookie, clearSessionCookie } from "./cookies.js";
|
|
16
16
|
export { isCrossSiteWrite, isStateChanging } from "./csrf.js";
|
|
17
|
-
export { randomToken, sha256hex, normalizeEmail, looksLikeEmail } from "./tokens.js";
|
|
17
|
+
export { randomToken, randomCode, normalizeCode, formatCode, sha256hex, normalizeEmail, looksLikeEmail } from "./tokens.js";
|
|
18
18
|
export { renderLoginPage, renderSentPage } from "./pages/login.js";
|
|
19
19
|
export { layout, escapeHtml, safeNextPath } from "./pages/html.js";
|
|
20
20
|
export { memoryIdentityStores } from "./store/memory.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACnH,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACpH,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5H,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,SAAS,GACf,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
export { identityMod } from "./mod.js";
|
|
8
8
|
export { default } from "./mod.js";
|
|
9
9
|
export { resolveOptions } from "./options.js";
|
|
10
|
-
export { DefaultIdentityService, API_TOKEN_PREFIX, API_TOKEN_SCOPES, } from "./service.js";
|
|
10
|
+
export { DefaultIdentityService, API_TOKEN_PREFIX, API_TOKEN_SCOPES, MAX_CODE_ATTEMPTS, } from "./service.js";
|
|
11
11
|
export { identityService, IDENTITY_SERVICE } from "./service-key.js";
|
|
12
12
|
export { sessionAuthProvider, SESSION_PROVIDER_NAME } from "./auth-provider.js";
|
|
13
13
|
export { apiTokenAuthProvider, API_TOKEN_PROVIDER_NAME } from "./api-token-provider.js";
|
|
14
14
|
export { deliverToken, absoluteUrl, DELIVER_TOKEN_HOOK } from "./deliver.js";
|
|
15
15
|
export { parseCookies, serializeSessionCookie, clearSessionCookie } from "./cookies.js";
|
|
16
16
|
export { isCrossSiteWrite, isStateChanging } from "./csrf.js";
|
|
17
|
-
export { randomToken, sha256hex, normalizeEmail, looksLikeEmail } from "./tokens.js";
|
|
17
|
+
export { randomToken, randomCode, normalizeCode, formatCode, sha256hex, normalizeEmail, looksLikeEmail } from "./tokens.js";
|
|
18
18
|
export { renderLoginPage, renderSentPage } from "./pages/login.js";
|
|
19
19
|
export { layout, escapeHtml, safeNextPath } from "./pages/html.js";
|
|
20
20
|
export { memoryIdentityStores } from "./store/memory.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAuE,MAAM,cAAc,CAAC;AACnH,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,GASlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAyC,MAAM,cAAc,CAAC;AACpH,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5H,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,oBAAoB,GAWrB,MAAM,kBAAkB,CAAC"}
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAkF,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnI,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAwBpE,wBAAgB,WAAW,CAAC,OAAO,GAAE,eAAoB,GAAG,UAAU,CAsErE;AAED,+EAA+E;;AAC/E,wBAA6B"}
|
package/dist/mod.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { existsSync } from "node:fs";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { localFs, provideFilesystem } from "@pattern-js/runtime-node";
|
|
18
|
-
import { AUTH_LOGIN_URL, IDENTITY_SERVICE, defineMod, z } from "@pattern-js/core";
|
|
18
|
+
import { AUTH_LOGIN_URL, ENGINE_INTROSPECT, IDENTITY_SERVICE, defineMod, z } from "@pattern-js/core";
|
|
19
19
|
import { resolveOptions } from "./options.js";
|
|
20
20
|
import { DefaultIdentityService } from "./service.js";
|
|
21
21
|
import { sessionAuthProvider } from "./auth-provider.js";
|
|
@@ -50,7 +50,7 @@ export function identityMod(options = {}) {
|
|
|
50
50
|
ops: identityOps,
|
|
51
51
|
// Auth-page routes + the admin Access screens' dedicated routes (one
|
|
52
52
|
// purposeful endpoint per screen and action, replacing the invoke path).
|
|
53
|
-
workflows: [...endpointWorkflows(opts.mount), ...identityAdminRoutes()],
|
|
53
|
+
workflows: [...endpointWorkflows(opts.mount, opts.rateLimit.perClient), ...identityAdminRoutes()],
|
|
54
54
|
authProviders: [sessionAuthProvider(() => service), apiTokenAuthProvider(() => service)],
|
|
55
55
|
hooks: [
|
|
56
56
|
{
|
|
@@ -78,9 +78,16 @@ export function identityMod(options = {}) {
|
|
|
78
78
|
engine.provideService(IDENTITY_SERVICE, service);
|
|
79
79
|
engine.provideService(AUTH_LOGIN_URL, `${opts.mount}/login`);
|
|
80
80
|
},
|
|
81
|
-
ready: async () => {
|
|
81
|
+
ready: async (engine) => {
|
|
82
82
|
if (!service)
|
|
83
83
|
return;
|
|
84
|
+
// `pattern ops/validate/run`: an inspection, not a server; no link to mint.
|
|
85
|
+
if (engine.service(ENGINE_INTROSPECT))
|
|
86
|
+
return;
|
|
87
|
+
if (process.env.NODE_ENV === "production" && !process.env.PATTERN_PUBLIC_URL?.trim()) {
|
|
88
|
+
console.warn("[pattern] ⚠ NODE_ENV=production without PATTERN_PUBLIC_URL: sign-in and invite links will NOT be emailed " +
|
|
89
|
+
"(they would be built from the client-controlled Host header). Set PATTERN_PUBLIC_URL to your public origin.");
|
|
90
|
+
}
|
|
84
91
|
// Bootstrap-on-empty: the first admin signs up through a one-time link.
|
|
85
92
|
const count = await service.listUsers().then((u) => u.length);
|
|
86
93
|
if (count > 0)
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC,EAAgC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC,EAAgC,MAAM,kBAAkB,CAAC;AACnI,OAAO,EAAE,cAAc,EAAwB,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,2DAA2D;AAE3D,sFAAsF;AACtF,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA2B,EAAE;IACvD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAErC,4EAA4E;IAC5E,kEAAkE;IAClE,IAAI,OAA2C,CAAC;IAEhD,OAAO,SAAS,CAAC;QACf,IAAI,EAAE,0BAA0B;QAChC,IAAI,EAAE,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE;QACnE,GAAG,EAAE,WAAW;QAChB,qEAAqE;QACrE,yEAAyE;QACzE,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,GAAG,mBAAmB,EAAE,CAAC;QACjG,aAAa,EAAE,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC;QACxF,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,CAAC;qBACP,MAAM,CAAC;oBACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;oBACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;oBACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;oBACnB,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;oBACtB,6FAA6F;oBAC7F,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;oBACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;oBACnB,yDAAyD;oBACzD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;iBACjC,CAAC;qBACD,KAAK,EAAE;aACX;SACF;QACD,QAAQ,EAAE,gBAAgB,EAAE;QAC5B,KAAK,EAAE,KAAK,EAAE,MAAc,EAAE,EAAE;YAC9B,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,MAAM,GACV,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,MAAM,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChG,OAAO,GAAG,IAAI,sBAAsB,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YACvE,MAAM,CAAC,cAAc,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,CAAC,cAAc,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC;QAC/D,CAAC;QACD,KAAK,EAAE,KAAK,EAAE,MAAc,EAAE,EAAE;YAC9B,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,4EAA4E;YAC5E,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC;gBAAE,OAAO;YAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,EAAE,CAAC;gBACrF,OAAO,CAAC,IAAI,CACV,2GAA2G;oBACzG,6GAA6G,CAChH,CAAC;YACJ,CAAC;YACD,wEAAwE;YACxE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC9D,IAAI,KAAK,GAAG,CAAC;gBAAE,OAAO;YACtB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;gBACtC,OAAO,EAAE,WAAW;gBACpB,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;gBAC1B,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE;aACrC,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,gBAAgB,MAAM,CAAC,KAAK,EAAE,CAAC;YACzD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC7E,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,oBAAoB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;YAC1G,OAAO,CAAC,GAAG,CACT,2FAA2F;gBACzF,eAAe,KAAK,IAAI;gBACxB,sBAAsB,IAAI,oDAAoD,CACjF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,eAAe,WAAW,EAAE,CAAC"}
|
package/dist/ops.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../src/ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../src/ops.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAyE9F,6EAA6E;AAC7E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAM,CAAC;AA4pBxG,eAAO,MAAM,WAAW,EAAE,YAAY,EA4BrC,CAAC"}
|
package/dist/ops.js
CHANGED
|
@@ -243,6 +243,11 @@ const usersGet = jsonOp("identity.users.get", "One user's profile for the detail
|
|
|
243
243
|
created: new Date(user.createdAt).toLocaleString(),
|
|
244
244
|
"active sessions": active.length,
|
|
245
245
|
"user id": user.id,
|
|
246
|
+
// Raw fields for the Tier-2 user page (the display strings above stay
|
|
247
|
+
// for anything still rendering this as a plain detail view).
|
|
248
|
+
rolesList: user.roles,
|
|
249
|
+
knownRoles: Object.keys(svc.options.roles),
|
|
250
|
+
createdAt: user.createdAt,
|
|
246
251
|
};
|
|
247
252
|
}, { sensitivity: "privileged" });
|
|
248
253
|
const usersRunStats = jsonOp("identity.users.runStats", "Per-workflow run counts for a user, from the admin's retained run window (admin). Args { userId }.", { in: { userId: z.string() }, out: "stats" }, async (args, svc, ctx) => {
|
|
@@ -379,13 +384,40 @@ const apiTokensRevoke = jsonOp("identity.apiTokens.revoke", "Revoke an API token
|
|
|
379
384
|
/* ── auth pages & flows ────────────────────────────────────────────────── */
|
|
380
385
|
const loginPage = authOp("identity.login.page", "Render the login page from the registered login methods. Query { next?, error?, sent? }.", { query: ["next", "error", "sent"] }, (args, svc) => {
|
|
381
386
|
if (args.sent)
|
|
382
|
-
return page(renderSentPage(String(args.sent)
|
|
387
|
+
return page(renderSentPage(String(args.sent), {
|
|
388
|
+
action: `${svc.options.mount}/code`,
|
|
389
|
+
email: String(args.sent),
|
|
390
|
+
next: typeof args.next === "string" ? args.next : undefined,
|
|
391
|
+
}));
|
|
383
392
|
return page(renderLoginPage({
|
|
384
393
|
methods: svc.loginMethods(),
|
|
385
394
|
next: args.next,
|
|
386
395
|
error: args.error ? String(args.error) : undefined,
|
|
387
396
|
}));
|
|
388
397
|
});
|
|
398
|
+
/**
|
|
399
|
+
* The shared login tail (link AND code callbacks): a just-consumed token →
|
|
400
|
+
* the user, per the EFFECTIVE signup policy (admin-toggleable, not the
|
|
401
|
+
* construction-time option). Returns only vetted error codes.
|
|
402
|
+
*/
|
|
403
|
+
async function userFromToken(svc, token) {
|
|
404
|
+
if (!token.emailNorm)
|
|
405
|
+
return { error: "invalid-token" };
|
|
406
|
+
const data = token.data ?? {};
|
|
407
|
+
const invite = token.purpose === "invite";
|
|
408
|
+
const user = await svc.findOrCreateByIdentity({
|
|
409
|
+
provider: typeof data.provider === "string" ? data.provider : "magic-link",
|
|
410
|
+
subject: token.emailNorm,
|
|
411
|
+
email: token.emailNorm,
|
|
412
|
+
allowCreate: invite || (await svc.getSignup()) === "open",
|
|
413
|
+
roles: invite && Array.isArray(data.roles) ? data.roles : undefined,
|
|
414
|
+
});
|
|
415
|
+
if (!user)
|
|
416
|
+
return { error: "signup-closed" };
|
|
417
|
+
if (user.disabled)
|
|
418
|
+
return { error: "account-disabled" };
|
|
419
|
+
return { user };
|
|
420
|
+
}
|
|
389
421
|
const tokenCallback = authOp("identity.token.callback", "The login/invite callback: consumes a single-use token and finds-or-creates the user per the signup policy. A login mints a session and redirects with the cookie set; an invite acceptance creates the account and lands on the /invited page — the first sign-in stays an explicit act.", { query: ["t", "next"], userAgent: true }, async (args, svc, req) => {
|
|
390
422
|
const loginUrl = `${svc.options.mount}/login`;
|
|
391
423
|
const raw = String(args.t ?? "");
|
|
@@ -404,18 +436,10 @@ const tokenCallback = authOp("identity.token.callback", "The login/invite callba
|
|
|
404
436
|
if (invite && inviteRecord?.revokedAt != null) {
|
|
405
437
|
return redirectTo(`${loginUrl}?error=invite-revoked`);
|
|
406
438
|
}
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
// The EFFECTIVE policy (admin-toggleable), not the construction-time option.
|
|
412
|
-
allowCreate: invite || (await svc.getSignup()) === "open",
|
|
413
|
-
roles: invite && Array.isArray(data.roles) ? data.roles : undefined,
|
|
414
|
-
});
|
|
415
|
-
if (!user)
|
|
416
|
-
return redirectTo(`${loginUrl}?error=signup-closed`);
|
|
417
|
-
if (user.disabled)
|
|
418
|
-
return redirectTo(`${loginUrl}?error=account-disabled`);
|
|
439
|
+
const resolved = await userFromToken(svc, token);
|
|
440
|
+
if (resolved.error || !resolved.user)
|
|
441
|
+
return redirectTo(`${loginUrl}?error=${resolved.error}`);
|
|
442
|
+
const user = resolved.user;
|
|
419
443
|
const next = safeNextPath(args.next ?? data.next ?? req.home);
|
|
420
444
|
if (invite) {
|
|
421
445
|
// Acceptance ≠ sign-in: stamp the record, then explain what happened and
|
|
@@ -427,6 +451,28 @@ const tokenCallback = authOp("identity.token.callback", "The login/invite callba
|
|
|
427
451
|
const minted = await svc.mintSession(user.id, { userAgent: args.userAgent ?? null });
|
|
428
452
|
return redirectTo(next, sessionCookie(svc, minted.token));
|
|
429
453
|
});
|
|
454
|
+
const codeCallback = authOp("identity.code.callback", "The sign-in code callback: consumes a code-bearing login token by { email, code } and mints the session in the browsing context that POSTED — the PWA/standalone path, where the emailed link would land its cookie in the system browser's jar instead of the installed app's. Wrong guesses re-render the sent page identically for unknown emails and wrong codes (no enumeration) with status 401; the token's wrong-guess budget burns it, link included.", { body: ["email", "code", "next"], userAgent: true }, async (args, svc, req) => {
|
|
455
|
+
const email = String(args.email ?? "").trim();
|
|
456
|
+
const next = safeNextPath(args.next);
|
|
457
|
+
const token = looksLikeEmail(email)
|
|
458
|
+
? await svc.consumeTokenByCode(email, String(args.code ?? ""), "login")
|
|
459
|
+
: null;
|
|
460
|
+
if (!token) {
|
|
461
|
+
// One page for wrong code / unknown email / expired / burned — nothing
|
|
462
|
+
// to enumerate. The 401 makes the outcome machine-readable for SPAs.
|
|
463
|
+
return page(renderSentPage(looksLikeEmail(email) ? email : "that address", {
|
|
464
|
+
action: `${svc.options.mount}/code`,
|
|
465
|
+
email,
|
|
466
|
+
next,
|
|
467
|
+
error: "invalid-code",
|
|
468
|
+
}), 401);
|
|
469
|
+
}
|
|
470
|
+
const resolved = await userFromToken(svc, token);
|
|
471
|
+
if (resolved.error || !resolved.user)
|
|
472
|
+
return redirectTo(`${svc.options.mount}/login?error=${resolved.error}`);
|
|
473
|
+
const minted = await svc.mintSession(resolved.user.id, { userAgent: args.userAgent ?? null });
|
|
474
|
+
return redirectTo(safeNextPath(args.next ?? token.data?.next ?? req.home), sessionCookie(svc, minted.token));
|
|
475
|
+
});
|
|
430
476
|
const invitedPage = authOp("identity.invited.page", "The invite-accepted interstitial: your account is ready, sign in for the first time — with a continue button carrying the invite's next path into the login page.", { query: ["next"] }, (args, svc) => page(renderInvitedPage({ mount: svc.options.mount, next: safeNextPath(args.next) })));
|
|
431
477
|
const logout = authOp("identity.logout", "Revoke the current session and clear the cookie. POST (same-origin; the CSRF guard makes forged logouts inert).", {}, async (_args, svc, req) => {
|
|
432
478
|
const sessionId = req.principal.kind === "user" ? req.principal.claims?.sessionId : undefined;
|
|
@@ -495,10 +541,37 @@ export const identityOps = [
|
|
|
495
541
|
apiTokensRevoke,
|
|
496
542
|
loginPage,
|
|
497
543
|
tokenCallback,
|
|
544
|
+
codeCallback,
|
|
498
545
|
logout,
|
|
499
546
|
welcomePage,
|
|
500
547
|
invitedPage,
|
|
501
548
|
bootstrapPage,
|
|
502
549
|
bootstrapSubmit,
|
|
503
550
|
];
|
|
551
|
+
// Replay-safety stamps for the helper-built ops above (absent ⇒ "external").
|
|
552
|
+
// Reads are pure; CAS-guarded writes converge on repeat. Deliberately NOT
|
|
553
|
+
// stamped: invite/loginLink/apiTokens.create (each call mints or sends).
|
|
554
|
+
const OP_EFFECTS = {
|
|
555
|
+
"identity.whoami": "pure",
|
|
556
|
+
"identity.users.list": "pure",
|
|
557
|
+
"identity.users.get": "pure",
|
|
558
|
+
"identity.users.runStats": "pure",
|
|
559
|
+
"identity.invites.list": "pure",
|
|
560
|
+
"identity.settings.get": "pure",
|
|
561
|
+
"identity.sessions.list": "pure",
|
|
562
|
+
"identity.apiTokens.list": "pure",
|
|
563
|
+
"identity.users.setRoles": "idempotent",
|
|
564
|
+
"identity.users.toggleDisabled": "idempotent",
|
|
565
|
+
"identity.users.revokeSessions": "idempotent",
|
|
566
|
+
"identity.users.delete": "idempotent",
|
|
567
|
+
"identity.invites.revoke": "idempotent",
|
|
568
|
+
"identity.settings.set": "idempotent",
|
|
569
|
+
"identity.sessions.revoke": "idempotent",
|
|
570
|
+
"identity.apiTokens.revoke": "idempotent",
|
|
571
|
+
};
|
|
572
|
+
for (const op of identityOps) {
|
|
573
|
+
const e = OP_EFFECTS[op.type];
|
|
574
|
+
if (e)
|
|
575
|
+
op.effects = e;
|
|
576
|
+
}
|
|
504
577
|
//# sourceMappingURL=ops.js.map
|