@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
@@ -0,0 +1,157 @@
1
+ import { type DataProvider, type DeviceId, type UserId } from "@parlor/core";
2
+ import { type ActionsOf } from "@parlor/core/registry";
3
+ import type { DeviceKeys, Migration, PluginHost, PresenceKey } from "@parlor/server";
4
+ import { z } from "zod";
5
+ export type { PresenceKey } from "@parlor/server";
6
+ export interface RegisteredDevice extends DeviceKeys {
7
+ readonly userId: UserId;
8
+ readonly createdAt: number;
9
+ readonly revokedAt?: number | undefined;
10
+ }
11
+ export interface DeviceRegistry {
12
+ /** A revoked device reads as undefined, so no host accepts its signatures again. */
13
+ get(userId: UserId, deviceId: DeviceId): Promise<RegisteredDevice | undefined>;
14
+ }
15
+ export declare function createDeviceRegistry(database: DataProvider): DeviceRegistry;
16
+ export declare const DEVICES_TAG = "parlor_auth_devices";
17
+ export declare const DEVICES_MIGRATION: Migration;
18
+ export declare const DEVICE_LINKING_MIGRATION: Migration;
19
+ export interface ConnectedDevice {
20
+ readonly id: DeviceId;
21
+ readonly name: string;
22
+ readonly platform: string;
23
+ readonly current: boolean;
24
+ readonly createdAt: string;
25
+ /** A linked device vouched for it; the computer pins it on its first prompt. */
26
+ readonly vouched: boolean;
27
+ /** It is showing a link code now. */
28
+ readonly linking: boolean;
29
+ /** The device whose vouch linked it last. */
30
+ readonly linkedBy?: string | undefined;
31
+ }
32
+ /** A device waiting to be linked, as the one linking it checks it against the code. */
33
+ export interface PendingDevice {
34
+ readonly id: DeviceId;
35
+ readonly name: string;
36
+ readonly platform: string;
37
+ readonly signingKey: JsonWebKey;
38
+ readonly presenceKey?: PresenceKey | undefined;
39
+ }
40
+ export interface DeviceActionDeps {
41
+ readonly host: PluginHost;
42
+ readonly registry: DeviceRegistry;
43
+ }
44
+ /**
45
+ * A device showing a link code, found by the code (normalized) or, from its QR, by its id;
46
+ * only `userId`'s own device, still within its 15 minutes.
47
+ */
48
+ export declare function findPendingDevice(database: DataProvider, userId: UserId, by: {
49
+ readonly code: string;
50
+ } | {
51
+ readonly device: string;
52
+ }): Promise<PendingDevice | undefined>;
53
+ export declare function deviceAccountActions(deps: DeviceActionDeps): import("@parlor/core/registry").ActionRegistry<{
54
+ devices: import("@parlor/core").ActionDefinition<z.ZodObject<{}, z.core.$strip>, ConnectedDevice[]>;
55
+ deviceChallenge: import("@parlor/core").ActionDefinition<z.ZodObject<{}, z.core.$strip>, {
56
+ readonly challenge: string;
57
+ }>;
58
+ enrollDevice: import("@parlor/core").ActionDefinition<z.ZodObject<{
59
+ name: z.ZodString;
60
+ platform: z.ZodString;
61
+ challenge: z.ZodString;
62
+ signing: z.ZodObject<{
63
+ keyId: z.ZodString;
64
+ publicKey: z.ZodRecord<z.ZodString, z.ZodUnknown>;
65
+ }, z.core.$strip>;
66
+ presence: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
67
+ format: z.ZodLiteral<"webauthn">;
68
+ credentialId: z.ZodString;
69
+ rpId: z.ZodString;
70
+ publicKey: z.ZodRecord<z.ZodString, z.ZodUnknown>;
71
+ attestation: z.ZodString;
72
+ }, z.core.$strip>, z.ZodObject<{
73
+ format: z.ZodLiteral<"es256-raw">;
74
+ keyId: z.ZodString;
75
+ publicKey: z.ZodRecord<z.ZodString, z.ZodUnknown>;
76
+ }, z.core.$strip>], "format">>;
77
+ proof: z.ZodObject<{
78
+ keyId: z.ZodString;
79
+ presence: z.ZodEnum<{
80
+ none: "none";
81
+ user: "user";
82
+ }>;
83
+ format: z.ZodEnum<{
84
+ "es256-raw": "es256-raw";
85
+ webauthn: "webauthn";
86
+ }>;
87
+ signature: z.ZodString;
88
+ authenticatorData: z.ZodOptional<z.ZodString>;
89
+ clientDataJSON: z.ZodOptional<z.ZodString>;
90
+ }, z.core.$strip>;
91
+ }, z.core.$strip>, {
92
+ readonly id: DeviceId;
93
+ }>;
94
+ startDeviceLink: import("@parlor/core").ActionDefinition<z.ZodObject<{
95
+ device: z.ZodString;
96
+ code: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
97
+ }, z.core.$strip>, {
98
+ readonly expiresAt: string;
99
+ }>;
100
+ resolveDeviceLink: import("@parlor/core").ActionDefinition<z.ZodUnion<readonly [z.ZodObject<{
101
+ code: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
102
+ }, z.core.$strip>, z.ZodObject<{
103
+ device: z.ZodString;
104
+ }, z.core.$strip>]>, PendingDevice>;
105
+ vouchDevice: import("@parlor/core").ActionDefinition<z.ZodObject<{
106
+ device: z.ZodString;
107
+ voucher: z.ZodString;
108
+ app: z.ZodString;
109
+ commitment: z.ZodString;
110
+ at: z.ZodNumber;
111
+ signature: z.ZodObject<{
112
+ keyId: z.ZodString;
113
+ presence: z.ZodEnum<{
114
+ none: "none";
115
+ user: "user";
116
+ }>;
117
+ format: z.ZodEnum<{
118
+ "es256-raw": "es256-raw";
119
+ webauthn: "webauthn";
120
+ }>;
121
+ signature: z.ZodString;
122
+ authenticatorData: z.ZodOptional<z.ZodString>;
123
+ clientDataJSON: z.ZodOptional<z.ZodString>;
124
+ }, z.core.$strip>;
125
+ }, z.core.$strip>, {
126
+ readonly id: string;
127
+ }>;
128
+ removeDevice: import("@parlor/core").ActionDefinition<z.ZodObject<{
129
+ device: z.ZodString;
130
+ actor: z.ZodString;
131
+ app: z.ZodString;
132
+ at: z.ZodNumber;
133
+ signature: z.ZodObject<{
134
+ keyId: z.ZodString;
135
+ presence: z.ZodEnum<{
136
+ none: "none";
137
+ user: "user";
138
+ }>;
139
+ format: z.ZodEnum<{
140
+ "es256-raw": "es256-raw";
141
+ webauthn: "webauthn";
142
+ }>;
143
+ signature: z.ZodString;
144
+ authenticatorData: z.ZodOptional<z.ZodString>;
145
+ clientDataJSON: z.ZodOptional<z.ZodString>;
146
+ }, z.core.$strip>;
147
+ }, z.core.$strip>, {
148
+ readonly id: string;
149
+ }>;
150
+ revokeDevice: import("@parlor/core").ActionDefinition<z.ZodObject<{
151
+ id: z.ZodString;
152
+ }, z.core.$strip>, {
153
+ readonly id: string;
154
+ }>;
155
+ }>;
156
+ export type DeviceAccountActions = ActionsOf<ReturnType<typeof deviceAccountActions>>;
157
+ //# sourceMappingURL=devices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,YAAY,EAEjB,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EACV,UAAU,EAEV,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;CAChF;AAgBD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,YAAY,GAAG,cAAc,CAyB3E;AAED,eAAO,MAAM,WAAW,wBAAwB,CAAC;AAEjD,eAAO,MAAM,iBAAiB,EAAE,SAsB/B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,SAQtC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,uFAAuF;AACvF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAChD;AA0CD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;CACnC;AAsDD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAqBpC;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB;;;4BA+BG,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA0Bd,QAAQ;;;;;;4BAkEU,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA8CxB,MAAM;;;;;;;;;;;;;;;;;;;;;;qBAqEN,MAAM;;;;;qBA0CL,MAAM;;GAgB5D;AAED,MAAM,MAAM,oBAAoB,GAAG,SAAS,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,398 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { decodeBase64url, enrollmentBytes } from "@parlor/capabilities/encoding";
3
+ import { ActionError, asId, defineAction, } from "@parlor/core";
4
+ import { createRegistry } from "@parlor/core/registry";
5
+ import { sql } from "drizzle-orm";
6
+ import { z } from "zod";
7
+ import { signedIn } from "./account.js";
8
+ import { jwkOf, rowsOf } from "./database.js";
9
+ import { verifyEs256 } from "./es256.js";
10
+ import { LINK_CODE_LENGTH, normalizeLinkCode, removeStatement, verifyStatement, vouchStatement, } from "./statements.js";
11
+ const parsed = (value) => typeof value === "string" ? JSON.parse(value) : value;
12
+ export function createDeviceRegistry(database) {
13
+ return {
14
+ async get(userId, deviceId) {
15
+ const [row] = rowsOf(await database.read().execute(sql `
16
+ select id, user_id, name, platform, signing_key, presence_key, vouches,
17
+ (extract(epoch from created_at) * 1000)::bigint as created_ms
18
+ from parlor_auth_devices
19
+ where id = ${deviceId} and user_id = ${userId} and revoked_at is null`));
20
+ if (!row)
21
+ return undefined;
22
+ const presence = parsed(row.presence_key);
23
+ const vouches = parsed(row.vouches) ?? [];
24
+ return {
25
+ id: asId("device", row.id),
26
+ userId: asId("user", row.user_id),
27
+ name: row.name,
28
+ platform: row.platform,
29
+ signingKey: jwkOf(row.signing_key),
30
+ presenceKey: presence ?? undefined,
31
+ ...(vouches.length > 0 ? { vouches } : {}),
32
+ createdAt: Number(row.created_ms),
33
+ };
34
+ },
35
+ };
36
+ }
37
+ export const DEVICES_TAG = "parlor_auth_devices";
38
+ export const DEVICES_MIGRATION = {
39
+ id: "auth/0004_devices",
40
+ sql: `
41
+ create table parlor_auth_devices (
42
+ id text primary key,
43
+ user_id text not null,
44
+ name text not null,
45
+ platform text not null,
46
+ signing_key jsonb not null,
47
+ presence_key jsonb,
48
+ created_at timestamptz not null default now(),
49
+ revoked_at timestamptz
50
+ );
51
+ create index parlor_auth_devices_user on parlor_auth_devices (user_id);
52
+ create unique index parlor_auth_devices_signing_key on parlor_auth_devices
53
+ (user_id, (signing_key->>'x'), (signing_key->>'y')) where revoked_at is null;
54
+
55
+ create table parlor_auth_device_challenges (
56
+ challenge text primary key,
57
+ user_id text not null,
58
+ expires_at timestamptz not null
59
+ )`,
60
+ };
61
+ export const DEVICE_LINKING_MIGRATION = {
62
+ id: "auth/0007_device_linking",
63
+ sql: `
64
+ alter table parlor_auth_devices add column vouches jsonb not null default '[]'::jsonb;
65
+ alter table parlor_auth_devices add column link_code text;
66
+ alter table parlor_auth_devices add column link_expires_at timestamptz;
67
+ create index parlor_auth_devices_link_code on parlor_auth_devices (user_id, link_code)
68
+ where link_code is not null`,
69
+ };
70
+ const CHALLENGE_TTL_MS = 5 * 60_000;
71
+ const LINK_TTL_MS = 15 * 60_000;
72
+ const VOUCH_MAX_AGE_MS = 5 * 60_000;
73
+ const linkCodeInput = z
74
+ .string()
75
+ .min(LINK_CODE_LENGTH)
76
+ .max(32)
77
+ .transform(normalizeLinkCode)
78
+ .refine((code) => /^[0-9A-HJKMNP-TV-Z]{10}$/.test(code), "Enter the 10-character code.");
79
+ const P256 = { name: "ECDSA", namedCurve: "P-256" };
80
+ const jwk = z.record(z.string(), z.unknown());
81
+ const deviceSignature = z.object({
82
+ keyId: z.string(),
83
+ presence: z.enum(["none", "user"]),
84
+ format: z.enum(["es256-raw", "webauthn"]),
85
+ signature: z.string(),
86
+ authenticatorData: z.string().optional(),
87
+ clientDataJSON: z.string().optional(),
88
+ });
89
+ const presenceInput = z.discriminatedUnion("format", [
90
+ z.object({
91
+ format: z.literal("webauthn"),
92
+ credentialId: z.string().min(1).max(1024),
93
+ rpId: z.string().min(1).max(255),
94
+ publicKey: jwk,
95
+ attestation: z.string().min(1),
96
+ }),
97
+ z.object({
98
+ format: z.literal("es256-raw"),
99
+ keyId: z.string().min(1),
100
+ publicKey: jwk,
101
+ }),
102
+ ]);
103
+ function databaseOf(host) {
104
+ if (!host.database)
105
+ throw new Error("Devices need a database.");
106
+ return host.database;
107
+ }
108
+ const invalid = (message) => new ActionError("invalid_input", message);
109
+ async function pointOf(key) {
110
+ try {
111
+ const imported = await crypto.subtle.importKey("jwk", key, P256, true, [
112
+ "verify",
113
+ ]);
114
+ const { kty, crv, x, y } = await crypto.subtle.exportKey("jwk", imported);
115
+ return { kty: kty, crv: crv, x: x, y: y };
116
+ }
117
+ catch {
118
+ throw invalid("A device key must be an ECDSA P-256 public key.");
119
+ }
120
+ }
121
+ async function presenceOf(input) {
122
+ if (!input)
123
+ return undefined;
124
+ const publicKey = await pointOf(input.publicKey);
125
+ if (input.format === "webauthn") {
126
+ const { credentialId, rpId, attestation } = input;
127
+ return {
128
+ stored: { format: "webauthn", credentialId, rpId, publicKey },
129
+ proven: { format: "webauthn", credentialId, publicKey, attestation },
130
+ };
131
+ }
132
+ const key = { format: "es256-raw", keyId: input.keyId, publicKey };
133
+ return { stored: key, proven: key };
134
+ }
135
+ const isoOf = (value) => new Date(value).toISOString();
136
+ /**
137
+ * A device showing a link code, found by the code (normalized) or, from its QR, by its id;
138
+ * only `userId`'s own device, still within its 15 minutes.
139
+ */
140
+ export async function findPendingDevice(database, userId, by) {
141
+ const match = "code" in by
142
+ ? sql `link_code = ${normalizeLinkCode(by.code)}`
143
+ : sql `id = ${by.device} and link_code is not null`;
144
+ const [row] = rowsOf(await database.read().execute(sql `
145
+ select id, user_id, name, platform, signing_key, presence_key, vouches, 0 as created_ms
146
+ from parlor_auth_devices
147
+ where user_id = ${userId} and ${match}
148
+ and link_expires_at > ${new Date().toISOString()} and revoked_at is null`));
149
+ if (!row)
150
+ return undefined;
151
+ const presence = parsed(row.presence_key);
152
+ return {
153
+ id: asId("device", row.id),
154
+ name: row.name,
155
+ platform: row.platform,
156
+ signingKey: jwkOf(row.signing_key),
157
+ ...(presence ? { presenceKey: presence } : {}),
158
+ };
159
+ }
160
+ export function deviceAccountActions(deps) {
161
+ return createRegistry({
162
+ devices: defineAction({
163
+ description: "List the devices that sign this account's chat messages.",
164
+ input: z.object({}),
165
+ reads: [DEVICES_TAG],
166
+ run: async (_input, ctx) => {
167
+ const user = signedIn(ctx);
168
+ const rows = rowsOf(await databaseOf(deps.host).read().execute(sql `
169
+ select id, name, platform, created_at, jsonb_array_length(vouches) > 0 as vouched,
170
+ coalesce(link_expires_at > ${new Date().toISOString()}, false) as linking,
171
+ vouches -> -1 ->> 'voucher' as linked_by
172
+ from parlor_auth_devices
173
+ where user_id = ${user.userId} and revoked_at is null order by created_at, id`));
174
+ return rows.map((row) => ({
175
+ id: asId("device", row.id),
176
+ name: row.name,
177
+ platform: row.platform,
178
+ current: row.id === user.deviceId,
179
+ createdAt: isoOf(row.created_at),
180
+ vouched: row.vouched,
181
+ linking: row.linking,
182
+ ...(row.linked_by ? { linkedBy: row.linked_by } : {}),
183
+ }));
184
+ },
185
+ }),
186
+ deviceChallenge: defineAction({
187
+ description: "A single-use challenge for enrolling this device.",
188
+ input: z.object({}),
189
+ run: async (_input, ctx) => {
190
+ const user = signedIn(ctx);
191
+ const challenge = randomBytes(32).toString("base64url");
192
+ await databaseOf(deps.host).transaction(async (handle) => {
193
+ const tx = handle;
194
+ await tx.execute(sql `delete from parlor_auth_device_challenges where expires_at < ${new Date().toISOString()}`);
195
+ await tx.execute(sql `
196
+ insert into parlor_auth_device_challenges (challenge, user_id, expires_at)
197
+ values (${challenge}, ${user.userId},
198
+ ${new Date(Date.now() + CHALLENGE_TTL_MS).toISOString()})`);
199
+ });
200
+ return { challenge };
201
+ },
202
+ }),
203
+ enrollDevice: defineAction({
204
+ description: "Register this device's public keys, proven by signing a challenge.",
205
+ input: z.object({
206
+ name: z.string().min(1).max(80),
207
+ platform: z.string().min(1).max(32),
208
+ challenge: z.string().min(1).max(256),
209
+ signing: z.object({ keyId: z.string().min(1), publicKey: jwk }),
210
+ presence: presenceInput.optional(),
211
+ proof: deviceSignature,
212
+ }),
213
+ run: async (input, ctx) => {
214
+ const user = signedIn(ctx);
215
+ const signing = await pointOf(input.signing.publicKey);
216
+ const presence = await presenceOf(input.presence);
217
+ const { proof } = input;
218
+ let proven = false;
219
+ try {
220
+ proven =
221
+ proof.format === "es256-raw" &&
222
+ proof.presence === "none" &&
223
+ (await verifyEs256(signing, decodeBase64url(proof.signature), enrollmentBytes({
224
+ challenge: decodeBase64url(input.challenge),
225
+ signing,
226
+ presence: presence?.proven,
227
+ })));
228
+ }
229
+ catch {
230
+ proven = false;
231
+ }
232
+ if (!proven)
233
+ throw invalid("The device could not prove it holds its keys.");
234
+ const enrolled = await databaseOf(deps.host).transaction(async (handle) => {
235
+ const tx = handle;
236
+ const consumed = rowsOf(await tx.execute(sql `
237
+ delete from parlor_auth_device_challenges
238
+ where challenge = ${input.challenge} and user_id = ${user.userId}
239
+ and expires_at >= ${new Date().toISOString()}
240
+ returning challenge`));
241
+ if (consumed.length === 0)
242
+ throw invalid("This challenge is unknown or expired.");
243
+ const id = asId("device", randomBytes(16).toString("base64url"));
244
+ const [inserted] = rowsOf(await tx.execute(sql `
245
+ insert into parlor_auth_devices (id, user_id, name, platform, signing_key, presence_key)
246
+ values (${id}, ${user.userId}, ${input.name}, ${input.platform},
247
+ ${JSON.stringify(signing)}::jsonb,
248
+ ${presence ? JSON.stringify(presence.stored) : null}::jsonb)
249
+ on conflict (user_id, (signing_key->>'x'), (signing_key->>'y'))
250
+ where revoked_at is null do nothing
251
+ returning id`));
252
+ if (!inserted) {
253
+ const [existing] = rowsOf(await tx.execute(sql `
254
+ select id from parlor_auth_devices
255
+ where user_id = ${user.userId} and revoked_at is null
256
+ and signing_key->>'x' = ${signing.x} and signing_key->>'y' = ${signing.y}`));
257
+ return { id: asId("device", existing.id), added: false };
258
+ }
259
+ return { id, added: true };
260
+ });
261
+ if (enrolled.added) {
262
+ await deps.host.announce(ctx.scope, [[DEVICES_TAG, enrolled.id]]);
263
+ }
264
+ return { id: enrolled.id };
265
+ },
266
+ }),
267
+ startDeviceLink: defineAction({
268
+ description: "Show this device's link code for 15 minutes, so a linked device can find it.",
269
+ input: z.object({ device: z.string().min(1), code: linkCodeInput }),
270
+ run: async ({ device, code }, ctx) => {
271
+ const user = signedIn(ctx);
272
+ const expiresAt = new Date(Date.now() + LINK_TTL_MS).toISOString();
273
+ const started = rowsOf(await databaseOf(deps.host).transaction((handle) => handle.execute(sql `
274
+ update parlor_auth_devices set link_code = ${code}, link_expires_at = ${expiresAt}
275
+ where id = ${device} and user_id = ${user.userId} and revoked_at is null
276
+ returning id`)));
277
+ if (started.length === 0)
278
+ throw new ActionError("not_found", `No device ${device}`);
279
+ return { expiresAt };
280
+ },
281
+ }),
282
+ resolveDeviceLink: defineAction({
283
+ description: "Find this account's device showing a link code, to check it and vouch for it.",
284
+ input: z.union([
285
+ z.object({ code: linkCodeInput }),
286
+ z.object({ device: z.string().min(1).max(64) }),
287
+ ]),
288
+ run: async (input, ctx) => {
289
+ const user = signedIn(ctx);
290
+ const found = await findPendingDevice(databaseOf(deps.host), user.userId, input);
291
+ if (!found) {
292
+ throw new ActionError("not_found", "No device of yours shows this code. Check it, or show a new code on the device.");
293
+ }
294
+ return found;
295
+ },
296
+ }),
297
+ vouchDevice: defineAction({
298
+ description: "Vouch, with this device's presence key, that a device showing a link code is the user's.",
299
+ input: z.object({
300
+ device: z.string().min(1),
301
+ voucher: z.string().min(1),
302
+ app: z.string().url(),
303
+ commitment: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
304
+ at: z.number(),
305
+ signature: deviceSignature,
306
+ }),
307
+ run: async (input, ctx) => {
308
+ const user = signedIn(ctx);
309
+ const voucher = await deps.registry.get(user.userId, asId("device", input.voucher));
310
+ if (!voucher?.presenceKey) {
311
+ throw new ActionError("forbidden", "Only a linked device that can confirm it is you can link another device.");
312
+ }
313
+ const outcome = await verifyStatement({
314
+ statement: vouchStatement(input.app, input.voucher, input.device, input.commitment, input.at),
315
+ deviceId: voucher.id,
316
+ signature: input.signature,
317
+ }, {
318
+ deviceId: voucher.id,
319
+ signingKey: voucher.signingKey,
320
+ presenceKey: voucher.presenceKey,
321
+ }, { requirePresence: true, now: Date.now(), maxAgeMs: VOUCH_MAX_AGE_MS });
322
+ if (outcome !== "valid")
323
+ throw invalid("This confirmation did not check out.");
324
+ const vouch = {
325
+ voucher: voucher.id,
326
+ commitment: input.commitment,
327
+ at: input.at,
328
+ signature: input.signature,
329
+ };
330
+ const vouched = rowsOf(await databaseOf(deps.host).transaction((handle) => handle.execute(sql `
331
+ update parlor_auth_devices
332
+ set vouches = vouches || ${JSON.stringify([vouch])}::jsonb,
333
+ link_code = null, link_expires_at = null
334
+ where id = ${input.device} and user_id = ${user.userId} and revoked_at is null
335
+ and link_code is not null and link_expires_at > ${new Date().toISOString()}
336
+ returning id`)));
337
+ if (vouched.length === 0) {
338
+ throw new ActionError("not_found", "That device's code expired. Show a new code on it and try again.");
339
+ }
340
+ await deps.host.announce(ctx.scope, [[DEVICES_TAG, input.device]]);
341
+ return { id: input.device };
342
+ },
343
+ }),
344
+ removeDevice: defineAction({
345
+ description: "Remove a device, confirmed with this device's presence key; no computer accepts its signatures afterwards.",
346
+ input: z.object({
347
+ device: z.string().min(1),
348
+ actor: z.string().min(1),
349
+ app: z.string().url(),
350
+ at: z.number(),
351
+ signature: deviceSignature,
352
+ }),
353
+ run: async (input, ctx) => {
354
+ const user = signedIn(ctx);
355
+ const actor = await deps.registry.get(user.userId, asId("device", input.actor));
356
+ if (!actor?.presenceKey) {
357
+ throw new ActionError("forbidden", "Only a linked device that can confirm it is you can remove a device.");
358
+ }
359
+ const outcome = await verifyStatement({
360
+ statement: removeStatement(input.app, input.actor, input.device, input.at),
361
+ deviceId: actor.id,
362
+ signature: input.signature,
363
+ }, {
364
+ deviceId: actor.id,
365
+ signingKey: actor.signingKey,
366
+ presenceKey: actor.presenceKey,
367
+ }, { requirePresence: true, now: Date.now(), maxAgeMs: VOUCH_MAX_AGE_MS });
368
+ if (outcome !== "valid")
369
+ throw invalid("This confirmation did not check out.");
370
+ const removed = rowsOf(await databaseOf(deps.host).transaction((handle) => handle.execute(sql `
371
+ update parlor_auth_devices set revoked_at = now(), link_code = null
372
+ where id = ${input.device} and user_id = ${user.userId} and revoked_at is null
373
+ returning id`)));
374
+ if (removed.length === 0) {
375
+ throw new ActionError("not_found", `No device ${input.device}`);
376
+ }
377
+ await deps.host.announce(ctx.scope, [[DEVICES_TAG, input.device]]);
378
+ return { id: input.device };
379
+ },
380
+ }),
381
+ revokeDevice: defineAction({
382
+ description: "Deprecated: use removeDevice, which a linked device confirms. Remove a device; no computer accepts its signatures afterwards.",
383
+ input: z.object({ id: z.string().min(1) }),
384
+ run: async ({ id }, ctx) => {
385
+ const user = signedIn(ctx);
386
+ const revoked = rowsOf(await databaseOf(deps.host).transaction((handle) => handle.execute(sql `
387
+ update parlor_auth_devices set revoked_at = now()
388
+ where id = ${id} and user_id = ${user.userId} and revoked_at is null
389
+ returning id`)));
390
+ if (revoked.length === 0)
391
+ throw new ActionError("not_found", `No device ${id}`);
392
+ await deps.host.announce(ctx.scope, [[DEVICES_TAG, id]]);
393
+ return { id };
394
+ },
395
+ }),
396
+ });
397
+ }
398
+ //# sourceMappingURL=devices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.js","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** WebAuthn signs in ASN.1 DER; WebCrypto verifies the raw `r || s`. */
2
+ export declare function derToRaw(der: Uint8Array): Uint8Array | undefined;
3
+ export declare function verifyEs256(publicKey: JsonWebKey, signature: Uint8Array, data: Uint8Array): Promise<boolean>;
4
+ //# sourceMappingURL=es256.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"es256.d.ts","sourceRoot":"","sources":["../src/es256.ts"],"names":[],"mappings":"AAIA,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CA2BhE;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,UAAU,EACrB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,OAAO,CAAC,CAQlB"}
package/dist/es256.js ADDED
@@ -0,0 +1,44 @@
1
+ const P256 = { name: "ECDSA", namedCurve: "P-256" };
2
+ const ES256 = { name: "ECDSA", hash: "SHA-256" };
3
+ const P256_SCALAR_BYTES = 32;
4
+ /** WebAuthn signs in ASN.1 DER; WebCrypto verifies the raw `r || s`. */
5
+ export function derToRaw(der) {
6
+ let at = 0;
7
+ const expect = (tag) => der[at++] === tag;
8
+ const length = () => {
9
+ const first = der[at++];
10
+ if (first === undefined || first < 0x80)
11
+ return first;
12
+ if (first !== 0x81)
13
+ return undefined;
14
+ return der[at++];
15
+ };
16
+ const integer = () => {
17
+ if (!expect(0x02))
18
+ return undefined;
19
+ const size = length();
20
+ if (size === undefined)
21
+ return undefined;
22
+ const bytes = der.slice(at, at + size);
23
+ at += size;
24
+ const trimmed = bytes.slice(bytes.findIndex((byte) => byte !== 0));
25
+ if (bytes.length !== size || trimmed.length > P256_SCALAR_BYTES)
26
+ return undefined;
27
+ return trimmed;
28
+ };
29
+ if (!expect(0x30) || length() !== der.length - at)
30
+ return undefined;
31
+ const r = integer();
32
+ const s = integer();
33
+ if (!r || !s || at !== der.length)
34
+ return undefined;
35
+ const raw = new Uint8Array(2 * P256_SCALAR_BYTES);
36
+ raw.set(r, P256_SCALAR_BYTES - r.length);
37
+ raw.set(s, 2 * P256_SCALAR_BYTES - s.length);
38
+ return raw;
39
+ }
40
+ export async function verifyEs256(publicKey, signature, data) {
41
+ const key = await crypto.subtle.importKey("jwk", publicKey, P256, false, ["verify"]);
42
+ return crypto.subtle.verify(ES256, key, new Uint8Array(signature), new Uint8Array(data));
43
+ }
44
+ //# sourceMappingURL=es256.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"es256.js","sourceRoot":"","sources":["../src/es256.ts"],"names":[],"mappings":"AAAA,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,CAAC;AAC7D,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAW,CAAC;AAC1D,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAC,GAAe;IACtC,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC;IAClD,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,MAAM,KAAK,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QACxB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,IAAI;YAAE,OAAO,KAAK,CAAC;QACtD,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACrC,OAAO,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IACnB,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;QACtB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QACvC,EAAE,IAAI,IAAI,CAAC;QACX,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB;YAAE,OAAO,SAAS,CAAC;QAClF,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,SAAS,CAAC;IACpE,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC;IAClD,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAqB,EACrB,SAAqB,EACrB,IAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACrF,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CACzB,KAAK,EACL,GAAG,EACH,IAAI,UAAU,CAAC,SAAS,CAAC,EACzB,IAAI,UAAU,CAAC,IAAI,CAAC,CACrB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type GrantId } from "@parlor/core";
2
+ /** One grant per user and OAuth client; re-authorizing after a revocation mints newer tokens. */
3
+ export declare function grantIdOf(userId: string, clientId: string): GrantId;
4
+ //# sourceMappingURL=grants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAElD,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnE"}