@ggui-ai/mcp-server 0.1.0-rc.1

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 (141) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +48 -0
  3. package/dist/admin-blueprints-transport.d.ts +114 -0
  4. package/dist/admin-blueprints-transport.d.ts.map +1 -0
  5. package/dist/admin-blueprints-transport.js +118 -0
  6. package/dist/admin-oauth-providers-transport.d.ts +40 -0
  7. package/dist/admin-oauth-providers-transport.d.ts.map +1 -0
  8. package/dist/admin-oauth-providers-transport.js +263 -0
  9. package/dist/auth.d.ts +39 -0
  10. package/dist/auth.d.ts.map +1 -0
  11. package/dist/auth.js +75 -0
  12. package/dist/build-mcp.d.ts +128 -0
  13. package/dist/build-mcp.d.ts.map +1 -0
  14. package/dist/build-mcp.js +113 -0
  15. package/dist/code-store-fs.d.ts +19 -0
  16. package/dist/code-store-fs.d.ts.map +1 -0
  17. package/dist/code-store-fs.js +98 -0
  18. package/dist/console-auth.d.ts +139 -0
  19. package/dist/console-auth.d.ts.map +1 -0
  20. package/dist/console-auth.js +102 -0
  21. package/dist/console-cache.d.ts +78 -0
  22. package/dist/console-cache.d.ts.map +1 -0
  23. package/dist/console-cache.js +105 -0
  24. package/dist/console-headers.d.ts +124 -0
  25. package/dist/console-headers.d.ts.map +1 -0
  26. package/dist/console-headers.js +49 -0
  27. package/dist/console-llm-trace.d.ts +66 -0
  28. package/dist/console-llm-trace.d.ts.map +1 -0
  29. package/dist/console-llm-trace.js +105 -0
  30. package/dist/console-payloads.d.ts +67 -0
  31. package/dist/console-payloads.d.ts.map +1 -0
  32. package/dist/console-payloads.js +105 -0
  33. package/dist/console-theme-routes.d.ts +111 -0
  34. package/dist/console-theme-routes.d.ts.map +1 -0
  35. package/dist/console-theme-routes.js +202 -0
  36. package/dist/console-timeline.d.ts +45 -0
  37. package/dist/console-timeline.d.ts.map +1 -0
  38. package/dist/console-timeline.js +169 -0
  39. package/dist/console-validator.d.ts +67 -0
  40. package/dist/console-validator.d.ts.map +1 -0
  41. package/dist/console-validator.js +105 -0
  42. package/dist/console-welcome.d.ts +7 -0
  43. package/dist/console-welcome.d.ts.map +1 -0
  44. package/dist/console-welcome.js +221 -0
  45. package/dist/csrf-middleware.d.ts +55 -0
  46. package/dist/csrf-middleware.d.ts.map +1 -0
  47. package/dist/csrf-middleware.js +138 -0
  48. package/dist/email-login.d.ts +174 -0
  49. package/dist/email-login.d.ts.map +1 -0
  50. package/dist/email-login.js +254 -0
  51. package/dist/email-resend.d.ts +29 -0
  52. package/dist/email-resend.d.ts.map +1 -0
  53. package/dist/email-resend.js +71 -0
  54. package/dist/email-sender-from-env.d.ts +34 -0
  55. package/dist/email-sender-from-env.d.ts.map +1 -0
  56. package/dist/email-sender-from-env.js +112 -0
  57. package/dist/email-smtp.d.ts +42 -0
  58. package/dist/email-smtp.d.ts.map +1 -0
  59. package/dist/email-smtp.js +81 -0
  60. package/dist/index.d.ts +102 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +122 -0
  63. package/dist/instructions-presets.d.ts +112 -0
  64. package/dist/instructions-presets.d.ts.map +1 -0
  65. package/dist/instructions-presets.js +195 -0
  66. package/dist/llm-backed-negotiator.d.ts +178 -0
  67. package/dist/llm-backed-negotiator.d.ts.map +1 -0
  68. package/dist/llm-backed-negotiator.js +579 -0
  69. package/dist/logger.d.ts +23 -0
  70. package/dist/logger.d.ts.map +1 -0
  71. package/dist/logger.js +41 -0
  72. package/dist/mcp-apps-inbound.d.ts +86 -0
  73. package/dist/mcp-apps-inbound.d.ts.map +1 -0
  74. package/dist/mcp-apps-inbound.js +278 -0
  75. package/dist/mcp-apps-outbound.d.ts +448 -0
  76. package/dist/mcp-apps-outbound.d.ts.map +1 -0
  77. package/dist/mcp-apps-outbound.js +1163 -0
  78. package/dist/mcp-mounts.d.ts +239 -0
  79. package/dist/mcp-mounts.d.ts.map +1 -0
  80. package/dist/mcp-mounts.js +222 -0
  81. package/dist/oauth-login-types.d.ts +160 -0
  82. package/dist/oauth-login-types.d.ts.map +1 -0
  83. package/dist/oauth-login-types.js +9 -0
  84. package/dist/oauth-login.d.ts +77 -0
  85. package/dist/oauth-login.d.ts.map +1 -0
  86. package/dist/oauth-login.js +455 -0
  87. package/dist/oauth-providers/github.d.ts +17 -0
  88. package/dist/oauth-providers/github.d.ts.map +1 -0
  89. package/dist/oauth-providers/github.js +89 -0
  90. package/dist/oauth-providers/google.d.ts +18 -0
  91. package/dist/oauth-providers/google.d.ts.map +1 -0
  92. package/dist/oauth-providers/google.js +59 -0
  93. package/dist/oauth-providers-store.d.ts +32 -0
  94. package/dist/oauth-providers-store.d.ts.map +1 -0
  95. package/dist/oauth-providers-store.js +291 -0
  96. package/dist/oauth.d.ts +347 -0
  97. package/dist/oauth.d.ts.map +1 -0
  98. package/dist/oauth.js +686 -0
  99. package/dist/pairing-transport.d.ts +99 -0
  100. package/dist/pairing-transport.d.ts.map +1 -0
  101. package/dist/pairing-transport.js +223 -0
  102. package/dist/rate-limit-middleware.d.ts +36 -0
  103. package/dist/rate-limit-middleware.d.ts.map +1 -0
  104. package/dist/rate-limit-middleware.js +57 -0
  105. package/dist/render-gate.d.ts +87 -0
  106. package/dist/render-gate.d.ts.map +1 -0
  107. package/dist/render-gate.js +77 -0
  108. package/dist/render-rate-limit.d.ts +59 -0
  109. package/dist/render-rate-limit.d.ts.map +1 -0
  110. package/dist/render-rate-limit.js +73 -0
  111. package/dist/render-signing.d.ts +98 -0
  112. package/dist/render-signing.d.ts.map +1 -0
  113. package/dist/render-signing.js +113 -0
  114. package/dist/request-context.d.ts +113 -0
  115. package/dist/request-context.d.ts.map +1 -0
  116. package/dist/request-context.js +154 -0
  117. package/dist/reserved-validators.d.ts +22 -0
  118. package/dist/reserved-validators.d.ts.map +1 -0
  119. package/dist/reserved-validators.js +101 -0
  120. package/dist/schema-compat.d.ts +167 -0
  121. package/dist/schema-compat.d.ts.map +1 -0
  122. package/dist/schema-compat.js +187 -0
  123. package/dist/security-headers-middleware.d.ts +38 -0
  124. package/dist/security-headers-middleware.d.ts.map +1 -0
  125. package/dist/security-headers-middleware.js +30 -0
  126. package/dist/server.d.ts +2060 -0
  127. package/dist/server.d.ts.map +1 -0
  128. package/dist/server.js +6338 -0
  129. package/dist/session-channel.d.ts +651 -0
  130. package/dist/session-channel.d.ts.map +1 -0
  131. package/dist/session-channel.js +1756 -0
  132. package/dist/storage.d.ts +89 -0
  133. package/dist/storage.d.ts.map +1 -0
  134. package/dist/storage.js +171 -0
  135. package/dist/thread-transport.d.ts +118 -0
  136. package/dist/thread-transport.d.ts.map +1 -0
  137. package/dist/thread-transport.js +478 -0
  138. package/dist/user-session-auth.d.ts +167 -0
  139. package/dist/user-session-auth.d.ts.map +1 -0
  140. package/dist/user-session-auth.js +148 -0
  141. package/package.json +76 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * OAuth 2.1 + PKCE + Dynamic Client Registration for the MCP server.
3
+ *
4
+ * Implements the auth flow MCP clients (Claude Desktop, claude.ai,
5
+ * Goose, etc.) expect from a remote MCP server per spec 2025-06-18+.
6
+ *
7
+ * ## Flow
8
+ *
9
+ * 1. Client hits `/mcp` without auth → 401 with
10
+ * `WWW-Authenticate: Bearer resource_metadata="<url>"`.
11
+ * 2. Client fetches the resource metadata
12
+ * (`/.well-known/oauth-protected-resource`, RFC 9728) → discovers
13
+ * the authorization server URL.
14
+ * 3. Client fetches the auth server metadata
15
+ * (`/.well-known/oauth-authorization-server`, RFC 8414) →
16
+ * discovers `authorize` / `token` / `register` endpoints.
17
+ * 4. Client POSTs to `/oauth/register` (RFC 7591 Dynamic Client
18
+ * Registration) → server returns a random `client_id`. PKCE-only,
19
+ * so no `client_secret` is issued.
20
+ * 5. Client redirects user-agent to `/oauth/authorize?...&code_challenge=...`
21
+ * → server renders an HTML page asking the user to paste their
22
+ * `ggui_user_*` API key.
23
+ * 6. User pastes key + submits → server validates the key against the
24
+ * configured {@link AuthAdapter} (same adapter that gates `/mcp`),
25
+ * mints an authorization code, redirects back to the client's
26
+ * `redirect_uri` with `?code=...`.
27
+ * 7. Client POSTs to `/oauth/token` with `code` + `code_verifier` →
28
+ * server validates PKCE, returns the user's `ggui_user_*` key as
29
+ * the `access_token`.
30
+ * 8. Client retries `/mcp` with `Authorization: Bearer ggui_user_*`
31
+ * → existing `ApiKeyAuthAdapter` accepts it. ✅
32
+ *
33
+ * ## Why the access token IS the API key
34
+ *
35
+ * The simplest possible bridge between MCP's OAuth-required client UX
36
+ * and ggui's existing API-key auth model. No parallel token table, no
37
+ * key→token translation in the request hot path, no token-refresh
38
+ * dance. The "OAuth flow" becomes a one-time ceremony Claude Desktop
39
+ * runs to capture the user's already-minted key into its own credential
40
+ * storage. After that, every `/mcp` request is identical to a CLI call
41
+ * with `Authorization: Bearer ggui_user_*`.
42
+ *
43
+ * Trade-off: the access token TTL = the API key TTL. If the user
44
+ * revokes the key, Claude Desktop's stored token stops working at the
45
+ * next request (intended: revocation works without OAuth-specific
46
+ * machinery). No refresh token issued — re-auth means re-running the
47
+ * OAuth ceremony, which is a one-paste step.
48
+ *
49
+ * ## Storage
50
+ *
51
+ * Auth codes + DCR clients live in-memory ({@link InMemoryOAuthStorage}).
52
+ * For multi-replica deployments (e.g. `mcp.ggui.ai` with 2+ pods),
53
+ * either:
54
+ * - Use nginx-ingress sticky sessions so the same pod handles both
55
+ * `/oauth/authorize` and `/oauth/token` (current sandbox posture).
56
+ * - Plug a Redis-backed {@link OAuthStorage} via the
57
+ * `oauth.storage` config option (production posture).
58
+ *
59
+ * DCR clients are short-lived in practice — Claude Desktop registers
60
+ * once per install + caches the `client_id`. A pod restart drops all
61
+ * registrations; clients re-register transparently on next failure.
62
+ */
63
+ import type { Request, Response } from 'express';
64
+ import type { AuthAdapter, PairingService } from '@ggui-ai/mcp-server-core';
65
+ export interface OAuthConfig {
66
+ /**
67
+ * Public origin of this server (e.g. `https://mcp.ggui.ai`). Used in
68
+ * discovery metadata, `WWW-Authenticate` headers, and OAuth redirects.
69
+ * Should NOT have a trailing slash. When absent, the server derives
70
+ * it from the request's `Host` header — fine for most deployments,
71
+ * fragile when a proxy rewrites the host.
72
+ */
73
+ readonly issuerUrl?: string;
74
+ /**
75
+ * Storage seam for auth codes + DCR clients. Defaults to
76
+ * {@link InMemoryOAuthStorage} — works for single-replica dev + any
77
+ * deployment with sticky sessions. Replace with a Redis/DDB-backed
78
+ * implementation for stateless multi-replica deployments.
79
+ */
80
+ readonly storage?: OAuthStorage;
81
+ /**
82
+ * External consent UI to delegate the user-facing approval step to
83
+ * (e.g. `https://console.ggui.ai/oauth/consent`). When set,
84
+ * `GET /oauth/authorize` returns a 302 to this URL with every OAuth
85
+ * query param forwarded verbatim — the consent UI then constructs an
86
+ * HTML form that POSTs back to `<issuer>/oauth/authorize` with the
87
+ * user's chosen `api_key`.
88
+ *
89
+ * Trade-off: the consent UI sees every OAuth param + the user's
90
+ * Cognito session — it MUST be operator-controlled (same trust
91
+ * boundary as the MCP server itself). Cross-origin POST is fine
92
+ * (form-encoded → no CORS preflight); the response is a 302 to the
93
+ * client's `redirect_uri` which the browser follows transparently.
94
+ *
95
+ * Absence: `GET /oauth/authorize` falls back to the in-server
96
+ * paste-key HTML page. Useful for OSS deployers who don't want to
97
+ * stand up a separate consent UI; the form works but is unbranded.
98
+ */
99
+ readonly consentUrl?: string;
100
+ /**
101
+ * RFC 8707 resource indicator validator. Receives
102
+ * the resolved `issuer` URL plus the client-supplied `resource`
103
+ * query param value; returns `true` when `resource` names a valid
104
+ * MCP endpoint on this deployment, `false` otherwise.
105
+ *
106
+ * For ggui this gates two shapes:
107
+ * - Universal: `${issuer}` (cloud bare root) or
108
+ * `${issuer}${universalMcpPath}` (OSS `/mcp`).
109
+ * - Per-app: `${issuer}${perAppRouting.pathPrefix}/<appId>`
110
+ * where `<appId>` matches `perAppRouting.paramPattern`.
111
+ *
112
+ * server.ts builds this validator at boot from the deployment shape
113
+ * (universalMcpPath + perAppRouting); the OAuth handlers stay
114
+ * deployment-agnostic. When omitted, the resource param is accepted
115
+ * as-is without validation — fine for OSS deployments that don't
116
+ * advertise per-app endpoints.
117
+ *
118
+ * RFC 8707 §2 — auth servers SHOULD reject unknown resources with
119
+ * `invalid_target`. We do that at /authorize time so the user sees
120
+ * a clear error before the consent step (vs. silently issuing a
121
+ * code that then fails at /token).
122
+ */
123
+ readonly validateResource?: (issuer: string, resource: string) => boolean;
124
+ }
125
+ export interface AuthCodeRecord {
126
+ readonly code: string;
127
+ /**
128
+ * The user's `ggui_user_*` API key — returned verbatim as the
129
+ * `access_token` on `/oauth/token` exchange. The auth code is the
130
+ * server-side handle that prevents the key from leaking through the
131
+ * redirect URL (which logs / referers can capture).
132
+ */
133
+ readonly accessToken: string;
134
+ /** PKCE — `S256(code_verifier)` computed at /authorize time. */
135
+ readonly codeChallenge: string;
136
+ /** Redirect URI the client claimed at /authorize — must match at /token. */
137
+ readonly redirectUri: string;
138
+ /** Client id from DCR. */
139
+ readonly clientId: string;
140
+ /** Unix epoch ms — codes expire after 5 minutes. */
141
+ readonly expiresAt: number;
142
+ /**
143
+ * RFC 8707 resource indicator. Captured at
144
+ * /authorize time; if the /token request includes a `resource`
145
+ * parameter, it MUST equal this value (RFC 8707 §2.2). Absent
146
+ * when the client didn't claim a specific resource — universal
147
+ * scoping applies.
148
+ *
149
+ * Storage shape note: persisted on the auth-code row only. Tokens
150
+ * issued by /token are opaque static keys (`ggui_user_*`); the
151
+ * appId-from-resource binding is enforced at the auth-code →
152
+ * token boundary, not via JWT claims. Runtime appId resolution
153
+ * keeps reading the key shape (`UserKey.appId` from the
154
+ * `GguiUserApiKey` row).
155
+ */
156
+ readonly resource?: string;
157
+ }
158
+ export interface ClientRecord {
159
+ readonly clientId: string;
160
+ readonly redirectUris: readonly string[];
161
+ /** Optional human-readable label from DCR `client_name`. */
162
+ readonly clientName?: string;
163
+ /** Unix epoch ms — for cleanup; clients have no hard expiry. */
164
+ readonly createdAt: number;
165
+ /**
166
+ * RFC 8707 resource indicator from this client's most recent
167
+ * `/oauth/authorize` request (Q7 RESOLVED 2026-05-06). Captured
168
+ * each time `handleAuthorizePost` consumes the params — surfaces
169
+ * "Connected to: <App>" on the operator's Connected Apps console
170
+ * once that surface goes live. DCR itself doesn't carry resource
171
+ * (clients don't know their target at registration time); per-app
172
+ * resource is a per-/authorize-request property, so we snapshot
173
+ * the latest one onto the client record for display.
174
+ *
175
+ * Absent when the client never used a resource indicator (universal
176
+ * scoping). Absent on pre-2026-05-06 records (graceful undefined
177
+ * rather than null/empty-string) — operators see "Universal" in
178
+ * the UI for those.
179
+ */
180
+ readonly lastResource?: string;
181
+ /**
182
+ * Unix epoch ms of the most recent `/oauth/authorize` POST that
183
+ * referenced this client. Pairs with {@link lastResource} for
184
+ * "last seen" UI hints. Distinct from `createdAt` (DCR registration
185
+ * time) — a client may have registered weeks ago but only just
186
+ * now run its first authorize.
187
+ */
188
+ readonly lastAuthorizeAt?: number;
189
+ }
190
+ /**
191
+ * Storage interface for OAuth state. Two collections:
192
+ * - **Auth codes** (short-lived, single-use): one-time tokens
193
+ * returned from /authorize, exchanged at /token for an access token.
194
+ * - **DCR clients** (long-lived): registered via /oauth/register,
195
+ * identifies which client is making subsequent OAuth requests.
196
+ *
197
+ * `listClients` + `deleteClient` back the console's "Connected Apps"
198
+ * management surface so operators can see who registered + revoke
199
+ * stale entries without a server restart.
200
+ *
201
+ * **Revocation semantics on `deleteClient`:** removes the
202
+ * registration only. In-flight access tokens minted under that
203
+ * client_id are NOT invalidated here (in the current paste-key flow
204
+ * `access_token === paired_bearer`, so cutting the bearer means
205
+ * cutting EVERYONE). Revoking only stops future `/oauth/register`
206
+ * re-discovery and `/oauth/token` exchanges from that client. This is
207
+ * documented at the console route level.
208
+ */
209
+ export interface OAuthStorage {
210
+ putAuthCode(record: AuthCodeRecord): Promise<void>;
211
+ /** Atomic fetch-and-delete (single-use enforcement). */
212
+ consumeAuthCode(code: string): Promise<AuthCodeRecord | null>;
213
+ putClient(record: ClientRecord): Promise<void>;
214
+ getClient(clientId: string): Promise<ClientRecord | null>;
215
+ /**
216
+ * List every registered DCR client, sorted oldest-first by
217
+ * `createdAt`. Operator-facing; the console's Connected Apps tab
218
+ * paints from this. Empty array when no clients have registered
219
+ * yet.
220
+ */
221
+ listClients(): Promise<readonly ClientRecord[]>;
222
+ /**
223
+ * Delete a single client registration by `clientId`. Idempotent —
224
+ * deleting an unknown id resolves cleanly (no error, no state
225
+ * change). See {@link OAuthStorage} doc for the in-flight-token
226
+ * caveat.
227
+ */
228
+ deleteClient(clientId: string): Promise<void>;
229
+ }
230
+ export declare class InMemoryOAuthStorage implements OAuthStorage {
231
+ private readonly codes;
232
+ private readonly clients;
233
+ putAuthCode(record: AuthCodeRecord): Promise<void>;
234
+ consumeAuthCode(code: string): Promise<AuthCodeRecord | null>;
235
+ putClient(record: ClientRecord): Promise<void>;
236
+ getClient(clientId: string): Promise<ClientRecord | null>;
237
+ listClients(): Promise<readonly ClientRecord[]>;
238
+ deleteClient(clientId: string): Promise<void>;
239
+ }
240
+ /**
241
+ * Resolve the public origin for OAuth metadata. Prefer the configured
242
+ * {@link OAuthConfig.issuerUrl} (operator-controlled, deterministic);
243
+ * fall back to deriving from the request's forwarded headers (works
244
+ * behind nginx-ingress + ALB which both set X-Forwarded-Proto / -Host).
245
+ */
246
+ export declare function resolveIssuerUrl(req: Request, configured?: string): string;
247
+ /**
248
+ * Build the `WWW-Authenticate` header value pointing at the resource-
249
+ * metadata document. Per RFC 9728 §5, MCP-aware clients fetch this URL
250
+ * to discover the authorization server.
251
+ *
252
+ * `resourcePath` (default `''`) is the path prefix the per-app metadata
253
+ * lives under — e.g. `/apps/aB3kP9xY` → header points at
254
+ * `${issuer}/apps/aB3kP9xY/.well-known/oauth-protected-resource` so a
255
+ * client that 401'd on a per-app endpoint discovers the per-app
256
+ * metadata document. Empty string preserves the
257
+ * universal-only behavior — `${issuer}/.well-known/...` — used by the
258
+ * universal `/mcp` route, the auth-check probe, and OSS deployments
259
+ * without per-app routing.
260
+ *
261
+ * Caller must supply a leading slash (or empty string). Trailing slash
262
+ * is normalized off so callers can pass either `/apps/x` or
263
+ * `/apps/x/` interchangeably.
264
+ */
265
+ export declare function buildWwwAuthenticate(issuerUrl: string, resourcePath?: string): string;
266
+ /**
267
+ * `GET /.well-known/oauth-protected-resource` — RFC 9728 metadata that
268
+ * tells the client where to find the authorization server. Same origin
269
+ * in our case (we host both the resource AND the auth server).
270
+ *
271
+ * `mcpPath` (default `/mcp`) is the deployment's universal MCP route.
272
+ * Cloud `mcp.ggui.ai` mounts at the bare root `/` so URLs are short
273
+ * (the domain already says "mcp"); OSS keeps the conventional `/mcp`.
274
+ * The trailing slash is normalized off when the path is `/` so the
275
+ * resource URL is `${issuer}` (no trailing slash) rather than
276
+ * `${issuer}/`.
277
+ */
278
+ export declare function handleProtectedResourceMetadata(req: Request, res: Response, config: OAuthConfig, mcpPath?: string): void;
279
+ /**
280
+ * `GET /.well-known/oauth-authorization-server` — RFC 8414 metadata
281
+ * describing this auth server's capabilities + endpoints.
282
+ */
283
+ export declare function handleAuthorizationServerMetadata(req: Request, res: Response, config: OAuthConfig): void;
284
+ /**
285
+ * `POST /oauth/register` — RFC 7591 Dynamic Client Registration. Issues
286
+ * a random `client_id`. No `client_secret` (PKCE-only). Accepts arbitrary
287
+ * `redirect_uris` from the client without validation against an allowlist
288
+ * — the trade-off matches the MCP spec's pragmatism: any client willing
289
+ * to do PKCE + paste-key gets registered.
290
+ */
291
+ export declare function handleRegister(req: Request, res: Response, config: OAuthConfig, storage: OAuthStorage): Promise<void>;
292
+ /**
293
+ * `GET /oauth/authorize` — render the paste-key form. Query params
294
+ * (per OAuth 2.1 + PKCE):
295
+ * - `client_id` — from DCR
296
+ * - `redirect_uri` — must match one registered at DCR time
297
+ * - `response_type=code`
298
+ * - `code_challenge` — base64url(SHA256(code_verifier))
299
+ * - `code_challenge_method=S256`
300
+ * - `state` — opaque, echoed back to client
301
+ * - `scope` — ignored (we don't gate by scope today)
302
+ *
303
+ * The page is intentionally minimal — server-rendered HTML, no JS
304
+ * required, no external CDN. The form POSTs back to `/oauth/authorize`
305
+ * with the user's pasted key.
306
+ */
307
+ export declare function handleAuthorizeGet(req: Request, res: Response, config: OAuthConfig, storage: OAuthStorage): Promise<void>;
308
+ /**
309
+ * `POST /oauth/authorize` — handle paste-key form submission. Validates
310
+ * the key against the configured {@link AuthAdapter} (same one that
311
+ * gates `/mcp`), mints an auth code, redirects to client's
312
+ * `redirect_uri` with `?code=...&state=...`.
313
+ *
314
+ * Validation flow:
315
+ * 1. Re-validate the OAuth params (defense — caller could skip /GET).
316
+ * 2. Resolve the pasted key through `auth.authenticate()` — accepts
317
+ * any key the adapter accepts. For ggui this means `ggui_user_*`
318
+ * keys validated against `GguiUserApiKey` table via the existing
319
+ * `lookupUser` binding.
320
+ * 3. On success: write {code, accessToken=key, codeChallenge,
321
+ * redirectUri, clientId} to storage. 5-minute TTL.
322
+ * 4. Redirect to `redirect_uri?code=<code>&state=<state>`.
323
+ * 5. On bad key: re-render the page with an error message (preserves
324
+ * OAuth params).
325
+ */
326
+ export declare function handleAuthorizePost(req: Request, res: Response, config: OAuthConfig, storage: OAuthStorage, auth: AuthAdapter, pairingService?: PairingService | null): Promise<void>;
327
+ /**
328
+ * `POST /oauth/token` — exchange auth code for access token. RFC 6749
329
+ * §4.1.3 + RFC 7636 (PKCE).
330
+ *
331
+ * Request body (form-urlencoded or JSON):
332
+ * - `grant_type=authorization_code`
333
+ * - `code` — from the redirect
334
+ * - `redirect_uri` — must match what was passed at /authorize
335
+ * - `client_id` — from DCR
336
+ * - `code_verifier` — PKCE verifier (raw, server hashes + compares)
337
+ *
338
+ * Response:
339
+ * - `access_token` — the user's `ggui_user_*` key (stored from /authorize)
340
+ * - `token_type=Bearer`
341
+ * - `expires_in` — omitted; key TTL matches the underlying API key
342
+ *
343
+ * Errors per RFC 6749 §5.2 — JSON body with `{error, error_description}`,
344
+ * 400 status code.
345
+ */
346
+ export declare function handleToken(req: Request, res: Response, storage: OAuthStorage): Promise<void>;
347
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}