@boxxy/auth-oauth 0.0.1 → 0.2.2

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 (142) hide show
  1. package/README.md +47 -2
  2. package/dist/auth/answer-authorize-request.d.ts +2 -0
  3. package/dist/auth/answer-consent-request.d.ts +10 -0
  4. package/dist/auth/answer-enroll-request.d.ts +9 -0
  5. package/dist/auth/answer-login-request.d.ts +9 -0
  6. package/dist/auth/approval-window-seconds.d.ts +1 -0
  7. package/dist/auth/build-consent-binding.d.ts +1 -0
  8. package/dist/auth/create-approval-state.d.ts +41 -0
  9. package/dist/auth/create-auth-admin.d.ts +33 -0
  10. package/dist/auth/create-enrollment-state.d.ts +6 -0
  11. package/dist/auth/create-enrollment-token.d.ts +6 -0
  12. package/dist/auth/create-owner-plugin.d.ts +38 -0
  13. package/dist/auth/create-passkey.d.ts +11 -0
  14. package/dist/auth/create-security-event-log.d.ts +12 -0
  15. package/dist/auth/derive-token-hash.d.ts +1 -0
  16. package/dist/auth/format-security-event.d.ts +7 -0
  17. package/dist/auth/is-allowed-redirect-uri.d.ts +1 -0
  18. package/dist/auth/load-web-authn.d.ts +6 -0
  19. package/dist/auth/mcp-auth-database.d.ts +5 -0
  20. package/dist/auth/mcp-auth-schema.d.ts +54 -0
  21. package/dist/auth/normalize-client-name.d.ts +1 -0
  22. package/dist/auth/open-auth-database.d.ts +2 -0
  23. package/dist/auth/open-mcp-auth.d.ts +80 -0
  24. package/dist/auth/passkey-script.d.ts +4 -0
  25. package/dist/auth/read-client-name.d.ts +3 -0
  26. package/dist/auth/read-client-views.d.ts +9 -0
  27. package/dist/auth/read-enrollment-token.d.ts +3 -0
  28. package/dist/auth/read-grant-views.d.ts +12 -0
  29. package/dist/auth/read-owner-session-id.d.ts +2 -0
  30. package/dist/auth/record-refused-exchange.d.ts +10 -0
  31. package/dist/auth/remove-client.d.ts +3 -0
  32. package/dist/auth/remove-owner-session.d.ts +3 -0
  33. package/dist/auth/render-consent-page.d.ts +9 -0
  34. package/dist/auth/render-enroll-page.d.ts +10 -0
  35. package/dist/auth/render-login-page.d.ts +9 -0
  36. package/dist/auth/revoke-grant.d.ts +3 -0
  37. package/dist/auth/to-html-text.d.ts +1 -0
  38. package/dist/auth/to-script-json.d.ts +1 -0
  39. package/dist/auth/types.d.ts +79 -0
  40. package/dist/auth/update-passkey-use.d.ts +10 -0
  41. package/dist/auth/verify-oauth-query.d.ts +1 -0
  42. package/dist/auth/verify-passkey-assertion.d.ts +11 -0
  43. package/dist/auth/verify-passkey-registration.d.ts +14 -0
  44. package/dist/auth/verify-session-passkey.d.ts +8 -0
  45. package/dist/auth/write-grant-use.d.ts +3 -0
  46. package/dist/http/answer-mcp-request.d.ts +9 -0
  47. package/dist/http/build-page-response.d.ts +5 -0
  48. package/dist/http/create-mcp-http-app.d.ts +92 -0
  49. package/dist/http/create-public-door.d.ts +20 -0
  50. package/dist/http/is-loopback-host.d.ts +1 -0
  51. package/dist/http/normalize-public-url.d.ts +1 -0
  52. package/dist/http/pick-error-message.d.ts +1 -0
  53. package/dist/http/public-door-paths.d.ts +17 -0
  54. package/dist/http/read-token-access.d.ts +8 -0
  55. package/dist/http/verify-access-token.d.ts +2 -0
  56. package/dist/index.d.ts +8 -0
  57. package/dist/test-utils/authorize-test-client.d.ts +13 -0
  58. package/dist/test-utils/build-mock-new-passkey.d.ts +2 -0
  59. package/dist/test-utils/build-passkey-seeder.d.ts +5 -0
  60. package/dist/test-utils/build-seeder.d.ts +5 -0
  61. package/dist/test-utils/build-stub-authenticator.d.ts +48 -0
  62. package/dist/test-utils/build-stub-clock.d.ts +7 -0
  63. package/dist/test-utils/load-web-authn-early.d.ts +1 -0
  64. package/dist/test-utils/register-test-cleanup.d.ts +1 -0
  65. package/dist/test-utils/seed-passkey.d.ts +4 -0
  66. package/dist/test-utils/setup-temp-dir.d.ts +6 -0
  67. package/dist/test-utils/start-stub-telemetry-collector.d.ts +5 -0
  68. package/dist/test-utils/types.d.ts +7 -0
  69. package/dist/utils/is-record.d.ts +1 -0
  70. package/dist/utils/parse-json-strings.d.ts +1 -0
  71. package/dist/utils/types.d.ts +4 -0
  72. package/package.json +42 -1
  73. package/src/auth/answer-authorize-request.ts +137 -0
  74. package/src/auth/answer-consent-request.ts +189 -0
  75. package/src/auth/answer-enroll-request.ts +190 -0
  76. package/src/auth/answer-login-request.ts +256 -0
  77. package/src/auth/approval-window-seconds.ts +5 -0
  78. package/src/auth/build-consent-binding.ts +13 -0
  79. package/src/auth/create-approval-state.ts +221 -0
  80. package/src/auth/create-auth-admin.ts +78 -0
  81. package/src/auth/create-enrollment-state.ts +56 -0
  82. package/src/auth/create-enrollment-token.ts +28 -0
  83. package/src/auth/create-owner-plugin.ts +113 -0
  84. package/src/auth/create-passkey.ts +48 -0
  85. package/src/auth/create-security-event-log.ts +97 -0
  86. package/src/auth/derive-token-hash.ts +7 -0
  87. package/src/auth/format-security-event.ts +72 -0
  88. package/src/auth/is-allowed-redirect-uri.ts +19 -0
  89. package/src/auth/load-web-authn.ts +19 -0
  90. package/src/auth/mcp-auth-database.ts +10 -0
  91. package/src/auth/mcp-auth-schema.ts +56 -0
  92. package/src/auth/normalize-client-name.ts +22 -0
  93. package/src/auth/open-auth-database.ts +22 -0
  94. package/src/auth/open-mcp-auth.ts +138 -0
  95. package/src/auth/passkey-script.ts +50 -0
  96. package/src/auth/read-client-name.ts +17 -0
  97. package/src/auth/read-client-views.ts +27 -0
  98. package/src/auth/read-enrollment-token.ts +20 -0
  99. package/src/auth/read-grant-views.ts +53 -0
  100. package/src/auth/read-owner-session-id.ts +16 -0
  101. package/src/auth/record-refused-exchange.ts +74 -0
  102. package/src/auth/remove-client.ts +33 -0
  103. package/src/auth/remove-owner-session.ts +11 -0
  104. package/src/auth/render-consent-page.ts +61 -0
  105. package/src/auth/render-enroll-page.ts +59 -0
  106. package/src/auth/render-login-page.ts +58 -0
  107. package/src/auth/revoke-grant.ts +25 -0
  108. package/src/auth/to-html-text.ts +10 -0
  109. package/src/auth/to-script-json.ts +8 -0
  110. package/src/auth/types.ts +115 -0
  111. package/src/auth/update-passkey-use.ts +31 -0
  112. package/src/auth/verify-oauth-query.ts +41 -0
  113. package/src/auth/verify-passkey-assertion.ts +124 -0
  114. package/src/auth/verify-passkey-registration.ts +118 -0
  115. package/src/auth/verify-session-passkey.ts +47 -0
  116. package/src/auth/write-grant-use.ts +18 -0
  117. package/src/http/answer-mcp-request.ts +77 -0
  118. package/src/http/build-page-response.ts +26 -0
  119. package/src/http/create-mcp-http-app.ts +340 -0
  120. package/src/http/create-public-door.ts +150 -0
  121. package/src/http/is-loopback-host.ts +10 -0
  122. package/src/http/normalize-public-url.ts +29 -0
  123. package/src/http/pick-error-message.ts +21 -0
  124. package/src/http/public-door-paths.ts +21 -0
  125. package/src/http/read-token-access.ts +44 -0
  126. package/src/http/verify-access-token.ts +46 -0
  127. package/src/index.ts +8 -0
  128. package/src/test-utils/authorize-test-client.ts +194 -0
  129. package/src/test-utils/build-mock-new-passkey.ts +15 -0
  130. package/src/test-utils/build-passkey-seeder.ts +11 -0
  131. package/src/test-utils/build-seeder.ts +10 -0
  132. package/src/test-utils/build-stub-authenticator.ts +244 -0
  133. package/src/test-utils/build-stub-clock.ts +65 -0
  134. package/src/test-utils/load-web-authn-early.ts +8 -0
  135. package/src/test-utils/register-test-cleanup.ts +55 -0
  136. package/src/test-utils/seed-passkey.ts +32 -0
  137. package/src/test-utils/setup-temp-dir.ts +24 -0
  138. package/src/test-utils/start-stub-telemetry-collector.ts +27 -0
  139. package/src/test-utils/types.ts +11 -0
  140. package/src/utils/is-record.ts +5 -0
  141. package/src/utils/parse-json-strings.ts +15 -0
  142. package/src/utils/types.ts +8 -0
@@ -0,0 +1,65 @@
1
+ // Copied from atc: packages/test-utils/src/build-stub-clock.ts at commit 4fe647cf.
2
+
3
+ import type { AuthClock } from '../utils/types.ts';
4
+
5
+ interface StubTimer {
6
+ readonly dueAt: number;
7
+ readonly callback: () => void;
8
+ }
9
+
10
+ export function buildStubClock(startMs: number) {
11
+ let time = startMs;
12
+ let nextID = 0;
13
+
14
+ const timers = new Map<number, StubTimer>();
15
+
16
+ const delays: number[] = [];
17
+
18
+ const findDue = (until: number): [number, StubTimer] | null => {
19
+ let due: [number, StubTimer] | null = null;
20
+
21
+ for (const entry of timers) {
22
+ if (entry[1].dueAt <= until && (due === null || entry[1].dueAt < due[1].dueAt)) {
23
+ due = entry;
24
+ }
25
+ }
26
+
27
+ return due;
28
+ };
29
+
30
+ const clock: AuthClock = {
31
+ now: () => time,
32
+ schedule: (callback, delayMs) => {
33
+ const id = nextID++;
34
+
35
+ delays.push(delayMs);
36
+ timers.set(id, { dueAt: time + Math.max(0, delayMs), callback });
37
+
38
+ return () => {
39
+ timers.delete(id);
40
+ };
41
+ },
42
+ };
43
+
44
+ return {
45
+ ...clock,
46
+ advance(durationMs: number): void {
47
+ const until = time + durationMs;
48
+
49
+ for (let due = findDue(until); due !== null; due = findDue(until)) {
50
+ timers.delete(due[0]);
51
+
52
+ time = due[1].dueAt;
53
+
54
+ due[1].callback();
55
+ }
56
+
57
+ time = until;
58
+ },
59
+ collectPending: (): number[] =>
60
+ [...timers.values()]
61
+ .map((timer) => timer.dueAt - time)
62
+ .toSorted((left, right) => left - right),
63
+ collectDelays: (): number[] => [...delays],
64
+ };
65
+ }
@@ -0,0 +1,8 @@
1
+ // Copied from atc: packages/mcp/src/testing/load-web-authn-early.ts at commit 4fe647cf.
2
+
3
+ import { loadWebAuthn } from '../auth/load-web-authn.ts';
4
+
5
+ // The first passkey ceremony in a test process loads the WebAuthn library,
6
+ // measured at about 0.26 s cold against 0.015 s warm. Loading it here keeps
7
+ // that one-time cost out of the first test's timeout.
8
+ await loadWebAuthn();
@@ -0,0 +1,55 @@
1
+ // Copied from atc: packages/test-utils/src/register-test-cleanup.ts at commit 4fe647cf.
2
+
3
+ import { onTestFinished } from 'bun:test';
4
+
5
+ // The releases the current test registered, in registration order; null
6
+ // before its first registration and once its hook has started.
7
+ let pending: (() => unknown)[] | null = null;
8
+
9
+ export function registerTestCleanup<T>(release: () => T): () => T {
10
+ let released: { readonly value: T } | null = null;
11
+
12
+ const releaseOnce = (): T => {
13
+ released ??= { value: release() };
14
+
15
+ return released.value;
16
+ };
17
+
18
+ if (pending === null) {
19
+ // Bun runs this hook after every afterEach, so a release works only from the
20
+ // paths and handles it captured.
21
+ onTestFinished(releasePending);
22
+
23
+ pending = [];
24
+ }
25
+
26
+ pending.push(releaseOnce);
27
+
28
+ return releaseOnce;
29
+ }
30
+
31
+ async function releasePending(): Promise<void> {
32
+ const releases = pending ?? [];
33
+
34
+ pending = null;
35
+
36
+ const errors: unknown[] = [];
37
+
38
+ // Last registered first, so a directory made before a process that lives in it
39
+ // goes only once that process stops.
40
+ for (const release of releases.toReversed()) {
41
+ try {
42
+ await release();
43
+ } catch (error) {
44
+ errors.push(error);
45
+ }
46
+ }
47
+
48
+ if (errors.length === 1) {
49
+ throw errors[0];
50
+ }
51
+
52
+ if (errors.length > 1) {
53
+ throw new AggregateError(errors, 'more than one test cleanup failed');
54
+ }
55
+ }
@@ -0,0 +1,32 @@
1
+ // Copied from atc: packages/mcp/src/test-utils/seed-passkey.ts at commit 4fe647cf.
2
+
3
+ import invariant from 'tiny-invariant';
4
+ import { createEnrollmentToken } from '../auth/create-enrollment-token.ts';
5
+ import { createPasskey } from '../auth/create-passkey.ts';
6
+ import type { NewPasskey } from '../auth/create-passkey.ts';
7
+ import { deriveTokenHash } from '../auth/derive-token-hash.ts';
8
+ import { buildMockNewPasskey } from './build-mock-new-passkey.ts';
9
+ import type { MCPAuthSeedDeps } from './types.ts';
10
+
11
+ export type PasskeyInput = Partial<Omit<NewPasskey, 'tokenHash'>>;
12
+
13
+ export async function seedPasskey(
14
+ deps: MCPAuthSeedDeps,
15
+ input: PasskeyInput = {},
16
+ ): Promise<NewPasskey> {
17
+ const now = input.now ?? deps.clock.now();
18
+
19
+ const enrollment = await createEnrollmentToken(deps.db, now);
20
+
21
+ const passkey = buildMockNewPasskey({
22
+ ...input,
23
+ now,
24
+ tokenHash: deriveTokenHash(enrollment.token),
25
+ });
26
+
27
+ const created = await createPasskey(deps.db, passkey);
28
+
29
+ invariant(created);
30
+
31
+ return passkey;
32
+ }
@@ -0,0 +1,24 @@
1
+ // Copied from atc: packages/test-utils/src/setup-temp-dir.ts at commit 4fe647cf.
2
+
3
+ import { mkdtempSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { registerTestCleanup } from './register-test-cleanup';
7
+
8
+ interface TempDir {
9
+ readonly path: string;
10
+ readonly teardown: () => void;
11
+ }
12
+
13
+ export function setupTempDir(prefix: string): TempDir {
14
+ const directory = mkdtempSync(join(tmpdir(), prefix));
15
+
16
+ const teardown = registerTestCleanup(() => {
17
+ rmSync(directory, { recursive: true, force: true });
18
+ });
19
+
20
+ return {
21
+ path: directory,
22
+ teardown,
23
+ };
24
+ }
@@ -0,0 +1,27 @@
1
+ // Copied from atc: packages/test-utils/src/start-stub-telemetry-collector.ts at commit 4fe647cf.
2
+
3
+ import { registerTestCleanup } from './register-test-cleanup.ts';
4
+
5
+ export function startStubTelemetryCollector() {
6
+ const received: string[] = [];
7
+
8
+ const answerRequest = (request: Request) => {
9
+ received.push(request.url);
10
+
11
+ return new Response(null, { status: 204 });
12
+ };
13
+
14
+ const server = Bun.serve({
15
+ hostname: '127.0.0.1',
16
+ port: 0,
17
+ fetch: answerRequest,
18
+ });
19
+
20
+ const stop = registerTestCleanup(() => server.stop(true));
21
+
22
+ return {
23
+ url: `http://127.0.0.1:${String(server.port)}/`,
24
+ received,
25
+ stop,
26
+ };
27
+ }
@@ -0,0 +1,11 @@
1
+ // Copied from atc: packages/mcp/src/test-utils/types.ts at commit 4fe647cf.
2
+
3
+ import type { Kysely } from 'kysely';
4
+ import type { MCPAuthSchema } from '../auth/mcp-auth-schema.ts';
5
+ import type { AuthClock } from '../utils/types.ts';
6
+
7
+ // The handles the seeds of the authorization database take.
8
+ export interface MCPAuthSeedDeps {
9
+ readonly db: Kysely<MCPAuthSchema>;
10
+ readonly clock: Pick<AuthClock, 'now'>;
11
+ }
@@ -0,0 +1,5 @@
1
+ // Copied from atc: packages/core/src/is-record.ts at commit 4fe647cf.
2
+
3
+ export function isRecord(value: unknown): value is Record<string, unknown> {
4
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
5
+ }
@@ -0,0 +1,15 @@
1
+ // Copied from atc: packages/core/src/parse-json-strings.ts at commit 4fe647cf.
2
+
3
+ export function parseJSONStrings(raw: string): string[] | null {
4
+ let parsed: unknown;
5
+
6
+ try {
7
+ parsed = JSON.parse(raw);
8
+ } catch {
9
+ return null;
10
+ }
11
+
12
+ return Array.isArray(parsed)
13
+ ? parsed.filter((item): item is string => typeof item === 'string')
14
+ : null;
15
+ }
@@ -0,0 +1,8 @@
1
+ // Copied from atc: packages/core/src/types.ts at commit 4fe647cf.
2
+
3
+ export interface AuthClock {
4
+ readonly now: () => number;
5
+
6
+ // runs the callback once after the delay; the returned function cancels it
7
+ readonly schedule: (callback: () => void, delayMs: number) => () => void;
8
+ }