@parlor/auth 0.0.0-stage → 0.1.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -2
  3. package/dist/account.d.ts +52 -0
  4. package/dist/account.d.ts.map +1 -0
  5. package/dist/account.js +114 -0
  6. package/dist/account.js.map +1 -0
  7. package/dist/audit.d.ts +10 -0
  8. package/dist/audit.d.ts.map +1 -0
  9. package/dist/audit.js +130 -0
  10. package/dist/audit.js.map +1 -0
  11. package/dist/better-auth.d.ts +41 -0
  12. package/dist/better-auth.d.ts.map +1 -0
  13. package/dist/better-auth.js +118 -0
  14. package/dist/better-auth.js.map +1 -0
  15. package/dist/client/index.d.ts +43 -0
  16. package/dist/client/index.d.ts.map +1 -0
  17. package/dist/client/index.js +465 -0
  18. package/dist/client/index.js.map +1 -0
  19. package/dist/database.d.ts +7 -0
  20. package/dist/database.d.ts.map +1 -0
  21. package/dist/database.js +11 -0
  22. package/dist/database.js.map +1 -0
  23. package/dist/devices.d.ts +157 -0
  24. package/dist/devices.d.ts.map +1 -0
  25. package/dist/devices.js +398 -0
  26. package/dist/devices.js.map +1 -0
  27. package/dist/es256.d.ts +4 -0
  28. package/dist/es256.d.ts.map +1 -0
  29. package/dist/es256.js +44 -0
  30. package/dist/es256.js.map +1 -0
  31. package/dist/grants.d.ts +4 -0
  32. package/dist/grants.d.ts.map +1 -0
  33. package/dist/grants.js +6 -0
  34. package/dist/grants.js.map +1 -0
  35. package/dist/hosts.d.ts +116 -0
  36. package/dist/hosts.d.ts.map +1 -0
  37. package/dist/hosts.js +530 -0
  38. package/dist/hosts.js.map +1 -0
  39. package/dist/index.d.ts +9 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +9 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/migrations.d.ts +3 -0
  44. package/dist/migrations.d.ts.map +1 -0
  45. package/dist/migrations.js +17 -0
  46. package/dist/migrations.js.map +1 -0
  47. package/dist/pages.d.ts +4 -0
  48. package/dist/pages.d.ts.map +1 -0
  49. package/dist/pages.js +53 -0
  50. package/dist/pages.js.map +1 -0
  51. package/dist/provider.d.ts +74 -0
  52. package/dist/provider.d.ts.map +1 -0
  53. package/dist/provider.js +286 -0
  54. package/dist/provider.js.map +1 -0
  55. package/dist/revocation.d.ts +51 -0
  56. package/dist/revocation.d.ts.map +1 -0
  57. package/dist/revocation.js +233 -0
  58. package/dist/revocation.js.map +1 -0
  59. package/dist/routes.d.ts +15 -0
  60. package/dist/routes.d.ts.map +1 -0
  61. package/dist/routes.js +116 -0
  62. package/dist/routes.js.map +1 -0
  63. package/dist/schema.d.ts +2970 -0
  64. package/dist/schema.d.ts.map +1 -0
  65. package/dist/schema.js +308 -0
  66. package/dist/schema.js.map +1 -0
  67. package/dist/statements.d.ts +92 -0
  68. package/dist/statements.d.ts.map +1 -0
  69. package/dist/statements.js +199 -0
  70. package/dist/statements.js.map +1 -0
  71. package/dist/tokens.d.ts +69 -0
  72. package/dist/tokens.d.ts.map +1 -0
  73. package/dist/tokens.js +134 -0
  74. package/dist/tokens.js.map +1 -0
  75. package/dist/turn-keys.d.ts +16 -0
  76. package/dist/turn-keys.d.ts.map +1 -0
  77. package/dist/turn-keys.js +108 -0
  78. package/dist/turn-keys.js.map +1 -0
  79. package/package.json +57 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cychien
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,26 @@
1
- # Temporary Holding Version
1
+ # @parlor/auth
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Sign-in for Parlor apps on Better Auth: sessions, per-user scopes, OAuth for MCP clients, and pairing a computer's agents with an account.
4
+
5
+ ```sh
6
+ pnpm parlor add auth
7
+ ```
8
+
9
+ `parlor add auth` installs this package and writes the wiring. The server side it writes:
10
+
11
+ ```ts
12
+ import { betterAuthProvider, parlorAuth } from "@parlor/auth";
13
+ import { betterAuth } from "better-auth";
14
+
15
+ export const auth = betterAuthProvider({
16
+ instance: betterAuth(
17
+ parlorAuth({
18
+ appName: "Todos",
19
+ signInPage: "/sign-in",
20
+ emailAndPassword: { enabled: true },
21
+ }),
22
+ ),
23
+ });
24
+ ```
25
+
26
+ Docs: https://cychien.github.io/parlor/guides/auth/
@@ -0,0 +1,52 @@
1
+ import { type ActionContext, type UserPrincipal } from "@parlor/core";
2
+ import { type ActionsOf } from "@parlor/core/registry";
3
+ import type { PluginHost } from "@parlor/server";
4
+ import { z } from "zod";
5
+ import type { DeviceAccountActions } from "./devices.js";
6
+ import type { HostAccountActions } from "./hosts.js";
7
+ import type { BetterAuthInstance } from "./provider.js";
8
+ import type { RevocationNotice } from "./revocation.js";
9
+ export declare const SESSIONS_TAG = "parlor_auth_sessions";
10
+ export declare const AGENTS_TAG = "parlor_auth_agents";
11
+ export interface SignedInSession {
12
+ readonly id: string;
13
+ readonly current: boolean;
14
+ readonly userAgent: string | null;
15
+ readonly ipAddress: string | null;
16
+ readonly createdAt: string;
17
+ readonly lastActiveAt: string;
18
+ }
19
+ export interface ConnectedAgent {
20
+ readonly clientId: string;
21
+ readonly name: string;
22
+ readonly scopes: readonly string[];
23
+ readonly connectedAt: string;
24
+ }
25
+ export type AccountActions = ActionsOf<ReturnType<typeof accountActions>> & HostAccountActions & DeviceAccountActions;
26
+ interface AccountDeps {
27
+ readonly instance: BetterAuthInstance;
28
+ readonly host: PluginHost;
29
+ readonly revoke: (notice: RevocationNotice) => Promise<void>;
30
+ }
31
+ export declare function signedIn(ctx: ActionContext): UserPrincipal;
32
+ /**
33
+ * Plugin actions at `/_parlor/auth/actions/<name>`. Better Auth writes through its own
34
+ * connection, so these never run inside an action transaction (on PGlite that would
35
+ * deadlock) and announce their changes afterwards instead of declaring `writes`.
36
+ */
37
+ export declare function accountActions({ instance, host, revoke }: AccountDeps): import("@parlor/core/registry").ActionRegistry<{
38
+ sessions: import("@parlor/core").ActionDefinition<z.ZodObject<{}, z.core.$strip>, SignedInSession[]>;
39
+ revokeSession: import("@parlor/core").ActionDefinition<z.ZodObject<{
40
+ id: z.ZodString;
41
+ }, z.core.$strip>, {
42
+ id: string;
43
+ }>;
44
+ agents: import("@parlor/core").ActionDefinition<z.ZodObject<{}, z.core.$strip>, ConnectedAgent[]>;
45
+ revokeAgent: import("@parlor/core").ActionDefinition<z.ZodObject<{
46
+ clientId: z.ZodString;
47
+ }, z.core.$strip>, {
48
+ id: string;
49
+ }>;
50
+ }>;
51
+ export {};
52
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,eAAO,MAAM,YAAY,yBAAyB,CAAC;AACnD,eAAO,MAAM,UAAU,uBAAuB,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAaD,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,GACvE,kBAAkB,GAClB,oBAAoB,CAAC;AAEvB,UAAU,WAAW;IACnB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,CAK1D;AAKD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW;;;;;;;;;;;;;GAiGrE"}
@@ -0,0 +1,114 @@
1
+ import { ActionError, defineAction, } from "@parlor/core";
2
+ import { createRegistry } from "@parlor/core/registry";
3
+ import { z } from "zod";
4
+ import { grantIdOf } from "./grants.js";
5
+ export const SESSIONS_TAG = "parlor_auth_sessions";
6
+ export const AGENTS_TAG = "parlor_auth_agents";
7
+ export function signedIn(ctx) {
8
+ if (ctx.principal.kind !== "user") {
9
+ throw new ActionError("forbidden", "Only a signed-in user manages the account.");
10
+ }
11
+ return ctx.principal;
12
+ }
13
+ const scopesOf = (scopes) => typeof scopes === "string" ? scopes.split(" ").filter(Boolean) : [...scopes];
14
+ /**
15
+ * Plugin actions at `/_parlor/auth/actions/<name>`. Better Auth writes through its own
16
+ * connection, so these never run inside an action transaction (on PGlite that would
17
+ * deadlock) and announce their changes afterwards instead of declaring `writes`.
18
+ */
19
+ export function accountActions({ instance, host, revoke }) {
20
+ return createRegistry({
21
+ sessions: defineAction({
22
+ description: "List the places this account is signed in.",
23
+ input: z.object({}),
24
+ reads: [SESSIONS_TAG],
25
+ run: async (_input, ctx) => {
26
+ const user = signedIn(ctx);
27
+ const { internalAdapter } = await instance.$context;
28
+ const sessions = await internalAdapter.listSessions(user.userId);
29
+ return sessions
30
+ .filter((session) => session.expiresAt.getTime() > Date.now())
31
+ .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
32
+ .map((session) => ({
33
+ id: session.id,
34
+ current: session.id === user.sessionId,
35
+ userAgent: session.userAgent ?? null,
36
+ ipAddress: session.ipAddress ?? null,
37
+ createdAt: session.createdAt.toISOString(),
38
+ lastActiveAt: session.updatedAt.toISOString(),
39
+ }));
40
+ },
41
+ }),
42
+ revokeSession: defineAction({
43
+ description: "Sign this account out of one place; its open connections close.",
44
+ input: z.object({ id: z.string().min(1) }),
45
+ run: async ({ id }, ctx) => {
46
+ const user = signedIn(ctx);
47
+ const { internalAdapter } = await instance.$context;
48
+ const sessions = await internalAdapter.listSessions(user.userId);
49
+ const session = sessions.find((candidate) => candidate.id === id);
50
+ if (!session)
51
+ throw new ActionError("not_found", `No session ${id}`);
52
+ await internalAdapter.deleteSession(session.token);
53
+ await host.announce(ctx.scope, [[SESSIONS_TAG, id]]);
54
+ return { id };
55
+ },
56
+ }),
57
+ agents: defineAction({
58
+ description: "List the agents this account authorized through MCP.",
59
+ input: z.object({}),
60
+ reads: [AGENTS_TAG],
61
+ run: async (_input, ctx) => {
62
+ const user = signedIn(ctx);
63
+ const { adapter } = await instance.$context;
64
+ const mine = [{ field: "userId", value: user.userId }];
65
+ const [consents, refreshTokens] = await Promise.all([
66
+ adapter.findMany({ model: "oauthConsent", where: mine }),
67
+ adapter.findMany({
68
+ model: "oauthRefreshToken",
69
+ where: mine,
70
+ }),
71
+ ]);
72
+ // A trusted client skips consent, so its live refresh tokens are its only trace.
73
+ const live = refreshTokens.filter((token) => !token.revoked && token.expiresAt.getTime() > Date.now());
74
+ const grants = new Map();
75
+ for (const grant of [...live, ...consents]) {
76
+ const known = grants.get(grant.clientId);
77
+ if (!known || grant.createdAt < known.createdAt)
78
+ grants.set(grant.clientId, grant);
79
+ }
80
+ return Promise.all([...grants.values()].map(async (grant) => {
81
+ const client = await adapter.findOne({
82
+ model: "oauthClient",
83
+ where: [{ field: "clientId", value: grant.clientId }],
84
+ });
85
+ return {
86
+ clientId: grant.clientId,
87
+ name: client?.name || grant.clientId,
88
+ scopes: scopesOf(grant.scopes),
89
+ connectedAt: grant.createdAt.toISOString(),
90
+ };
91
+ }));
92
+ },
93
+ }),
94
+ revokeAgent: defineAction({
95
+ description: "Revoke an agent's access; its tokens stop working at once.",
96
+ input: z.object({ clientId: z.string().min(1) }),
97
+ run: async ({ clientId }, ctx) => {
98
+ const user = signedIn(ctx);
99
+ await revoke({ kind: "grant", id: grantIdOf(user.userId, clientId) });
100
+ const { adapter } = await instance.$context;
101
+ const where = [
102
+ { field: "userId", value: user.userId },
103
+ { field: "clientId", value: clientId },
104
+ ];
105
+ for (const model of ["oauthConsent", "oauthRefreshToken", "oauthAccessToken"]) {
106
+ await adapter.deleteMany({ model, where });
107
+ }
108
+ await host.announce(ctx.scope, [[AGENTS_TAG, clientId]]);
109
+ return { id: clientId };
110
+ },
111
+ }),
112
+ });
113
+ }
114
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../src/account.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { DataProvider } from "@parlor/core";
2
+ import type { ActionAudit, Migration } from "@parlor/server";
3
+ export declare const AUDIT_MIGRATION: Migration;
4
+ export interface AuditLog {
5
+ record(audit: ActionAudit): void;
6
+ flush(): Promise<void>;
7
+ stop(): Promise<void>;
8
+ }
9
+ export declare function createAuditLog(database: DataProvider): AuditLog;
10
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAK7D,eAAO,MAAM,eAAe,EAAE,SAiC7B,CAAC;AA+BF,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,QAAQ,CAkE/D"}
package/dist/audit.js ADDED
@@ -0,0 +1,130 @@
1
+ import { sql } from "drizzle-orm";
2
+ export const AUDIT_MIGRATION = {
3
+ id: "auth/0006_audit",
4
+ sql: `
5
+ create table parlor_auth_audit (
6
+ id bigint generated always as identity primary key,
7
+ at timestamptz not null,
8
+ action text not null,
9
+ actor_kind text not null,
10
+ user_id text,
11
+ grant_id text,
12
+ session_id text,
13
+ device_id text,
14
+ host_id text,
15
+ agent_name text,
16
+ agent_verified boolean,
17
+ thread_id text,
18
+ turn_id text,
19
+ source text not null,
20
+ scope text not null,
21
+ input_digest text not null,
22
+ stage text not null,
23
+ outcome text not null,
24
+ confirmation text
25
+ );
26
+ create index parlor_auth_audit_by_user on parlor_auth_audit (user_id, at);
27
+ create function parlor_auth_audit_append_only() returns trigger language plpgsql as $$
28
+ begin
29
+ raise exception 'parlor_auth_audit is append-only';
30
+ end
31
+ $$;
32
+ create trigger parlor_auth_audit_append_only
33
+ before update or delete on parlor_auth_audit
34
+ for each row execute function parlor_auth_audit_append_only()`,
35
+ };
36
+ const FLUSH_MS = 250;
37
+ const FLUSH_AT = 100;
38
+ const KEEP_AT_MOST = 10_000;
39
+ function audited({ principal, stage, confirmation }) {
40
+ return principal.kind === "agent" || stage === "refused" || confirmation !== undefined;
41
+ }
42
+ function rowOf(audit) {
43
+ const { principal } = audit;
44
+ const user = principal.kind === "anonymous" ? undefined : principal.userId;
45
+ const grant = principal.kind === "agent" || principal.kind === "host"
46
+ ? principal.grantId
47
+ : undefined;
48
+ const turn = principal.kind === "agent" ? principal.turn : undefined;
49
+ const host = principal.kind === "host" ? principal.hostId : turn?.hostId;
50
+ return sql `(${new Date(audit.at).toISOString()}, ${audit.action}, ${principal.kind},
51
+ ${user ?? null}, ${grant ?? null},
52
+ ${principal.kind === "user" ? principal.sessionId : null},
53
+ ${principal.kind === "user" ? (principal.deviceId ?? null) : null},
54
+ ${host ?? null},
55
+ ${principal.kind === "agent" ? principal.agent.name : null},
56
+ ${principal.kind === "agent" ? principal.agent.verified : null},
57
+ ${turn?.threadId ?? null}, ${turn?.turnId ?? null},
58
+ ${audit.source}, ${audit.scope}, ${audit.inputDigest}, ${audit.stage}, ${audit.outcome},
59
+ ${audit.confirmation ?? null})`;
60
+ }
61
+ export function createAuditLog(database) {
62
+ let queued = [];
63
+ let timer;
64
+ let writing = Promise.resolve();
65
+ let stopped = false;
66
+ const write = async () => {
67
+ if (queued.length === 0)
68
+ return;
69
+ const batch = queued;
70
+ queued = [];
71
+ try {
72
+ await database.transaction(async (tx) => {
73
+ await tx.execute(sql `
74
+ insert into parlor_auth_audit
75
+ (at, action, actor_kind, user_id, grant_id, session_id, device_id, host_id,
76
+ agent_name, agent_verified, thread_id, turn_id, source, scope, input_digest,
77
+ stage, outcome, confirmation)
78
+ values ${sql.join(batch.map(rowOf), sql `, `)}`);
79
+ });
80
+ }
81
+ catch (error) {
82
+ queued = [...batch, ...queued].slice(-KEEP_AT_MOST);
83
+ throw error;
84
+ }
85
+ };
86
+ const flush = () => {
87
+ if (timer !== undefined)
88
+ clearTimeout(timer);
89
+ timer = undefined;
90
+ const next = writing.then(write, write);
91
+ writing = next.catch(() => { });
92
+ return next;
93
+ };
94
+ const schedule = () => {
95
+ if (timer !== undefined || stopped)
96
+ return;
97
+ timer = setTimeout(() => {
98
+ timer = undefined;
99
+ flush().catch((error) => {
100
+ console.error("[parlor] audit write failed, will retry:", error);
101
+ schedule();
102
+ });
103
+ }, FLUSH_MS);
104
+ timer.unref?.();
105
+ };
106
+ return {
107
+ record(audit) {
108
+ if (!audited(audit))
109
+ return;
110
+ queued.push(audit);
111
+ if (queued.length >= FLUSH_AT) {
112
+ flush().catch((error) => {
113
+ console.error("[parlor] audit write failed, will retry:", error);
114
+ schedule();
115
+ });
116
+ }
117
+ else {
118
+ schedule();
119
+ }
120
+ },
121
+ flush,
122
+ async stop() {
123
+ stopped = true;
124
+ await flush().catch((error) => {
125
+ console.error("[parlor] audit write failed on shutdown:", error);
126
+ });
127
+ },
128
+ };
129
+ }
130
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { BetterAuthOptions } from "better-auth";
2
+ import type { RevocationNotice } from "./revocation.js";
3
+ export declare const AUTH_PLUGIN = "auth";
4
+ export declare const AUTH_BASE_PATH = "/_parlor/auth";
5
+ export declare const MCP_SCOPE = "mcp";
6
+ export declare const CONSENT_PATH = "/consent";
7
+ export declare const ACCESS_TOKEN_TTL_S: number;
8
+ export interface ParlorAuthOptions extends Omit<BetterAuthOptions, "basePath" | "baseURL"> {
9
+ /**
10
+ * The app's public origin, which serves its pages and `/_parlor`. Defaults to
11
+ * `PARLOR_ORIGIN`, then to the web origin `parlor dev` sets.
12
+ */
13
+ readonly origin?: string | undefined;
14
+ /**
15
+ * The app page that signs a user in. An MCP client's authorization lands there and
16
+ * resumes once the user signs in through `@parlor/auth/client`.
17
+ */
18
+ readonly signInPage: string;
19
+ }
20
+ export interface ParlorAuthSettings {
21
+ readonly appName?: string | undefined;
22
+ readonly origin: string;
23
+ readonly baseURL: string;
24
+ readonly resource: string;
25
+ }
26
+ interface Bridge {
27
+ settings: ParlorAuthSettings;
28
+ revoke?: ((notice: RevocationNotice) => Promise<void>) | undefined;
29
+ }
30
+ export declare function bridgeOf(options: BetterAuthOptions): Bridge;
31
+ /** The other frontends one `parlor dev` serves next to the web target, each on its own port. */
32
+ export declare function devOrigins(env?: NodeJS.ProcessEnv): readonly string[];
33
+ /**
34
+ * The Better Auth options Parlor needs, merged into the app's own: sessions as a cookie
35
+ * on the web and a bearer token in native shells, and an OAuth authorization server for
36
+ * MCP clients (MCP 2026-07-28: protected resource metadata, Client ID Metadata Documents,
37
+ * PKCE, tokens bound to `/_parlor/mcp`).
38
+ */
39
+ export declare function parlorAuth(options: ParlorAuthOptions): BetterAuthOptions;
40
+ export {};
41
+ //# sourceMappingURL=better-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"better-auth.d.ts","sourceRoot":"","sources":["../src/better-auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,aAAa,CAAC;AAGvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,eAAO,MAAM,WAAW,SAAS,CAAC;AAClC,eAAO,MAAM,cAAc,kBAAwC,CAAC;AACpE,eAAO,MAAM,SAAS,QAAQ,CAAC;AAC/B,eAAO,MAAM,YAAY,aAAa,CAAC;AAGvC,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAa3E,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,iBAAiB,EACjB,UAAU,GAAG,SAAS,CACvB;IACC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,UAAU,MAAM;IACd,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CACpE;AA8BD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAQ3D;AAED,gGAAgG;AAChG,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,MAAM,EAAE,CAMlF;AAWD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CA4BxE"}
@@ -0,0 +1,118 @@
1
+ import { cimd } from "@better-auth/cimd";
2
+ import { fetchClientMetadataResource } from "@better-auth/cimd/node";
3
+ import { mcp } from "@better-auth/mcp";
4
+ import { DEFAULT_BASE_PATH, REVOCATION_HORIZON_MS, SHELL_ORIGINS } from "@parlor/server";
5
+ import { bearer, jwt } from "better-auth/plugins";
6
+ export const AUTH_PLUGIN = "auth";
7
+ export const AUTH_BASE_PATH = `${DEFAULT_BASE_PATH}/${AUTH_PLUGIN}`;
8
+ export const MCP_SCOPE = "mcp";
9
+ export const CONSENT_PATH = "/consent";
10
+ /** Clock skew a revocation must outlast beyond a credential's own lifetime. */
11
+ const SKEW_MS = 60_000;
12
+ export const ACCESS_TOKEN_TTL_S = (REVOCATION_HORIZON_MS - SKEW_MS) / 1000;
13
+ /** Better Auth endpoints that would let a user bypass Parlor's own account actions. */
14
+ const DISABLED_PATHS = [
15
+ "/oauth2/create-client",
16
+ "/oauth2/update-client",
17
+ "/oauth2/delete-client",
18
+ "/oauth2/client/rotate-secret",
19
+ "/oauth2/get-clients",
20
+ "/oauth2/delete-consent",
21
+ "/oauth2/update-consent",
22
+ ];
23
+ const BRIDGE_PLUGIN = "parlor";
24
+ function bridgePlugin(bridge) {
25
+ const ended = async (notice) => {
26
+ if (!bridge.revoke) {
27
+ throw new Error("Install betterAuthProvider() in createServer({ auth }).");
28
+ }
29
+ await bridge.revoke(notice);
30
+ };
31
+ return {
32
+ id: BRIDGE_PLUGIN,
33
+ bridge,
34
+ init: () => ({
35
+ options: {
36
+ databaseHooks: {
37
+ session: {
38
+ delete: { after: (session) => ended({ kind: "session", id: session.id }) },
39
+ },
40
+ },
41
+ },
42
+ }),
43
+ };
44
+ }
45
+ export function bridgeOf(options) {
46
+ const plugin = options.plugins?.find((candidate) => candidate.id === BRIDGE_PLUGIN);
47
+ if (!plugin) {
48
+ throw new Error("Pass the Better Auth options through parlorAuth() before betterAuth().");
49
+ }
50
+ return plugin.bridge;
51
+ }
52
+ /** The other frontends one `parlor dev` serves next to the web target, each on its own port. */
53
+ export function devOrigins(env = process.env) {
54
+ if (env.PARLOR_DEV !== "1")
55
+ return [];
56
+ return (env.PARLOR_DEV_ORIGINS ?? "")
57
+ .split(",")
58
+ .map((origin) => origin.trim())
59
+ .filter((origin) => /^https?:\/\//.test(origin));
60
+ }
61
+ function originOf(options) {
62
+ const origin = options.origin ?? process.env.PARLOR_ORIGIN ?? process.env.PARLOR_WEB_ORIGIN;
63
+ if (!origin) {
64
+ throw new Error("Set PARLOR_ORIGIN to the app's public origin, or pass `origin`.");
65
+ }
66
+ return new URL(origin).origin;
67
+ }
68
+ /**
69
+ * The Better Auth options Parlor needs, merged into the app's own: sessions as a cookie
70
+ * on the web and a bearer token in native shells, and an OAuth authorization server for
71
+ * MCP clients (MCP 2026-07-28: protected resource metadata, Client ID Metadata Documents,
72
+ * PKCE, tokens bound to `/_parlor/mcp`).
73
+ */
74
+ export function parlorAuth(options) {
75
+ const { origin: _origin, signInPage, ...rest } = options;
76
+ const origin = originOf(options);
77
+ const baseURL = `${origin}${AUTH_BASE_PATH}`;
78
+ const resource = `${origin}${DEFAULT_BASE_PATH}/mcp`;
79
+ const bridge = {
80
+ settings: { appName: rest.appName, origin, baseURL, resource },
81
+ };
82
+ const trusted = rest.trustedOrigins;
83
+ return {
84
+ ...rest,
85
+ baseURL,
86
+ basePath: AUTH_BASE_PATH,
87
+ trustedOrigins: Array.isArray(trusted)
88
+ ? [origin, ...SHELL_ORIGINS, ...devOrigins(), ...trusted]
89
+ : async (request) => [
90
+ origin,
91
+ ...SHELL_ORIGINS,
92
+ ...devOrigins(),
93
+ ...(typeof trusted === "function" ? await trusted(request) : []),
94
+ ],
95
+ disabledPaths: [...DISABLED_PATHS, ...(rest.disabledPaths ?? [])],
96
+ plugins: [
97
+ ...(rest.plugins ?? []),
98
+ ...frameworkPlugins(resource, signInPage),
99
+ bridgePlugin(bridge),
100
+ ],
101
+ };
102
+ }
103
+ function frameworkPlugins(resource, signInPage) {
104
+ // Better Auth's plugin types are not written for exactOptionalPropertyTypes.
105
+ return [
106
+ bearer(),
107
+ jwt(),
108
+ mcp({
109
+ resource,
110
+ loginPage: signInPage,
111
+ consentPage: `${AUTH_BASE_PATH}${CONSENT_PATH}`,
112
+ scopes: [MCP_SCOPE, "offline_access"],
113
+ accessTokenExpiresIn: ACCESS_TOKEN_TTL_S,
114
+ }),
115
+ cimd({ fetchClientMetadataResource, metadataProfile: "mcp-2026-07-28" }),
116
+ ];
117
+ }
118
+ //# sourceMappingURL=better-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"better-auth.js","sourceRoot":"","sources":["../src/better-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { type DeviceKey, type SessionStore } from "@parlor/capabilities";
2
+ import { type ActionClient, type DeviceLinker, type DeviceSource, type HostAuthorizer, type SessionDevice, type SessionSource } from "@parlor/client";
3
+ import type { AccountActions } from "../account.js";
4
+ export interface AuthClientOptions {
5
+ /** The backend origin; defaults to the shell's `server.url`, or this page's origin. */
6
+ readonly baseUrl?: string | undefined;
7
+ readonly basePath?: string | undefined;
8
+ /**
9
+ * Where a bearer session is kept. Defaults to the `sessionStore` capability: native
10
+ * shells keep a bearer token, the web keeps a cookie.
11
+ */
12
+ readonly session?: SessionStore | undefined;
13
+ /** Where this device's signing keys live. Defaults to the `deviceKey` capability. */
14
+ readonly deviceKey?: DeviceKey | undefined;
15
+ readonly fetch?: typeof fetch | undefined;
16
+ }
17
+ export declare class AuthClientError extends Error {
18
+ readonly status: number;
19
+ readonly code: string | undefined;
20
+ readonly name = "AuthClientError";
21
+ constructor(message: string, status: number, code: string | undefined);
22
+ }
23
+ export type DeviceHandle = SessionDevice;
24
+ export interface AuthClient extends SessionSource, DeviceSource, HostAuthorizer, DeviceLinker {
25
+ deviceSetup(): Promise<"needed" | "ready">;
26
+ /** Plug into `createActionClient({ headers })` so every surface carries the session. */
27
+ readonly headers: () => Promise<HeadersInit>;
28
+ signIn(input: {
29
+ readonly email: string;
30
+ readonly password: string;
31
+ }): Promise<void>;
32
+ signUp(input: {
33
+ readonly name: string;
34
+ readonly email: string;
35
+ readonly password: string;
36
+ }): Promise<void>;
37
+ signOut(): Promise<void>;
38
+ /** The account actions: `sessions`, `revokeSession`, `agents`, `revokeAgent`. */
39
+ readonly account: ActionClient<AccountActions>;
40
+ }
41
+ export declare function createAuthClient(options?: AuthClientOptions): AuthClient;
42
+ export type { HostPairing, SessionStore } from "@parlor/capabilities";
43
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,SAAS,EAKd,KAAK,YAAY,EAElB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,YAAY,EAGjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,cAAc,EAGnB,KAAK,aAAa,EAElB,KAAK,aAAa,EAEnB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA4BpD,MAAM,WAAW,iBAAiB;IAChC,uFAAuF;IACvF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAC5C,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAC;CAC3C;AAED,qBAAa,eAAgB,SAAQ,KAAK;IAItC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAJnC,SAAkB,IAAI,qBAAqB;IAC3C,YACE,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GAAG,SAAS,EAGlC;CACF;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC;AAEzC,MAAM,WAAW,UACf,SAAQ,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY;IACjE,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;IAC3C,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,MAAM,CAAC,KAAK,EAAE;QACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,cAAc,CAAC,CAAC;CAChD;AA+HD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAkX5E;AAED,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC"}