@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.
- package/LICENSE +201 -0
- package/README.md +48 -0
- package/dist/admin-blueprints-transport.d.ts +114 -0
- package/dist/admin-blueprints-transport.d.ts.map +1 -0
- package/dist/admin-blueprints-transport.js +118 -0
- package/dist/admin-oauth-providers-transport.d.ts +40 -0
- package/dist/admin-oauth-providers-transport.d.ts.map +1 -0
- package/dist/admin-oauth-providers-transport.js +263 -0
- package/dist/auth.d.ts +39 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +75 -0
- package/dist/build-mcp.d.ts +128 -0
- package/dist/build-mcp.d.ts.map +1 -0
- package/dist/build-mcp.js +113 -0
- package/dist/code-store-fs.d.ts +19 -0
- package/dist/code-store-fs.d.ts.map +1 -0
- package/dist/code-store-fs.js +98 -0
- package/dist/console-auth.d.ts +139 -0
- package/dist/console-auth.d.ts.map +1 -0
- package/dist/console-auth.js +102 -0
- package/dist/console-cache.d.ts +78 -0
- package/dist/console-cache.d.ts.map +1 -0
- package/dist/console-cache.js +105 -0
- package/dist/console-headers.d.ts +124 -0
- package/dist/console-headers.d.ts.map +1 -0
- package/dist/console-headers.js +49 -0
- package/dist/console-llm-trace.d.ts +66 -0
- package/dist/console-llm-trace.d.ts.map +1 -0
- package/dist/console-llm-trace.js +105 -0
- package/dist/console-payloads.d.ts +67 -0
- package/dist/console-payloads.d.ts.map +1 -0
- package/dist/console-payloads.js +105 -0
- package/dist/console-theme-routes.d.ts +111 -0
- package/dist/console-theme-routes.d.ts.map +1 -0
- package/dist/console-theme-routes.js +202 -0
- package/dist/console-timeline.d.ts +45 -0
- package/dist/console-timeline.d.ts.map +1 -0
- package/dist/console-timeline.js +169 -0
- package/dist/console-validator.d.ts +67 -0
- package/dist/console-validator.d.ts.map +1 -0
- package/dist/console-validator.js +105 -0
- package/dist/console-welcome.d.ts +7 -0
- package/dist/console-welcome.d.ts.map +1 -0
- package/dist/console-welcome.js +221 -0
- package/dist/csrf-middleware.d.ts +55 -0
- package/dist/csrf-middleware.d.ts.map +1 -0
- package/dist/csrf-middleware.js +138 -0
- package/dist/email-login.d.ts +174 -0
- package/dist/email-login.d.ts.map +1 -0
- package/dist/email-login.js +254 -0
- package/dist/email-resend.d.ts +29 -0
- package/dist/email-resend.d.ts.map +1 -0
- package/dist/email-resend.js +71 -0
- package/dist/email-sender-from-env.d.ts +34 -0
- package/dist/email-sender-from-env.d.ts.map +1 -0
- package/dist/email-sender-from-env.js +112 -0
- package/dist/email-smtp.d.ts +42 -0
- package/dist/email-smtp.d.ts.map +1 -0
- package/dist/email-smtp.js +81 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/instructions-presets.d.ts +112 -0
- package/dist/instructions-presets.d.ts.map +1 -0
- package/dist/instructions-presets.js +195 -0
- package/dist/llm-backed-negotiator.d.ts +178 -0
- package/dist/llm-backed-negotiator.d.ts.map +1 -0
- package/dist/llm-backed-negotiator.js +579 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +41 -0
- package/dist/mcp-apps-inbound.d.ts +86 -0
- package/dist/mcp-apps-inbound.d.ts.map +1 -0
- package/dist/mcp-apps-inbound.js +278 -0
- package/dist/mcp-apps-outbound.d.ts +448 -0
- package/dist/mcp-apps-outbound.d.ts.map +1 -0
- package/dist/mcp-apps-outbound.js +1163 -0
- package/dist/mcp-mounts.d.ts +239 -0
- package/dist/mcp-mounts.d.ts.map +1 -0
- package/dist/mcp-mounts.js +222 -0
- package/dist/oauth-login-types.d.ts +160 -0
- package/dist/oauth-login-types.d.ts.map +1 -0
- package/dist/oauth-login-types.js +9 -0
- package/dist/oauth-login.d.ts +77 -0
- package/dist/oauth-login.d.ts.map +1 -0
- package/dist/oauth-login.js +455 -0
- package/dist/oauth-providers/github.d.ts +17 -0
- package/dist/oauth-providers/github.d.ts.map +1 -0
- package/dist/oauth-providers/github.js +89 -0
- package/dist/oauth-providers/google.d.ts +18 -0
- package/dist/oauth-providers/google.d.ts.map +1 -0
- package/dist/oauth-providers/google.js +59 -0
- package/dist/oauth-providers-store.d.ts +32 -0
- package/dist/oauth-providers-store.d.ts.map +1 -0
- package/dist/oauth-providers-store.js +291 -0
- package/dist/oauth.d.ts +347 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +686 -0
- package/dist/pairing-transport.d.ts +99 -0
- package/dist/pairing-transport.d.ts.map +1 -0
- package/dist/pairing-transport.js +223 -0
- package/dist/rate-limit-middleware.d.ts +36 -0
- package/dist/rate-limit-middleware.d.ts.map +1 -0
- package/dist/rate-limit-middleware.js +57 -0
- package/dist/render-gate.d.ts +87 -0
- package/dist/render-gate.d.ts.map +1 -0
- package/dist/render-gate.js +77 -0
- package/dist/render-rate-limit.d.ts +59 -0
- package/dist/render-rate-limit.d.ts.map +1 -0
- package/dist/render-rate-limit.js +73 -0
- package/dist/render-signing.d.ts +98 -0
- package/dist/render-signing.d.ts.map +1 -0
- package/dist/render-signing.js +113 -0
- package/dist/request-context.d.ts +113 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +154 -0
- package/dist/reserved-validators.d.ts +22 -0
- package/dist/reserved-validators.d.ts.map +1 -0
- package/dist/reserved-validators.js +101 -0
- package/dist/schema-compat.d.ts +167 -0
- package/dist/schema-compat.d.ts.map +1 -0
- package/dist/schema-compat.js +187 -0
- package/dist/security-headers-middleware.d.ts +38 -0
- package/dist/security-headers-middleware.d.ts.map +1 -0
- package/dist/security-headers-middleware.js +30 -0
- package/dist/server.d.ts +2060 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +6338 -0
- package/dist/session-channel.d.ts +651 -0
- package/dist/session-channel.d.ts.map +1 -0
- package/dist/session-channel.js +1756 -0
- package/dist/storage.d.ts +89 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +171 -0
- package/dist/thread-transport.d.ts +118 -0
- package/dist/thread-transport.d.ts.map +1 -0
- package/dist/thread-transport.js +478 -0
- package/dist/user-session-auth.d.ts +167 -0
- package/dist/user-session-auth.d.ts.map +1 -0
- package/dist/user-session-auth.js +148 -0
- package/package.json +76 -0
package/dist/oauth.d.ts
ADDED
|
@@ -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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"}
|