@boxxy/auth-oauth 0.2.6 → 0.2.7

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 (144) hide show
  1. package/README.md +12 -13
  2. package/dist/auth/create-auth-admin.d.ts +25 -12
  3. package/dist/auth/create-security-event-log.d.ts +1 -1
  4. package/dist/auth/format-security-event.d.ts +10 -2
  5. package/dist/auth/open-auth-database.d.ts +2 -2
  6. package/dist/http/create-public-door.d.ts +5 -1
  7. package/dist/http/{answer-mcp-request.d.ts → types.d.ts} +0 -2
  8. package/dist/index.d.ts +2 -5
  9. package/dist/migrations/grant-rows-migration.d.ts +2 -0
  10. package/dist/migrations/run-auth-migrations.d.ts +2 -0
  11. package/dist/test-utils/authorize-test-client.d.ts +1 -13
  12. package/dist/test-utils/pre-package-schema.d.ts +1 -0
  13. package/dist/test-utils/seed-pre-package-auth.d.ts +21 -0
  14. package/package.json +7 -13
  15. package/src/auth/create-auth-admin.ts +109 -41
  16. package/src/auth/create-security-event-log.ts +1 -3
  17. package/src/auth/format-security-event.ts +12 -11
  18. package/src/auth/normalize-client-name.ts +0 -2
  19. package/src/auth/open-auth-database.ts +8 -13
  20. package/src/http/create-public-door.ts +113 -29
  21. package/src/http/is-loopback-host.ts +0 -2
  22. package/src/http/normalize-public-url.ts +0 -2
  23. package/src/http/types.ts +11 -0
  24. package/src/index.ts +9 -5
  25. package/src/migrations/grant-rows-migration.ts +108 -0
  26. package/src/migrations/run-auth-migrations.ts +53 -0
  27. package/src/test-utils/authorize-test-client.ts +2 -194
  28. package/src/test-utils/build-stub-clock.ts +0 -2
  29. package/src/test-utils/pre-package-schema.ts +36 -0
  30. package/src/test-utils/register-test-cleanup.ts +0 -2
  31. package/src/test-utils/seed-pre-package-auth.ts +162 -0
  32. package/src/test-utils/setup-temp-dir.ts +0 -2
  33. package/src/utils/types.ts +0 -2
  34. package/dist/auth/answer-authorize-request.d.ts +0 -2
  35. package/dist/auth/answer-consent-request.d.ts +0 -10
  36. package/dist/auth/answer-enroll-request.d.ts +0 -9
  37. package/dist/auth/answer-login-request.d.ts +0 -9
  38. package/dist/auth/approval-window-seconds.d.ts +0 -1
  39. package/dist/auth/build-consent-binding.d.ts +0 -1
  40. package/dist/auth/create-approval-state.d.ts +0 -41
  41. package/dist/auth/create-enrollment-state.d.ts +0 -6
  42. package/dist/auth/create-enrollment-token.d.ts +0 -6
  43. package/dist/auth/create-owner-plugin.d.ts +0 -38
  44. package/dist/auth/create-passkey.d.ts +0 -11
  45. package/dist/auth/derive-token-hash.d.ts +0 -1
  46. package/dist/auth/is-allowed-redirect-uri.d.ts +0 -1
  47. package/dist/auth/load-web-authn.d.ts +0 -6
  48. package/dist/auth/mcp-auth-database.d.ts +0 -5
  49. package/dist/auth/mcp-auth-schema.d.ts +0 -54
  50. package/dist/auth/open-mcp-auth.d.ts +0 -80
  51. package/dist/auth/passkey-script.d.ts +0 -4
  52. package/dist/auth/read-client-name.d.ts +0 -3
  53. package/dist/auth/read-client-views.d.ts +0 -9
  54. package/dist/auth/read-enrollment-token.d.ts +0 -3
  55. package/dist/auth/read-grant-views.d.ts +0 -12
  56. package/dist/auth/read-owner-session-id.d.ts +0 -2
  57. package/dist/auth/record-refused-exchange.d.ts +0 -10
  58. package/dist/auth/remove-client.d.ts +0 -3
  59. package/dist/auth/remove-owner-session.d.ts +0 -3
  60. package/dist/auth/render-consent-page.d.ts +0 -9
  61. package/dist/auth/render-enroll-page.d.ts +0 -10
  62. package/dist/auth/render-login-page.d.ts +0 -9
  63. package/dist/auth/revoke-grant.d.ts +0 -3
  64. package/dist/auth/to-html-text.d.ts +0 -1
  65. package/dist/auth/to-script-json.d.ts +0 -1
  66. package/dist/auth/types.d.ts +0 -79
  67. package/dist/auth/update-passkey-use.d.ts +0 -10
  68. package/dist/auth/verify-oauth-query.d.ts +0 -1
  69. package/dist/auth/verify-passkey-assertion.d.ts +0 -11
  70. package/dist/auth/verify-passkey-registration.d.ts +0 -14
  71. package/dist/auth/verify-session-passkey.d.ts +0 -8
  72. package/dist/auth/write-grant-use.d.ts +0 -3
  73. package/dist/http/build-page-response.d.ts +0 -5
  74. package/dist/http/create-mcp-http-app.d.ts +0 -92
  75. package/dist/http/pick-error-message.d.ts +0 -1
  76. package/dist/http/public-door-paths.d.ts +0 -17
  77. package/dist/http/read-token-access.d.ts +0 -8
  78. package/dist/http/verify-access-token.d.ts +0 -2
  79. package/dist/test-utils/build-mock-new-passkey.d.ts +0 -2
  80. package/dist/test-utils/build-passkey-seeder.d.ts +0 -5
  81. package/dist/test-utils/build-seeder.d.ts +0 -5
  82. package/dist/test-utils/build-stub-authenticator.d.ts +0 -48
  83. package/dist/test-utils/load-web-authn-early.d.ts +0 -1
  84. package/dist/test-utils/seed-passkey.d.ts +0 -4
  85. package/dist/test-utils/start-stub-telemetry-collector.d.ts +0 -5
  86. package/dist/test-utils/types.d.ts +0 -7
  87. package/dist/utils/is-record.d.ts +0 -1
  88. package/dist/utils/parse-json-strings.d.ts +0 -1
  89. package/src/auth/answer-authorize-request.ts +0 -137
  90. package/src/auth/answer-consent-request.ts +0 -189
  91. package/src/auth/answer-enroll-request.ts +0 -190
  92. package/src/auth/answer-login-request.ts +0 -256
  93. package/src/auth/approval-window-seconds.ts +0 -5
  94. package/src/auth/build-consent-binding.ts +0 -13
  95. package/src/auth/create-approval-state.ts +0 -221
  96. package/src/auth/create-enrollment-state.ts +0 -56
  97. package/src/auth/create-enrollment-token.ts +0 -28
  98. package/src/auth/create-owner-plugin.ts +0 -113
  99. package/src/auth/create-passkey.ts +0 -48
  100. package/src/auth/derive-token-hash.ts +0 -7
  101. package/src/auth/is-allowed-redirect-uri.ts +0 -19
  102. package/src/auth/load-web-authn.ts +0 -19
  103. package/src/auth/mcp-auth-database.ts +0 -10
  104. package/src/auth/mcp-auth-schema.ts +0 -56
  105. package/src/auth/open-mcp-auth.ts +0 -138
  106. package/src/auth/passkey-script.ts +0 -50
  107. package/src/auth/read-client-name.ts +0 -17
  108. package/src/auth/read-client-views.ts +0 -27
  109. package/src/auth/read-enrollment-token.ts +0 -20
  110. package/src/auth/read-grant-views.ts +0 -53
  111. package/src/auth/read-owner-session-id.ts +0 -16
  112. package/src/auth/record-refused-exchange.ts +0 -74
  113. package/src/auth/remove-client.ts +0 -33
  114. package/src/auth/remove-owner-session.ts +0 -11
  115. package/src/auth/render-consent-page.ts +0 -61
  116. package/src/auth/render-enroll-page.ts +0 -59
  117. package/src/auth/render-login-page.ts +0 -58
  118. package/src/auth/revoke-grant.ts +0 -25
  119. package/src/auth/to-html-text.ts +0 -10
  120. package/src/auth/to-script-json.ts +0 -8
  121. package/src/auth/types.ts +0 -115
  122. package/src/auth/update-passkey-use.ts +0 -31
  123. package/src/auth/verify-oauth-query.ts +0 -41
  124. package/src/auth/verify-passkey-assertion.ts +0 -124
  125. package/src/auth/verify-passkey-registration.ts +0 -118
  126. package/src/auth/verify-session-passkey.ts +0 -47
  127. package/src/auth/write-grant-use.ts +0 -18
  128. package/src/http/answer-mcp-request.ts +0 -77
  129. package/src/http/build-page-response.ts +0 -26
  130. package/src/http/create-mcp-http-app.ts +0 -340
  131. package/src/http/pick-error-message.ts +0 -21
  132. package/src/http/public-door-paths.ts +0 -21
  133. package/src/http/read-token-access.ts +0 -44
  134. package/src/http/verify-access-token.ts +0 -46
  135. package/src/test-utils/build-mock-new-passkey.ts +0 -15
  136. package/src/test-utils/build-passkey-seeder.ts +0 -11
  137. package/src/test-utils/build-seeder.ts +0 -10
  138. package/src/test-utils/build-stub-authenticator.ts +0 -244
  139. package/src/test-utils/load-web-authn-early.ts +0 -8
  140. package/src/test-utils/seed-passkey.ts +0 -32
  141. package/src/test-utils/start-stub-telemetry-collector.ts +0 -27
  142. package/src/test-utils/types.ts +0 -11
  143. package/src/utils/is-record.ts +0 -5
  144. package/src/utils/parse-json-strings.ts +0 -15
@@ -0,0 +1,162 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import { createHash } from 'node:crypto';
3
+ import { PRE_PACKAGE_SCHEMA } from './pre-package-schema.ts';
4
+
5
+ export interface PrePackageGrant {
6
+ readonly clientName: string;
7
+ readonly scopes: readonly string[];
8
+
9
+ // how many times the client refreshed; each refresh rotated out the token pair before it
10
+ readonly refreshes: number;
11
+
12
+ // what the old `boxxy_grant_use` table held for the grant
13
+ readonly lastUsedAt: string | null;
14
+
15
+ // true: every token of the grant expired an hour ago
16
+ readonly isExpired?: boolean;
17
+ }
18
+
19
+ export interface PrePackageAuth {
20
+ readonly origin: string;
21
+ readonly grants: readonly PrePackageGrant[];
22
+ readonly passkeyID: string;
23
+ readonly now: number;
24
+ }
25
+
26
+ // One per seeded grant, in the order of the input: its IDs, and its newest token pair.
27
+ export interface SeededPrePackageGrant {
28
+ readonly grantID: string;
29
+ readonly clientID: string;
30
+ readonly accessToken: string;
31
+ readonly refreshToken: string;
32
+ }
33
+
34
+ const OWNER_ID = 'owner-user-row';
35
+ const HOUR_MS = 60 * 60_000;
36
+
37
+ // Writes the auth database the way boxxy's own OAuth server left it before the package: one owner
38
+ // `user` row that every token, session and consent points at, and the grant-use table. No old
39
+ // module is left to seed through, so the rows go in directly, as that server wrote them.
40
+ export function seedPrePackageAuth(
41
+ sqlite: Database,
42
+ input: PrePackageAuth,
43
+ ): SeededPrePackageGrant[] {
44
+ const at = (offsetMs: number) => new Date(input.now + offsetMs).toISOString();
45
+
46
+ return sqlite.transaction(() => {
47
+ for (const statement of PRE_PACKAGE_SCHEMA) {
48
+ sqlite.run(statement);
49
+ }
50
+
51
+ sqlite.run(
52
+ `INSERT INTO "user" (id, name, email, emailVerified, createdAt, updatedAt) VALUES (?, 'owner', 'owner@boxxy.invalid', 1, ?, ?)`,
53
+ [OWNER_ID, at(-HOUR_MS), at(-HOUR_MS)],
54
+ );
55
+
56
+ sqlite.run(
57
+ `INSERT INTO session (id, expiresAt, token, createdAt, updatedAt, userId) VALUES ('owner-session', ?, 'owner-session-token', ?, ?, ?)`,
58
+ [at(HOUR_MS), at(-HOUR_MS), at(-HOUR_MS), OWNER_ID],
59
+ );
60
+
61
+ sqlite.run(
62
+ `INSERT INTO oauthResource (id, identifier, name, customClaims, dpopBoundAccessTokensRequired, disabled, createdAt, updatedAt, policyVersion, metadata) VALUES ('resource-1', ?, ?, 'null', 0, 0, ?, ?, 1, 'null')`,
63
+ [`${input.origin}/mcp`, `${input.origin}/mcp`, at(-HOUR_MS), at(-HOUR_MS)],
64
+ );
65
+
66
+ sqlite.run(
67
+ `INSERT INTO boxxy_passkey (id, public_key, counter, transports, created_at, last_used_at) VALUES (?, 'pQECAyYgASFYIA', 1, '["internal"]', ?, ?)`,
68
+ [input.passkeyID, at(-HOUR_MS), at(-HOUR_MS)],
69
+ );
70
+
71
+ return input.grants.map((grant, index) =>
72
+ seedGrant(sqlite, grant, { index, at, resource: JSON.stringify([`${input.origin}/mcp`]) }),
73
+ );
74
+ })();
75
+ }
76
+
77
+ interface GrantContext {
78
+ readonly index: number;
79
+ readonly at: (offsetMs: number) => string;
80
+ readonly resource: string;
81
+ }
82
+
83
+ function seedGrant(
84
+ sqlite: Database,
85
+ grant: PrePackageGrant,
86
+ ctx: GrantContext,
87
+ ): SeededPrePackageGrant {
88
+ const clientID = `client-${ctx.index}`;
89
+ const grantID = `code-${ctx.index}`;
90
+ const scopes = JSON.stringify([...grant.scopes, 'offline_access']);
91
+ const createdAt = ctx.at(-2 * HOUR_MS);
92
+ const accessExpiresAt = grant.isExpired === true ? ctx.at(-HOUR_MS) : ctx.at(HOUR_MS);
93
+ const refreshExpiresAt = grant.isExpired === true ? ctx.at(-HOUR_MS) : ctx.at(30 * 24 * HOUR_MS);
94
+
95
+ sqlite.run(
96
+ `INSERT INTO oauthClient (id, clientId, disabled, scopes, clientCredentialsScopes, createdAt, updatedAt, name, redirectUris, tokenEndpointAuthMethod, applicationType, grantTypes, responseTypes, requirePKCE, dpopBoundAccessTokens) VALUES (?, ?, 0, '["read","write","offline_access"]', '[]', ?, ?, ?, '["https://client.example/callback"]', 'none', 'web', '["authorization_code","refresh_token"]', '["code"]', 1, 0)`,
97
+ [`row-${clientID}`, clientID, createdAt, createdAt, grant.clientName],
98
+ );
99
+
100
+ sqlite.run(
101
+ `INSERT INTO oauthConsent (id, clientId, userId, resources, requestedUserInfoClaims, scopes, createdAt, updatedAt) VALUES (?, ?, ?, ?, '[]', ?, ?, ?)`,
102
+ [`consent-${clientID}`, clientID, OWNER_ID, ctx.resource, scopes, createdAt, createdAt],
103
+ );
104
+
105
+ for (let pair = 0; pair <= grant.refreshes; pair++) {
106
+ const refreshID = `refresh-${grantID}-${pair}`;
107
+ const rotatedAt = pair < grant.refreshes ? createdAt : null;
108
+
109
+ sqlite.run(
110
+ `INSERT INTO oauthRefreshToken (id, token, clientId, userId, authorizationCodeId, resources, expiresAt, createdAt, revoked, rotatedAt, authTime, scopes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
111
+ [
112
+ refreshID,
113
+ toTokenHash(`refresh-token-${grantID}-${pair}`),
114
+ clientID,
115
+ OWNER_ID,
116
+ grantID,
117
+ ctx.resource,
118
+ refreshExpiresAt,
119
+ createdAt,
120
+ rotatedAt,
121
+ rotatedAt,
122
+ createdAt,
123
+ scopes,
124
+ ],
125
+ );
126
+
127
+ sqlite.run(
128
+ `INSERT INTO oauthAccessToken (id, token, clientId, userId, authorizationCodeId, resources, refreshId, expiresAt, createdAt, scopes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
129
+ [
130
+ `access-${grantID}-${pair}`,
131
+ toTokenHash(`access-token-${grantID}-${pair}`),
132
+ clientID,
133
+ OWNER_ID,
134
+ grantID,
135
+ ctx.resource,
136
+ refreshID,
137
+ accessExpiresAt,
138
+ createdAt,
139
+ scopes,
140
+ ],
141
+ );
142
+ }
143
+
144
+ if (grant.lastUsedAt !== null) {
145
+ sqlite.run(`INSERT INTO boxxy_grant_use (grant_id, last_used_at) VALUES (?, ?)`, [
146
+ grantID,
147
+ grant.lastUsedAt,
148
+ ]);
149
+ }
150
+
151
+ return {
152
+ grantID,
153
+ clientID,
154
+ accessToken: `access-token-${grantID}-${grant.refreshes}`,
155
+ refreshToken: `refresh-token-${grantID}-${grant.refreshes}`,
156
+ };
157
+ }
158
+
159
+ // better-auth stores each token as its SHA-256 digest, in base64url.
160
+ function toTokenHash(token: string): string {
161
+ return createHash('sha256').update(token).digest('base64url');
162
+ }
@@ -1,5 +1,3 @@
1
- // Copied from atc: packages/test-utils/src/setup-temp-dir.ts at commit 4fe647cf.
2
-
3
1
  import { mkdtempSync, rmSync } from 'node:fs';
4
2
  import { tmpdir } from 'node:os';
5
3
  import { join } from 'node:path';
@@ -1,5 +1,3 @@
1
- // Copied from atc: packages/core/src/types.ts at commit 4fe647cf.
2
-
3
1
  export interface AuthClock {
4
2
  readonly now: () => number;
5
3
 
@@ -1,2 +0,0 @@
1
- import type { HTTPServerContext, Requester } from './types.ts';
2
- export declare function answerAuthorizeRequest(ctx: HTTPServerContext, url: URL, requester: Requester): Promise<Response>;
@@ -1,10 +0,0 @@
1
- import type { HTTPServerContext, Requester } from './types.ts';
2
- interface ConsentRequest {
3
- readonly method: 'GET' | 'POST';
4
- readonly url: URL;
5
- readonly cookie: string | null;
6
- readonly body: string;
7
- readonly requester: Requester;
8
- }
9
- export declare function answerConsentRequest(ctx: HTTPServerContext, request: ConsentRequest): Promise<Response>;
10
- export {};
@@ -1,9 +0,0 @@
1
- import type { HTTPServerContext, Requester } from './types.ts';
2
- interface EnrollRequest {
3
- readonly method: 'GET' | 'POST';
4
- readonly url: URL;
5
- readonly body: string;
6
- readonly requester: Requester;
7
- }
8
- export declare function answerEnrollRequest(ctx: HTTPServerContext, request: EnrollRequest): Promise<Response>;
9
- export {};
@@ -1,9 +0,0 @@
1
- import type { HTTPServerContext, Requester } from './types.ts';
2
- interface LoginRequest {
3
- readonly method: 'GET' | 'POST';
4
- readonly url: URL;
5
- readonly body: string;
6
- readonly requester: Requester;
7
- }
8
- export declare function answerLoginRequest(ctx: HTTPServerContext, request: LoginRequest): Promise<Response>;
9
- export {};
@@ -1 +0,0 @@
1
- export declare const APPROVAL_WINDOW_SECONDS = 600;
@@ -1 +0,0 @@
1
- export declare function buildConsentBinding(oauthQuery: string): string;
@@ -1,41 +0,0 @@
1
- interface ApprovalRequest {
2
- readonly key: string;
3
- readonly clientID: string;
4
- readonly clientName: string;
5
- readonly redirectURI: string;
6
- }
7
- interface PendingApproval extends ApprovalRequest {
8
- readonly correlationID: string;
9
- failures: number;
10
- readonly expiresAt: number;
11
- challenge: {
12
- readonly value: string;
13
- readonly expiresAt: number;
14
- } | null;
15
- }
16
- export type TakenChallenge = {
17
- readonly kind: 'taken';
18
- readonly challenge: string;
19
- } | {
20
- readonly kind: 'expired';
21
- } | {
22
- readonly kind: 'missing';
23
- };
24
- interface ApprovedRequest {
25
- readonly binding: string;
26
- readonly correlationID: string;
27
- readonly expiresAt: number;
28
- }
29
- export interface ApprovalState {
30
- readonly createPending: (request: ApprovalRequest) => PendingApproval | null;
31
- readonly findPending: (key: string) => PendingApproval | null;
32
- readonly mintChallenge: (key: string) => string | null;
33
- readonly claimChallenge: (key: string) => TakenChallenge;
34
- readonly recordFailure: (key: string) => 'retry' | 'locked';
35
- readonly removePending: (key: string) => void;
36
- readonly recordApproved: (sessionID: string, approved: Omit<ApprovedRequest, 'expiresAt'>) => void;
37
- readonly findApproved: (sessionID: string) => ApprovedRequest | null;
38
- readonly removeApproved: (sessionID: string) => void;
39
- }
40
- export declare function createApprovalState(pendingMs: number, now: () => number): ApprovalState;
41
- export {};
@@ -1,6 +0,0 @@
1
- import type { TakenChallenge } from './create-approval-state.ts';
2
- export interface EnrollmentState {
3
- readonly mintChallenge: (tokenHash: string) => string;
4
- readonly claimChallenge: (tokenHash: string) => TakenChallenge;
5
- }
6
- export declare function createEnrollmentState(now: () => number): EnrollmentState;
@@ -1,6 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export declare function createEnrollmentToken(db: Kysely<MCPAuthSchema>, now: number): Promise<{
4
- readonly token: string;
5
- readonly expiresAt: number;
6
- }>;
@@ -1,38 +0,0 @@
1
- import { z } from 'zod';
2
- export declare function createOwnerPlugin(): {
3
- id: "boxxy-owner";
4
- endpoints: {
5
- signInOwner: import("better-auth").StrictEndpoint<"/boxxy/sign-in-owner", {
6
- method: "POST";
7
- body: z.ZodObject<{
8
- oauth_query: z.ZodOptional<z.ZodString>;
9
- }, z.core.$strip>;
10
- metadata: {
11
- SERVER_ONLY: true;
12
- };
13
- }, {
14
- signedIn: boolean;
15
- }>;
16
- verifyMCPAccessToken: import("better-auth").StrictEndpoint<"/boxxy/verify-access-token", {
17
- method: "POST";
18
- body: z.ZodObject<{
19
- token: z.ZodString;
20
- }, z.core.$strip>;
21
- metadata: {
22
- SERVER_ONLY: true;
23
- };
24
- }, import("@better-auth/oauth-provider").ActiveAccessTokenPayload>;
25
- createFixedClient: import("better-auth").StrictEndpoint<"/boxxy/create-fixed-client", {
26
- method: "POST";
27
- body: z.ZodObject<{
28
- name: z.ZodString;
29
- redirectURIs: z.ZodArray<z.ZodString>;
30
- }, z.core.$strip>;
31
- metadata: {
32
- SERVER_ONLY: true;
33
- };
34
- }, {
35
- clientID: string;
36
- }>;
37
- };
38
- };
@@ -1,11 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export interface NewPasskey {
4
- readonly tokenHash: string;
5
- readonly id: string;
6
- readonly publicKey: string;
7
- readonly counter: number;
8
- readonly transports: readonly string[];
9
- readonly now: number;
10
- }
11
- export declare function createPasskey(db: Kysely<MCPAuthSchema>, passkey: NewPasskey): Promise<boolean>;
@@ -1 +0,0 @@
1
- export declare function deriveTokenHash(token: string): string;
@@ -1 +0,0 @@
1
- export declare function isAllowedRedirectURI(raw: string): boolean;
@@ -1,6 +0,0 @@
1
- import type { verifyAuthenticationResponse, verifyRegistrationResponse } from '@simplewebauthn/server';
2
- export interface WebAuthn {
3
- readonly verifyAuthenticationResponse: typeof verifyAuthenticationResponse;
4
- readonly verifyRegistrationResponse: typeof verifyRegistrationResponse;
5
- }
6
- export declare function loadWebAuthn(): Promise<WebAuthn>;
@@ -1,5 +0,0 @@
1
- import type { Dialect } from 'kysely';
2
- export interface MCPAuthDatabase {
3
- readonly dialect: Dialect;
4
- readonly close: () => void;
5
- }
@@ -1,54 +0,0 @@
1
- export interface MCPAuthSchema {
2
- readonly oauthClient: {
3
- readonly id: string;
4
- readonly clientId: string;
5
- readonly name: string | null;
6
- readonly redirectUris: string;
7
- readonly createdAt: string | null;
8
- };
9
- readonly oauthAccessToken: {
10
- readonly id: string;
11
- readonly token: string;
12
- readonly clientId: string;
13
- readonly sessionId: string | null;
14
- readonly authorizationCodeId: string | null;
15
- };
16
- readonly oauthRefreshToken: {
17
- readonly id: string;
18
- readonly clientId: string;
19
- readonly sessionId: string | null;
20
- readonly authorizationCodeId: string | null;
21
- readonly scopes: string;
22
- readonly createdAt: string;
23
- readonly expiresAt: string;
24
- readonly revoked: string | null;
25
- };
26
- readonly oauthConsent: {
27
- readonly id: string;
28
- readonly clientId: string;
29
- };
30
- readonly oauthResource: {
31
- readonly identifier: string;
32
- };
33
- readonly session: {
34
- readonly id: string;
35
- readonly expiresAt: string;
36
- };
37
- readonly boxxy_grant_use: {
38
- readonly grant_id: string;
39
- readonly last_used_at: string;
40
- };
41
- readonly boxxy_passkey: {
42
- readonly id: string;
43
- readonly public_key: string;
44
- readonly counter: number;
45
- readonly transports: string;
46
- readonly created_at: string;
47
- readonly last_used_at: string | null;
48
- };
49
- readonly boxxy_enrollment_token: {
50
- readonly token_hash: string;
51
- readonly created_at: string;
52
- readonly expires_at: string;
53
- };
54
- }
@@ -1,80 +0,0 @@
1
- import type { BetterAuthPlugin } from 'better-auth';
2
- import { Kysely } from 'kysely';
3
- import type { MCPAuthDatabase } from './mcp-auth-database.ts';
4
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
5
- interface MCPAuthOptions {
6
- readonly dbPath: string;
7
- readonly origin: string | null;
8
- readonly refreshReuseSeconds?: number;
9
- readonly openDatabase: (path: string) => MCPAuthDatabase;
10
- }
11
- export declare function openMCPAuth(options: MCPAuthOptions): Promise<{
12
- auth: import("better-auth").Auth<{
13
- baseURL: string;
14
- basePath: string;
15
- secret: string;
16
- database: {
17
- db: Kysely<MCPAuthSchema>;
18
- type: "sqlite";
19
- };
20
- telemetry: {
21
- enabled: false;
22
- };
23
- logger: {
24
- level: "error";
25
- };
26
- rateLimit: {
27
- enabled: false;
28
- };
29
- advanced: {
30
- database: {
31
- validateSchema: false;
32
- };
33
- };
34
- trustedOrigins: string[];
35
- session: {
36
- expiresIn: number;
37
- disableSessionRefresh: true;
38
- };
39
- plugins: [BetterAuthPlugin, {
40
- id: "boxxy-owner";
41
- endpoints: {
42
- signInOwner: import("better-auth").StrictEndpoint<"/boxxy/sign-in-owner", {
43
- method: "POST";
44
- body: import("zod").ZodObject<{
45
- oauth_query: import("zod").ZodOptional<import("zod").ZodString>;
46
- }, import("zod/v4/core").$strip>;
47
- metadata: {
48
- SERVER_ONLY: true;
49
- };
50
- }, {
51
- signedIn: boolean;
52
- }>;
53
- verifyMCPAccessToken: import("better-auth").StrictEndpoint<"/boxxy/verify-access-token", {
54
- method: "POST";
55
- body: import("zod").ZodObject<{
56
- token: import("zod").ZodString;
57
- }, import("zod/v4/core").$strip>;
58
- metadata: {
59
- SERVER_ONLY: true;
60
- };
61
- }, import("@better-auth/oauth-provider").ActiveAccessTokenPayload>;
62
- createFixedClient: import("better-auth").StrictEndpoint<"/boxxy/create-fixed-client", {
63
- method: "POST";
64
- body: import("zod").ZodObject<{
65
- name: import("zod").ZodString;
66
- redirectURIs: import("zod").ZodArray<import("zod").ZodString>;
67
- }, import("zod/v4/core").$strip>;
68
- metadata: {
69
- SERVER_ONLY: true;
70
- };
71
- }, {
72
- clientID: string;
73
- }>;
74
- };
75
- }];
76
- }>;
77
- db: Kysely<MCPAuthSchema>;
78
- close(): Promise<void>;
79
- }>;
80
- export {};
@@ -1,4 +0,0 @@
1
- export declare const PASSKEY_SCRIPT: {
2
- readonly source: "(() => {\n const data = document.getElementById('boxxy-passkey');\n const form = document.getElementById('boxxy-passkey-form');\n const button = document.getElementById('boxxy-passkey-start');\n const status = document.getElementById('boxxy-passkey-status');\n if (data === null || form === null || button === null || status === null) return;\n const spec = JSON.parse(data.textContent);\n const toBytes = (value) => {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));\n };\n const toText = (buffer) => {\n let text = '';\n for (const byte of new Uint8Array(buffer)) text += String.fromCharCode(byte);\n return btoa(text).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n };\n const toDescriptors = (list) => (list || []).map((item) => ({ ...item, id: toBytes(item.id) }));\n button.addEventListener('click', async () => {\n button.disabled = true;\n status.textContent = '';\n try {\n const options = spec.options;\n const credential = spec.mode === 'create'\n ? await navigator.credentials.create({ publicKey: { ...options, challenge: toBytes(options.challenge), user: { ...options.user, id: toBytes(options.user.id) }, excludeCredentials: toDescriptors(options.excludeCredentials) } })\n : await navigator.credentials.get({ publicKey: { ...options, challenge: toBytes(options.challenge), allowCredentials: toDescriptors(options.allowCredentials) } });\n const reply = credential.response;\n const response = spec.mode === 'create'\n ? { clientDataJSON: toText(reply.clientDataJSON), attestationObject: toText(reply.attestationObject), transports: typeof reply.getTransports === 'function' ? reply.getTransports() : [] }\n : { clientDataJSON: toText(reply.clientDataJSON), authenticatorData: toText(reply.authenticatorData), signature: toText(reply.signature), userHandle: reply.userHandle ? toText(reply.userHandle) : undefined };\n form.elements.namedItem('credential').value = JSON.stringify({ id: credential.id, rawId: toText(credential.rawId), type: credential.type, response, clientExtensionResults: credential.getClientExtensionResults(), authenticatorAttachment: credential.authenticatorAttachment || undefined });\n form.submit();\n } catch (error) {\n status.textContent = 'The passkey prompt ended without a passkey (' + (error && error.name ? error.name : 'Error') + '). Try again.';\n button.disabled = false;\n }\n });\n})();";
3
- readonly policySource: `'sha256-${string}'`;
4
- };
@@ -1,3 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export declare function readClientName(db: Kysely<MCPAuthSchema>, clientID: string): Promise<string | null>;
@@ -1,9 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export interface ClientView {
4
- readonly clientID: string;
5
- readonly name: string;
6
- readonly redirectURIs: readonly string[];
7
- readonly createdAt: string | null;
8
- }
9
- export declare function readClientViews(db: Kysely<MCPAuthSchema>): Promise<readonly ClientView[]>;
@@ -1,3 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export declare function readEnrollmentToken(db: Kysely<MCPAuthSchema>, token: string, now: number): Promise<string | null>;
@@ -1,12 +0,0 @@
1
- import type { GrantScope } from '@boxxy/contract';
2
- import type { Kysely } from 'kysely';
3
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
4
- export interface GrantView {
5
- readonly grantID: string;
6
- readonly clientID: string;
7
- readonly clientName: string;
8
- readonly scopes: readonly GrantScope[];
9
- readonly createdAt: string;
10
- readonly lastUsedAt: string | null;
11
- }
12
- export declare function readGrantViews(db: Kysely<MCPAuthSchema>): Promise<readonly GrantView[]>;
@@ -1,2 +0,0 @@
1
- import type { HTTPServerContext } from './types.ts';
2
- export declare function readOwnerSessionID(ctx: HTTPServerContext, cookie: string | null): Promise<string | null>;
@@ -1,10 +0,0 @@
1
- import type { HTTPServerContext, Requester } from './types.ts';
2
- interface RefusedExchange {
3
- readonly contentType: string | null;
4
- readonly body: string;
5
- readonly response: Response;
6
- readonly path: string;
7
- readonly requester: Requester;
8
- }
9
- export declare function recordRefusedExchange(ctx: Pick<HTTPServerContext, 'store' | 'security'>, exchange: RefusedExchange): Promise<void>;
10
- export {};
@@ -1,3 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export declare function removeClient(db: Kysely<MCPAuthSchema>, clientID: string): Promise<boolean>;
@@ -1,3 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export declare function removeOwnerSession(db: Kysely<MCPAuthSchema>, sessionID: string): Promise<void>;
@@ -1,9 +0,0 @@
1
- import type { GrantScope } from '@boxxy/contract';
2
- interface ConsentPageView {
3
- readonly oauthQuery: string;
4
- readonly clientName: string;
5
- readonly redirectURI: string;
6
- readonly scopes: readonly GrantScope[];
7
- }
8
- export declare function renderConsentPage(view: ConsentPageView): string;
9
- export {};
@@ -1,10 +0,0 @@
1
- interface EnrollPageView {
2
- readonly ceremony: {
3
- readonly token: string;
4
- readonly options: unknown;
5
- } | null;
6
- readonly error: string | null;
7
- readonly prefill?: string;
8
- }
9
- export declare function renderEnrollPage(view: EnrollPageView): string;
10
- export {};
@@ -1,9 +0,0 @@
1
- interface LoginPageView {
2
- readonly oauthQuery: string;
3
- readonly clientName: string;
4
- readonly redirectURI: string;
5
- readonly options: unknown;
6
- readonly error: string | null;
7
- }
8
- export declare function renderLoginPage(view: LoginPageView): string;
9
- export {};
@@ -1,3 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { MCPAuthSchema } from './mcp-auth-schema.ts';
3
- export declare function revokeGrant(db: Kysely<MCPAuthSchema>, grantID: string): Promise<boolean>;
@@ -1 +0,0 @@
1
- export declare function toHTMLText(text: string): string;
@@ -1 +0,0 @@
1
- export declare function toScriptJSON(value: unknown): string;