@nocoo/base-mcp 0.1.0-alpha.1 → 0.1.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/README.md +315 -3
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/oauth/constants.d.ts +7 -0
- package/dist/auth/oauth/constants.d.ts.map +1 -0
- package/dist/auth/oauth/constants.js +10 -0
- package/dist/auth/oauth/constants.js.map +1 -0
- package/dist/auth/oauth/handlers/authorize.d.ts +12 -0
- package/dist/auth/oauth/handlers/authorize.d.ts.map +1 -0
- package/dist/auth/oauth/handlers/authorize.js +89 -0
- package/dist/auth/oauth/handlers/authorize.js.map +1 -0
- package/dist/auth/oauth/handlers/callback.d.ts +13 -0
- package/dist/auth/oauth/handlers/callback.d.ts.map +1 -0
- package/dist/auth/oauth/handlers/callback.js +72 -0
- package/dist/auth/oauth/handlers/callback.js.map +1 -0
- package/dist/auth/oauth/handlers/index.d.ts +5 -0
- package/dist/auth/oauth/handlers/index.d.ts.map +1 -0
- package/dist/auth/oauth/handlers/index.js +8 -0
- package/dist/auth/oauth/handlers/index.js.map +1 -0
- package/dist/auth/oauth/handlers/register.d.ts +9 -0
- package/dist/auth/oauth/handlers/register.d.ts.map +1 -0
- package/dist/auth/oauth/handlers/register.js +62 -0
- package/dist/auth/oauth/handlers/register.js.map +1 -0
- package/dist/auth/oauth/handlers/token.d.ts +10 -0
- package/dist/auth/oauth/handlers/token.d.ts.map +1 -0
- package/dist/auth/oauth/handlers/token.js +139 -0
- package/dist/auth/oauth/handlers/token.js.map +1 -0
- package/dist/auth/oauth/index.d.ts +4 -0
- package/dist/auth/oauth/index.d.ts.map +1 -0
- package/dist/auth/oauth/index.js +8 -0
- package/dist/auth/oauth/index.js.map +1 -0
- package/dist/auth/oauth/testing/index.d.ts +2 -0
- package/dist/auth/oauth/testing/index.d.ts.map +1 -0
- package/dist/auth/oauth/testing/index.js +5 -0
- package/dist/auth/oauth/testing/index.js.map +1 -0
- package/dist/auth/oauth/testing/mock-stores.d.ts +36 -0
- package/dist/auth/oauth/testing/mock-stores.d.ts.map +1 -0
- package/dist/auth/oauth/testing/mock-stores.js +218 -0
- package/dist/auth/oauth/testing/mock-stores.js.map +1 -0
- package/dist/auth/oauth/types.d.ts +187 -0
- package/dist/auth/oauth/types.d.ts.map +1 -0
- package/dist/auth/oauth/types.js +5 -0
- package/dist/auth/oauth/types.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,319 @@ Peer dependencies:
|
|
|
20
20
|
pnpm add @modelcontextprotocol/sdk zod
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Full Integration Guide
|
|
24
|
+
|
|
25
|
+
This guide covers the complete setup for integrating MCP with OAuth 2.1 authentication in a Next.js/Hono app.
|
|
26
|
+
|
|
27
|
+
### Architecture Overview
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
┌─────────────────┐ ┌──────────────────────────────────────┐
|
|
31
|
+
│ MCP Client │ │ Your App │
|
|
32
|
+
│ (Claude Code, │ │ ┌─────────────────────────────────┐ │
|
|
33
|
+
│ Cursor, etc.) │────▶│ │ /.well-known/oauth-auth-server │ │ OAuth Discovery
|
|
34
|
+
│ │ │ └─────────────────────────────────┘ │
|
|
35
|
+
│ │ │ │
|
|
36
|
+
│ │────▶│ /api/mcp/register (Client Reg) │
|
|
37
|
+
│ │────▶│ /api/mcp/authorize (Auth Start) │
|
|
38
|
+
│ │────▶│ /api/mcp/callback (Auth Complete) │
|
|
39
|
+
│ │────▶│ /api/mcp/token (Token Exchange) │
|
|
40
|
+
│ │ │ │
|
|
41
|
+
│ │────▶│ /api/mcp (MCP Endpoint) │
|
|
42
|
+
└─────────────────┘ └──────────────────────────────────────┘
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Step 1: OAuth Discovery Endpoint
|
|
46
|
+
|
|
47
|
+
Create `/.well-known/oauth-authorization-server` to expose OAuth metadata.
|
|
48
|
+
|
|
49
|
+
**CRITICAL**: This endpoint must NOT be protected by authentication.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// src/app/.well-known/oauth-authorization-server/route.ts
|
|
53
|
+
import { getOAuthMetadata } from "@nocoo/base-mcp/auth";
|
|
54
|
+
import { NextResponse } from "next/server";
|
|
55
|
+
|
|
56
|
+
export function GET() {
|
|
57
|
+
// Use environment variable or derive from request
|
|
58
|
+
const issuer = process.env.AUTH_URL ?? "http://localhost:3000";
|
|
59
|
+
const metadata = getOAuthMetadata(issuer);
|
|
60
|
+
|
|
61
|
+
return NextResponse.json(metadata, {
|
|
62
|
+
headers: { "Cache-Control": "public, max-age=3600" },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The metadata includes:
|
|
68
|
+
- `authorization_endpoint`: `/api/mcp/authorize`
|
|
69
|
+
- `token_endpoint`: `/api/mcp/token`
|
|
70
|
+
- `registration_endpoint`: `/api/mcp/register`
|
|
71
|
+
|
|
72
|
+
### Step 2: Middleware Configuration
|
|
73
|
+
|
|
74
|
+
Ensure `/.well-known/` is publicly accessible without authentication:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// middleware.ts or proxy-logic.ts
|
|
78
|
+
const PUBLIC_PATHS = [
|
|
79
|
+
"/login",
|
|
80
|
+
"/terms",
|
|
81
|
+
"/privacy",
|
|
82
|
+
"/.well-known/", // OAuth metadata - must be public
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
function isPublicPath(pathname: string): boolean {
|
|
86
|
+
return PUBLIC_PATHS.some(p => pathname === p || pathname.startsWith(p));
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Step 3: OAuth Endpoints
|
|
91
|
+
|
|
92
|
+
#### 3.1 Dynamic Client Registration (`/api/mcp/register`)
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// src/app/api/mcp/register/route.ts
|
|
96
|
+
import { isLoopbackRedirectUri } from "@nocoo/base-mcp/auth";
|
|
97
|
+
import { createMcpClient } from "@/data/mcp-clients"; // Your data layer
|
|
98
|
+
|
|
99
|
+
export async function POST(request: Request) {
|
|
100
|
+
const body = await request.json();
|
|
101
|
+
|
|
102
|
+
// Validate redirect_uris (loopback only for security)
|
|
103
|
+
for (const uri of body.redirect_uris) {
|
|
104
|
+
if (!isLoopbackRedirectUri(uri)) {
|
|
105
|
+
return errorResponse("Only loopback redirect URIs are allowed");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const client = await createMcpClient(db, {
|
|
110
|
+
client_name: body.client_name,
|
|
111
|
+
redirect_uris: body.redirect_uris,
|
|
112
|
+
grant_types: body.grant_types ?? ["authorization_code"],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return jsonResponse({
|
|
116
|
+
client_id: client.client_id,
|
|
117
|
+
client_name: client.client_name,
|
|
118
|
+
redirect_uris: JSON.parse(client.redirect_uris),
|
|
119
|
+
grant_types: JSON.parse(client.grant_types),
|
|
120
|
+
token_endpoint_auth_method: "none",
|
|
121
|
+
}, 201);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### 3.2 Authorization (`/api/mcp/authorize`)
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// src/app/api/mcp/authorize/route.ts
|
|
129
|
+
export async function GET(request: Request) {
|
|
130
|
+
const url = new URL(request.url);
|
|
131
|
+
const params = url.searchParams;
|
|
132
|
+
|
|
133
|
+
// Required OAuth params
|
|
134
|
+
const responseType = params.get("response_type"); // Must be "code"
|
|
135
|
+
const clientId = params.get("client_id");
|
|
136
|
+
const redirectUri = params.get("redirect_uri");
|
|
137
|
+
const codeChallenge = params.get("code_challenge");
|
|
138
|
+
const codeChallengeMethod = params.get("code_challenge_method"); // Must be "S256"
|
|
139
|
+
const state = params.get("state");
|
|
140
|
+
|
|
141
|
+
// Store auth session keyed by state
|
|
142
|
+
await createAuthSession(db, {
|
|
143
|
+
state,
|
|
144
|
+
client_id: clientId,
|
|
145
|
+
redirect_uri: redirectUri,
|
|
146
|
+
code_challenge: codeChallenge,
|
|
147
|
+
code_challenge_method: codeChallengeMethod,
|
|
148
|
+
scope,
|
|
149
|
+
expires_at: now + AUTH_CODE_TTL,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Check if user already authenticated
|
|
153
|
+
const session = await auth();
|
|
154
|
+
if (session?.user?.email) {
|
|
155
|
+
return NextResponse.redirect(`/api/mcp/callback?state=${state}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Redirect to login
|
|
159
|
+
return NextResponse.redirect(`/login?callbackUrl=/api/mcp/callback?state=${state}`);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### 3.3 Callback (`/api/mcp/callback`)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// src/app/api/mcp/callback/route.ts
|
|
167
|
+
export async function GET(request: Request) {
|
|
168
|
+
const state = new URL(request.url).searchParams.get("state");
|
|
169
|
+
|
|
170
|
+
// Verify user is authenticated
|
|
171
|
+
const session = await auth();
|
|
172
|
+
if (!session?.user?.email) {
|
|
173
|
+
return errorResponse("Authentication required", 401);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Look up auth session
|
|
177
|
+
const authSession = await getAuthSessionByState(db, state);
|
|
178
|
+
if (!authSession) {
|
|
179
|
+
return errorResponse("Invalid or expired session");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Generate authorization code
|
|
183
|
+
const code = randomBytes(32).toString("hex");
|
|
184
|
+
await upgradeAuthSession(db, state, code, session.user.email);
|
|
185
|
+
|
|
186
|
+
// Redirect to client's redirect_uri
|
|
187
|
+
const redirectUrl = new URL(authSession.redirect_uri);
|
|
188
|
+
redirectUrl.searchParams.set("code", code);
|
|
189
|
+
redirectUrl.searchParams.set("state", state);
|
|
190
|
+
|
|
191
|
+
return NextResponse.redirect(redirectUrl.toString());
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### 3.4 Token Exchange (`/api/mcp/token`)
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// src/app/api/mcp/token/route.ts
|
|
199
|
+
import { verifyPkceS256 } from "@nocoo/base-mcp/auth";
|
|
200
|
+
|
|
201
|
+
export async function POST(request: Request) {
|
|
202
|
+
const body = await request.formData();
|
|
203
|
+
const grantType = body.get("grant_type");
|
|
204
|
+
|
|
205
|
+
if (grantType === "authorization_code") {
|
|
206
|
+
const code = body.get("code");
|
|
207
|
+
const codeVerifier = body.get("code_verifier");
|
|
208
|
+
|
|
209
|
+
// Look up and validate auth code
|
|
210
|
+
const authCode = await getAuthCodeByCode(db, code);
|
|
211
|
+
|
|
212
|
+
// Verify PKCE
|
|
213
|
+
const pkceValid = await verifyPkceS256(codeVerifier, authCode.code_challenge);
|
|
214
|
+
if (!pkceValid) {
|
|
215
|
+
return oauthError("invalid_grant", "PKCE verification failed");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Issue tokens
|
|
219
|
+
return jsonResponse({
|
|
220
|
+
access_token: accessToken,
|
|
221
|
+
token_type: "Bearer",
|
|
222
|
+
expires_in: ACCESS_TOKEN_TTL,
|
|
223
|
+
refresh_token: refreshToken,
|
|
224
|
+
scope,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (grantType === "refresh_token") {
|
|
229
|
+
// Handle refresh token exchange
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Step 4: MCP Endpoint
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// src/app/api/mcp/route.ts
|
|
238
|
+
import { validateMcpToken, validateOrigin } from "@nocoo/base-mcp/auth";
|
|
239
|
+
|
|
240
|
+
export async function POST(request: Request) {
|
|
241
|
+
const siteUrl = process.env.AUTH_URL ?? "http://localhost:3000";
|
|
242
|
+
|
|
243
|
+
// Step 1: Validate Origin (DNS rebinding protection)
|
|
244
|
+
const origin = request.headers.get("origin");
|
|
245
|
+
const originResult = validateOrigin(origin, siteUrl);
|
|
246
|
+
if (!originResult.valid) {
|
|
247
|
+
return errorResponse(originResult.error, originResult.status);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Step 2: Validate Bearer token
|
|
251
|
+
const authResult = await validateMcpToken(
|
|
252
|
+
db,
|
|
253
|
+
request.headers.get("authorization"),
|
|
254
|
+
);
|
|
255
|
+
if (!authResult.valid) {
|
|
256
|
+
return errorResponse(authResult.error, authResult.status);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Step 3: Create MCP server and handle request
|
|
260
|
+
const server = createMcpServer(db);
|
|
261
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
262
|
+
enableJsonResponse: true, // Stateless mode
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
await server.connect(transport);
|
|
266
|
+
return transport.handleRequest(request);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Step 5: MCP Configuration Page
|
|
271
|
+
|
|
272
|
+
Create a user-facing page to help configure MCP clients:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// src/app/mcp-tokens/mcp-tokens-client.tsx
|
|
276
|
+
"use client";
|
|
277
|
+
|
|
278
|
+
function getMcpUrl(): string {
|
|
279
|
+
if (typeof window === "undefined") {
|
|
280
|
+
return "https://your-app.com/api/mcp";
|
|
281
|
+
}
|
|
282
|
+
// Dynamic URL derivation - no hardcoding
|
|
283
|
+
const { protocol, hostname, port } = window.location;
|
|
284
|
+
let baseUrl = `${protocol}//${hostname}`;
|
|
285
|
+
if (port && port !== "80" && port !== "443") {
|
|
286
|
+
baseUrl += `:${port}`;
|
|
287
|
+
}
|
|
288
|
+
return `${baseUrl}/api/mcp`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function McpConfigPage() {
|
|
292
|
+
const mcpUrl = useMemo(() => getMcpUrl(), []);
|
|
293
|
+
|
|
294
|
+
const configs = {
|
|
295
|
+
"claude-code": `{
|
|
296
|
+
"mcpServers": {
|
|
297
|
+
"your-app": {
|
|
298
|
+
"type": "http",
|
|
299
|
+
"url": "${mcpUrl}"
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}`,
|
|
303
|
+
"claude-desktop": `{
|
|
304
|
+
"mcpServers": {
|
|
305
|
+
"your-app": {
|
|
306
|
+
"command": "npx",
|
|
307
|
+
"args": ["-y", "mcp-remote", "${mcpUrl}"]
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}`,
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Render configuration tabs...
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Key principles:**
|
|
318
|
+
- Never hardcode domains - derive from `window.location`
|
|
319
|
+
- Use `type: "http"` for Claude Code (supports Streamable HTTP natively)
|
|
320
|
+
- Use `mcp-remote` bridge for Claude Desktop (requires stdio)
|
|
321
|
+
|
|
322
|
+
### Summary Checklist
|
|
323
|
+
|
|
324
|
+
- [ ] `/.well-known/oauth-authorization-server` returns JSON without auth
|
|
325
|
+
- [ ] Middleware allows `/.well-known/` paths through without login
|
|
326
|
+
- [ ] `/api/mcp/register` validates loopback redirect URIs
|
|
327
|
+
- [ ] `/api/mcp/authorize` stores auth session and redirects to login
|
|
328
|
+
- [ ] `/api/mcp/callback` generates auth code after login
|
|
329
|
+
- [ ] `/api/mcp/token` verifies PKCE and issues tokens
|
|
330
|
+
- [ ] `/api/mcp` validates origin and bearer token
|
|
331
|
+
- [ ] MCP config page derives URL dynamically (no hardcoding)
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Quick Start (Entity-Driven CRUD)
|
|
24
336
|
|
|
25
337
|
```typescript
|
|
26
338
|
import { createMcpServer, registerEntityTools, ok, error } from "@nocoo/base-mcp";
|
|
@@ -57,7 +369,7 @@ const ctx = { repos: createRepos(db) };
|
|
|
57
369
|
registerEntityTools(server, productEntity, ctx);
|
|
58
370
|
|
|
59
371
|
// 4. Handle HTTP requests (Hono/Cloudflare Worker example)
|
|
60
|
-
app.post("/mcp", async (c) => {
|
|
372
|
+
app.post("/api/mcp", async (c) => {
|
|
61
373
|
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
62
374
|
sessionIdGenerator: undefined, // Stateless
|
|
63
375
|
enableJsonResponse: true,
|
|
@@ -67,7 +379,7 @@ app.post("/mcp", async (c) => {
|
|
|
67
379
|
});
|
|
68
380
|
```
|
|
69
381
|
|
|
70
|
-
|
|
382
|
+
---
|
|
71
383
|
|
|
72
384
|
### Server
|
|
73
385
|
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { verifyPkceS256, generateCodeVerifier, generateCodeChallenge, isLoopback
|
|
|
2
2
|
export { getOAuthMetadata, type OAuthMetadata, type OAuthMetadataOptions, } from "./oauth-metadata.js";
|
|
3
3
|
export { validateOrigin, isLoopbackHost, type OriginValidationResult, } from "./origin.js";
|
|
4
4
|
export { generateToken, hashToken, tokenPreview, extractBearerToken, validateMcpToken, type TokenStore, type TokenValidationResult, type TokenValidationSuccess, type TokenValidationError, } from "./token.js";
|
|
5
|
+
export * from "./oauth/index.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC;AAGpB,cAAc,kBAAkB,CAAC"}
|
package/dist/auth/index.js
CHANGED
|
@@ -3,4 +3,6 @@ export { verifyPkceS256, generateCodeVerifier, generateCodeChallenge, isLoopback
|
|
|
3
3
|
export { getOAuthMetadata, } from "./oauth-metadata.js";
|
|
4
4
|
export { validateOrigin, isLoopbackHost, } from "./origin.js";
|
|
5
5
|
export { generateToken, hashToken, tokenPreview, extractBearerToken, validateMcpToken, } from "./token.js";
|
|
6
|
+
// OAuth handlers module
|
|
7
|
+
export * from "./oauth/index.js";
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,GAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,cAAc,GAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,GAKjB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,gBAAgB,GAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,cAAc,GAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,GAKjB,MAAM,YAAY,CAAC;AAEpB,wBAAwB;AACxB,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Authorization code TTL in seconds (10 minutes) */
|
|
2
|
+
export declare const AUTH_CODE_TTL: number;
|
|
3
|
+
/** Access token TTL in seconds (30 days) */
|
|
4
|
+
export declare const ACCESS_TOKEN_TTL: number;
|
|
5
|
+
/** Refresh token TTL in seconds (90 days) */
|
|
6
|
+
export declare const REFRESH_TOKEN_TTL: number;
|
|
7
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/constants.ts"],"names":[],"mappings":"AAIA,qDAAqD;AACrD,eAAO,MAAM,aAAa,QAAU,CAAC;AAErC,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB,QAAoB,CAAC;AAElD,6CAA6C;AAC7C,eAAO,MAAM,iBAAiB,QAAoB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// OAuth Constants
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
/** Authorization code TTL in seconds (10 minutes) */
|
|
5
|
+
export const AUTH_CODE_TTL = 10 * 60;
|
|
6
|
+
/** Access token TTL in seconds (30 days) */
|
|
7
|
+
export const ACCESS_TOKEN_TTL = 30 * 24 * 60 * 60;
|
|
8
|
+
/** Refresh token TTL in seconds (90 days) */
|
|
9
|
+
export const REFRESH_TOKEN_TTL = 90 * 24 * 60 * 60;
|
|
10
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/auth/oauth/constants.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,qDAAqD;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,GAAG,EAAE,CAAC;AAErC,4CAA4C;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAElD,6CAA6C;AAC7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OAuthContext, AuthorizeInput, AuthorizeResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle OAuth authorization request.
|
|
4
|
+
*
|
|
5
|
+
* This endpoint:
|
|
6
|
+
* 1. Validates all required parameters
|
|
7
|
+
* 2. Verifies client_id and redirect_uri
|
|
8
|
+
* 3. Stores the authorization session
|
|
9
|
+
* 4. Redirects to callback (if already logged in) or login page
|
|
10
|
+
*/
|
|
11
|
+
export declare function handleAuthorize(ctx: OAuthContext, input: AuthorizeInput): Promise<AuthorizeResult>;
|
|
12
|
+
//# sourceMappingURL=authorize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/authorize.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGjF;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,eAAe,CAAC,CA8F1B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// OAuth Authorize Handler — Authorization Endpoint
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { AUTH_CODE_TTL } from "../constants.js";
|
|
5
|
+
/**
|
|
6
|
+
* Handle OAuth authorization request.
|
|
7
|
+
*
|
|
8
|
+
* This endpoint:
|
|
9
|
+
* 1. Validates all required parameters
|
|
10
|
+
* 2. Verifies client_id and redirect_uri
|
|
11
|
+
* 3. Stores the authorization session
|
|
12
|
+
* 4. Redirects to callback (if already logged in) or login page
|
|
13
|
+
*/
|
|
14
|
+
export async function handleAuthorize(ctx, input) {
|
|
15
|
+
const { response_type, client_id, redirect_uri, code_challenge, code_challenge_method, state, scope, } = input;
|
|
16
|
+
// Validate required params
|
|
17
|
+
if (!response_type ||
|
|
18
|
+
!client_id ||
|
|
19
|
+
!redirect_uri ||
|
|
20
|
+
!code_challenge ||
|
|
21
|
+
!code_challenge_method ||
|
|
22
|
+
!state) {
|
|
23
|
+
return {
|
|
24
|
+
action: "error",
|
|
25
|
+
error: "Missing required parameters: response_type, client_id, redirect_uri, code_challenge, code_challenge_method, state",
|
|
26
|
+
status: 400,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (response_type !== "code") {
|
|
30
|
+
return {
|
|
31
|
+
action: "error",
|
|
32
|
+
error: "response_type must be 'code'",
|
|
33
|
+
status: 400,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (code_challenge_method !== "S256") {
|
|
37
|
+
return {
|
|
38
|
+
action: "error",
|
|
39
|
+
error: "code_challenge_method must be 'S256'",
|
|
40
|
+
status: 400,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Verify client exists
|
|
44
|
+
const client = await ctx.clients.findByClientId(client_id);
|
|
45
|
+
if (!client) {
|
|
46
|
+
return {
|
|
47
|
+
action: "error",
|
|
48
|
+
error: "Unknown client_id",
|
|
49
|
+
status: 401,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Verify redirect_uri matches registered URIs
|
|
53
|
+
const registeredUris = JSON.parse(client.redirect_uris);
|
|
54
|
+
if (!registeredUris.includes(redirect_uri)) {
|
|
55
|
+
return {
|
|
56
|
+
action: "error",
|
|
57
|
+
error: "redirect_uri does not match any registered URIs",
|
|
58
|
+
status: 400,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Store authorization session
|
|
62
|
+
const now = Math.floor(Date.now() / 1000);
|
|
63
|
+
await ctx.authCodes.createSession({
|
|
64
|
+
state,
|
|
65
|
+
client_id,
|
|
66
|
+
redirect_uri,
|
|
67
|
+
code_challenge,
|
|
68
|
+
code_challenge_method,
|
|
69
|
+
scope: scope ?? "mcp:full",
|
|
70
|
+
expires_at: now + AUTH_CODE_TTL,
|
|
71
|
+
});
|
|
72
|
+
// Build callback URL
|
|
73
|
+
const callbackUrl = `${ctx.issuer}/api/mcp/callback?state=${encodeURIComponent(state)}`;
|
|
74
|
+
// If user already authenticated, go straight to callback
|
|
75
|
+
const isAuthenticated = await ctx.auth.isAuthenticated();
|
|
76
|
+
if (isAuthenticated) {
|
|
77
|
+
return {
|
|
78
|
+
action: "redirect_to_callback",
|
|
79
|
+
url: callbackUrl,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// Otherwise, redirect to login
|
|
83
|
+
const loginUrl = `${ctx.issuer}/login?callbackUrl=${encodeURIComponent(callbackUrl)}`;
|
|
84
|
+
return {
|
|
85
|
+
action: "redirect_to_login",
|
|
86
|
+
loginUrl,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=authorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/authorize.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mDAAmD;AACnD,8EAA8E;AAG9E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAiB,EACjB,KAAqB;IAErB,MAAM,EACJ,aAAa,EACb,SAAS,EACT,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,KAAK,EACL,KAAK,GACN,GAAG,KAAK,CAAC;IAEV,2BAA2B;IAC3B,IACE,CAAC,aAAa;QACd,CAAC,SAAS;QACV,CAAC,YAAY;QACb,CAAC,cAAc;QACf,CAAC,qBAAqB;QACtB,CAAC,KAAK,EACN,CAAC;QACD,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EACH,mHAAmH;YACrH,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;QAC7B,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,8BAA8B;YACrC,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,IAAI,qBAAqB,KAAK,MAAM,EAAE,CAAC;QACrC,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,sCAAsC;YAC7C,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,uBAAuB;IACvB,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,mBAAmB;YAC1B,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,8CAA8C;IAC9C,MAAM,cAAc,GAAa,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAClE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3C,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,iDAAiD;YACxD,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,8BAA8B;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC;QAChC,KAAK;QACL,SAAS;QACT,YAAY;QACZ,cAAc;QACd,qBAAqB;QACrB,KAAK,EAAE,KAAK,IAAI,UAAU;QAC1B,UAAU,EAAE,GAAG,GAAG,aAAa;KAChC,CAAC,CAAC;IAEH,qBAAqB;IACrB,MAAM,WAAW,GAAG,GAAG,GAAG,CAAC,MAAM,2BAA2B,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;IAExF,yDAAyD;IACzD,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;IACzD,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO;YACL,MAAM,EAAE,sBAAsB;YAC9B,GAAG,EAAE,WAAW;SACjB,CAAC;IACJ,CAAC;IAED,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,MAAM,sBAAsB,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;IACtF,OAAO;QACL,MAAM,EAAE,mBAAmB;QAC3B,QAAQ;KACT,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OAuthContext, CallbackInput, CallbackResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle OAuth callback after user authentication.
|
|
4
|
+
*
|
|
5
|
+
* This endpoint:
|
|
6
|
+
* 1. Verifies user is authenticated
|
|
7
|
+
* 2. Verifies email is allowed
|
|
8
|
+
* 3. Generates authorization code
|
|
9
|
+
* 4. Upgrades the session with the code
|
|
10
|
+
* 5. Redirects to client's redirect_uri
|
|
11
|
+
*/
|
|
12
|
+
export declare function handleCallback(ctx: OAuthContext, input: CallbackInput): Promise<CallbackResult>;
|
|
13
|
+
//# sourceMappingURL=callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/callback.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG/E;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,cAAc,CAAC,CAoEzB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// OAuth Callback Handler — Generate Authorization Code after Login
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
import { generateToken } from "../../token.js";
|
|
5
|
+
import { AUTH_CODE_TTL } from "../constants.js";
|
|
6
|
+
/**
|
|
7
|
+
* Handle OAuth callback after user authentication.
|
|
8
|
+
*
|
|
9
|
+
* This endpoint:
|
|
10
|
+
* 1. Verifies user is authenticated
|
|
11
|
+
* 2. Verifies email is allowed
|
|
12
|
+
* 3. Generates authorization code
|
|
13
|
+
* 4. Upgrades the session with the code
|
|
14
|
+
* 5. Redirects to client's redirect_uri
|
|
15
|
+
*/
|
|
16
|
+
export async function handleCallback(ctx, input) {
|
|
17
|
+
const { state } = input;
|
|
18
|
+
if (!state) {
|
|
19
|
+
return {
|
|
20
|
+
action: "error",
|
|
21
|
+
error: "Missing state parameter",
|
|
22
|
+
status: 400,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Verify user is authenticated
|
|
26
|
+
const email = await ctx.auth.getEmail();
|
|
27
|
+
if (!email) {
|
|
28
|
+
return {
|
|
29
|
+
action: "error",
|
|
30
|
+
error: "Authentication required",
|
|
31
|
+
status: 401,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Verify email is allowed
|
|
35
|
+
if (!ctx.auth.isEmailAllowed(email)) {
|
|
36
|
+
return {
|
|
37
|
+
action: "error",
|
|
38
|
+
error: "Email not authorized",
|
|
39
|
+
status: 403,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Look up authorization session by state
|
|
43
|
+
const session = await ctx.authCodes.findByState(state);
|
|
44
|
+
if (!session) {
|
|
45
|
+
return {
|
|
46
|
+
action: "error",
|
|
47
|
+
error: "Invalid or expired authorization session",
|
|
48
|
+
status: 400,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Generate authorization code (64 hex chars = 32 bytes)
|
|
52
|
+
const code = generateToken(32);
|
|
53
|
+
// Upgrade session with code and user email
|
|
54
|
+
const now = Math.floor(Date.now() / 1000);
|
|
55
|
+
const upgraded = await ctx.authCodes.upgrade(state, code, email, now + AUTH_CODE_TTL);
|
|
56
|
+
if (!upgraded) {
|
|
57
|
+
return {
|
|
58
|
+
action: "error",
|
|
59
|
+
error: "Authorization session already used or expired",
|
|
60
|
+
status: 400,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Redirect to client
|
|
64
|
+
const redirectUrl = new URL(session.redirect_uri);
|
|
65
|
+
redirectUrl.searchParams.set("code", code);
|
|
66
|
+
redirectUrl.searchParams.set("state", state);
|
|
67
|
+
return {
|
|
68
|
+
action: "redirect_to_client",
|
|
69
|
+
url: redirectUrl.toString(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.js","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/callback.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,8EAA8E;AAE9E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAiB,EACjB,KAAoB;IAEpB,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAExB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,yBAAyB;YAChC,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,+BAA+B;IAC/B,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,yBAAyB;YAChC,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,sBAAsB;YAC7B,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,0CAA0C;YACjD,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,wDAAwD;IACxD,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAE/B,2CAA2C;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAC1C,KAAK,EACL,IAAI,EACJ,KAAK,EACL,GAAG,GAAG,aAAa,CACpB,CAAC;IACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,+CAA+C;YACtD,MAAM,EAAE,GAAG;SACZ,CAAC;IACJ,CAAC;IAED,qBAAqB;IACrB,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3C,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,EAAE,oBAAoB;QAC5B,GAAG,EAAE,WAAW,CAAC,QAAQ,EAAE;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// OAuth Handlers Index — Export all handlers
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
export { handleRegister } from "./register.js";
|
|
5
|
+
export { handleAuthorize } from "./authorize.js";
|
|
6
|
+
export { handleCallback } from "./callback.js";
|
|
7
|
+
export { handleToken } from "./token.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6CAA6C;AAC7C,8EAA8E;AAE9E,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OAuthContext, RegisterInput, RegisterResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle dynamic client registration.
|
|
4
|
+
*
|
|
5
|
+
* Per RFC 7591, clients can register themselves to obtain a client_id.
|
|
6
|
+
* Only loopback redirect URIs are allowed (native CLI apps).
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleRegister(ctx: OAuthContext, input: RegisterInput): Promise<RegisterResult>;
|
|
9
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/auth/oauth/handlers/register.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,cAAc,CAAC,CAqDzB"}
|