@sonoma-security/mcp-gateway 0.1.0
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 +82 -0
- package/dist/auth/client.d.ts +35 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +217 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/server.d.ts +26 -0
- package/dist/auth/server.d.ts.map +1 -0
- package/dist/auth/server.js +155 -0
- package/dist/auth/server.js.map +1 -0
- package/dist/auth/storage.d.ts +58 -0
- package/dist/auth/storage.d.ts.map +1 -0
- package/dist/auth/storage.js +181 -0
- package/dist/auth/storage.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +272 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +134 -0
- package/dist/config.js.map +1 -0
- package/dist/gateway.d.ts +66 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +346 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/sonoma-client.d.ts +55 -0
- package/dist/sonoma-client.d.ts.map +1 -0
- package/dist/sonoma-client.js +144 -0
- package/dist/sonoma-client.js.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @sonoma/mcp-gateway
|
|
2
|
+
|
|
3
|
+
Local MCP proxy for tool-level visibility. Intercepts stdio MCP servers, aggregates tools, forwards calls.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# With config file
|
|
9
|
+
bun run src/cli.ts --config config.json
|
|
10
|
+
|
|
11
|
+
# Auto-detect Claude Desktop config
|
|
12
|
+
bun run src/cli.ts --auto --debug
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Config Format
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"servers": [
|
|
20
|
+
{ "name": "fs", "command": "npx", "args": ["-y", "@anthropic-ai/mcp-server-filesystem", "/tmp"] }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Also accepts Claude Desktop `mcpServers` format.
|
|
26
|
+
|
|
27
|
+
## Add to MCP Client
|
|
28
|
+
|
|
29
|
+
**Cursor** (`~/.cursor/mcp.json`):
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"sonoma": {
|
|
34
|
+
"command": "bun",
|
|
35
|
+
"args": ["run", "/path/to/packages/mcp-gateway/src/cli.ts", "--config", "/path/to/config.json"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Claude Code**:
|
|
42
|
+
```bash
|
|
43
|
+
claude mcp add sonoma -- bun run /path/to/packages/mcp-gateway/src/cli.ts --config /path/to/config.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Run tests (vitest)
|
|
50
|
+
bun run test
|
|
51
|
+
|
|
52
|
+
# Watch mode
|
|
53
|
+
bun run test -- --watch
|
|
54
|
+
|
|
55
|
+
# Typecheck
|
|
56
|
+
bun run typecheck
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Manual JSON-RPC Testing
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
cat << 'EOF' | bun run src/cli.ts --config tests/fixtures/echo-server.json
|
|
63
|
+
{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}
|
|
64
|
+
{"jsonrpc":"2.0","method":"tools/list","id":2}
|
|
65
|
+
EOF
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Architecture
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
AI Client (stdio) → Gateway → [Upstream MCP Servers]
|
|
72
|
+
↓
|
|
73
|
+
Tools namespaced as serverName__toolName
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Status
|
|
77
|
+
|
|
78
|
+
- [x] Core proxy (tools/list, tools/call forwarding)
|
|
79
|
+
- [x] Config loader (native + Claude Desktop format)
|
|
80
|
+
- [x] CLI
|
|
81
|
+
- [ ] Telemetry reporter
|
|
82
|
+
- [ ] Policy enforcement
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple OAuth 2.0 client with Dynamic Client Registration (DCR)
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Discover auth endpoints via /.well-known/oauth-authorization-server
|
|
6
|
+
* 2. Register client via DCR (if not already registered)
|
|
7
|
+
* 3. Authorization code flow with PKCE
|
|
8
|
+
* 4. Store tokens, auto-refresh when expired
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthClientOptions {
|
|
11
|
+
sonomaEndpoint: string;
|
|
12
|
+
debug?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Login via OAuth - opens browser, waits for callback
|
|
16
|
+
*/
|
|
17
|
+
export declare function login(options: AuthClientOptions): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Get valid access token - refreshes if expired
|
|
20
|
+
*/
|
|
21
|
+
export declare function getAccessToken(options: AuthClientOptions): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Logout - clear stored credentials
|
|
24
|
+
*/
|
|
25
|
+
export declare function logout(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Get auth status
|
|
28
|
+
*/
|
|
29
|
+
export declare function getAuthStatus(): {
|
|
30
|
+
loggedIn: boolean;
|
|
31
|
+
endpoint?: string;
|
|
32
|
+
expiresAt?: Date;
|
|
33
|
+
hasRefreshToken: boolean;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2HD;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkErE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiChF;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,IAAI,CAG7B;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;CAC1B,CASA"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple OAuth 2.0 client with Dynamic Client Registration (DCR)
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Discover auth endpoints via /.well-known/oauth-authorization-server
|
|
6
|
+
* 2. Register client via DCR (if not already registered)
|
|
7
|
+
* 3. Authorization code flow with PKCE
|
|
8
|
+
* 4. Store tokens, auto-refresh when expired
|
|
9
|
+
*/
|
|
10
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
11
|
+
import { loadCredentials, saveCredentials, clearCredentials } from "./storage";
|
|
12
|
+
import { startCallbackServer, getCallbackUrl } from "./server";
|
|
13
|
+
const CALLBACK_PORT = 19842;
|
|
14
|
+
const CLIENT_NAME = "Sonoma MCP Gateway";
|
|
15
|
+
function log(debug, msg, ...args) {
|
|
16
|
+
if (debug)
|
|
17
|
+
console.error(`[auth] ${msg}`, ...args);
|
|
18
|
+
}
|
|
19
|
+
/** Discover OAuth endpoints */
|
|
20
|
+
async function discoverAuthServer(sonomaUrl) {
|
|
21
|
+
const url = `${sonomaUrl}/.well-known/oauth-authorization-server`;
|
|
22
|
+
const res = await fetch(url);
|
|
23
|
+
if (!res.ok)
|
|
24
|
+
throw new Error(`Failed to discover auth server: ${res.status}`);
|
|
25
|
+
return res.json();
|
|
26
|
+
}
|
|
27
|
+
/** Generate PKCE code verifier and challenge */
|
|
28
|
+
function generatePKCE() {
|
|
29
|
+
const verifier = randomBytes(32).toString("base64url");
|
|
30
|
+
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
31
|
+
return { verifier, challenge };
|
|
32
|
+
}
|
|
33
|
+
/** Open URL in browser */
|
|
34
|
+
async function openBrowser(url) {
|
|
35
|
+
const { exec } = await import("node:child_process");
|
|
36
|
+
const platform = process.platform;
|
|
37
|
+
if (platform === "darwin") {
|
|
38
|
+
exec(`open "${url}"`);
|
|
39
|
+
}
|
|
40
|
+
else if (platform === "win32") {
|
|
41
|
+
exec(`start "" "${url}"`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
exec(`xdg-open "${url}"`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Register client via DCR */
|
|
48
|
+
async function registerClient(metadata, debug) {
|
|
49
|
+
if (!metadata.registration_endpoint) {
|
|
50
|
+
throw new Error("Auth server does not support Dynamic Client Registration");
|
|
51
|
+
}
|
|
52
|
+
log(debug, "Registering client via DCR...");
|
|
53
|
+
const res = await fetch(metadata.registration_endpoint, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
client_name: CLIENT_NAME,
|
|
58
|
+
redirect_uris: [getCallbackUrl(CALLBACK_PORT)],
|
|
59
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
60
|
+
response_types: ["code"],
|
|
61
|
+
token_endpoint_auth_method: "none", // Public client (PKCE only)
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
const error = await res.text();
|
|
66
|
+
throw new Error(`DCR failed: ${res.status} - ${error}`);
|
|
67
|
+
}
|
|
68
|
+
const data = await res.json();
|
|
69
|
+
log(debug, "Client registered:", data.client_id);
|
|
70
|
+
return {
|
|
71
|
+
clientId: data.client_id,
|
|
72
|
+
clientSecret: data.client_secret,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Exchange authorization code for tokens */
|
|
76
|
+
async function exchangeCode(metadata, code, codeVerifier, clientId) {
|
|
77
|
+
const res = await fetch(metadata.token_endpoint, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
80
|
+
body: new URLSearchParams({
|
|
81
|
+
grant_type: "authorization_code",
|
|
82
|
+
code,
|
|
83
|
+
redirect_uri: getCallbackUrl(CALLBACK_PORT),
|
|
84
|
+
client_id: clientId,
|
|
85
|
+
code_verifier: codeVerifier,
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const error = await res.text();
|
|
90
|
+
throw new Error(`Token exchange failed: ${res.status} - ${error}`);
|
|
91
|
+
}
|
|
92
|
+
return res.json();
|
|
93
|
+
}
|
|
94
|
+
/** Refresh access token */
|
|
95
|
+
async function refreshTokens(metadata, refreshToken, clientId) {
|
|
96
|
+
const res = await fetch(metadata.token_endpoint, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
99
|
+
body: new URLSearchParams({
|
|
100
|
+
grant_type: "refresh_token",
|
|
101
|
+
refresh_token: refreshToken,
|
|
102
|
+
client_id: clientId,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
const error = await res.text();
|
|
107
|
+
throw new Error(`Token refresh failed: ${res.status} - ${error}`);
|
|
108
|
+
}
|
|
109
|
+
return res.json();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Login via OAuth - opens browser, waits for callback
|
|
113
|
+
*/
|
|
114
|
+
export async function login(options) {
|
|
115
|
+
const { sonomaEndpoint, debug = false } = options;
|
|
116
|
+
log(debug, "Starting login flow...");
|
|
117
|
+
// 1. Discover auth endpoints
|
|
118
|
+
const metadata = await discoverAuthServer(sonomaEndpoint);
|
|
119
|
+
log(debug, "Auth server:", metadata.issuer);
|
|
120
|
+
// 2. Check if we have a registered client, or register one
|
|
121
|
+
let creds = loadCredentials();
|
|
122
|
+
if (!creds?.clientId || creds.sonomaEndpoint !== sonomaEndpoint) {
|
|
123
|
+
const client = await registerClient(metadata, debug);
|
|
124
|
+
creds = {
|
|
125
|
+
clientId: client.clientId,
|
|
126
|
+
clientSecret: client.clientSecret,
|
|
127
|
+
sonomaEndpoint,
|
|
128
|
+
};
|
|
129
|
+
saveCredentials(creds);
|
|
130
|
+
}
|
|
131
|
+
// 3. Generate PKCE
|
|
132
|
+
const pkce = generatePKCE();
|
|
133
|
+
// 4. Build authorization URL
|
|
134
|
+
const state = randomBytes(16).toString("hex");
|
|
135
|
+
const authUrl = new URL(metadata.authorization_endpoint);
|
|
136
|
+
authUrl.searchParams.set("response_type", "code");
|
|
137
|
+
authUrl.searchParams.set("client_id", creds.clientId);
|
|
138
|
+
authUrl.searchParams.set("redirect_uri", getCallbackUrl(CALLBACK_PORT));
|
|
139
|
+
authUrl.searchParams.set("code_challenge", pkce.challenge);
|
|
140
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
141
|
+
authUrl.searchParams.set("state", state);
|
|
142
|
+
authUrl.searchParams.set("scope", "openid profile email");
|
|
143
|
+
// 5. Start callback server and open browser
|
|
144
|
+
console.error("\nOpening browser for authentication...");
|
|
145
|
+
console.error(`If browser doesn't open, visit:\n${authUrl.toString()}\n`);
|
|
146
|
+
const callbackPromise = startCallbackServer({ port: CALLBACK_PORT, debug });
|
|
147
|
+
await openBrowser(authUrl.toString());
|
|
148
|
+
// 6. Wait for callback
|
|
149
|
+
const result = await callbackPromise;
|
|
150
|
+
if (result.state !== state) {
|
|
151
|
+
throw new Error("State mismatch - possible CSRF attack");
|
|
152
|
+
}
|
|
153
|
+
log(debug, "Received authorization code");
|
|
154
|
+
// 7. Exchange code for tokens
|
|
155
|
+
const tokens = await exchangeCode(metadata, result.code, pkce.verifier, creds.clientId);
|
|
156
|
+
log(debug, "Got tokens, expires_in:", tokens.expires_in);
|
|
157
|
+
// 8. Save tokens
|
|
158
|
+
saveCredentials({
|
|
159
|
+
...creds,
|
|
160
|
+
accessToken: tokens.access_token,
|
|
161
|
+
refreshToken: tokens.refresh_token,
|
|
162
|
+
tokenType: tokens.token_type,
|
|
163
|
+
scope: tokens.scope,
|
|
164
|
+
expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
|
|
165
|
+
lastRefreshed: Date.now(),
|
|
166
|
+
});
|
|
167
|
+
console.error("\nAuthentication successful!");
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get valid access token - refreshes if expired
|
|
171
|
+
*/
|
|
172
|
+
export async function getAccessToken(options) {
|
|
173
|
+
const { sonomaEndpoint, debug = false } = options;
|
|
174
|
+
const creds = loadCredentials();
|
|
175
|
+
if (!creds?.accessToken) {
|
|
176
|
+
throw new Error("Not logged in. Run: sonoma-gateway --login");
|
|
177
|
+
}
|
|
178
|
+
if (creds.sonomaEndpoint !== sonomaEndpoint) {
|
|
179
|
+
throw new Error(`Logged in to different endpoint: ${creds.sonomaEndpoint}`);
|
|
180
|
+
}
|
|
181
|
+
// Check if token needs refresh (5 min buffer)
|
|
182
|
+
const needsRefresh = creds.expiresAt && Date.now() + 300000 >= creds.expiresAt;
|
|
183
|
+
if (needsRefresh && creds.refreshToken && creds.clientId) {
|
|
184
|
+
log(debug, "Refreshing token...");
|
|
185
|
+
const metadata = await discoverAuthServer(sonomaEndpoint);
|
|
186
|
+
const tokens = await refreshTokens(metadata, creds.refreshToken, creds.clientId);
|
|
187
|
+
saveCredentials({
|
|
188
|
+
...creds,
|
|
189
|
+
accessToken: tokens.access_token,
|
|
190
|
+
refreshToken: tokens.refresh_token || creds.refreshToken,
|
|
191
|
+
expiresAt: tokens.expires_in ? Date.now() + tokens.expires_in * 1000 : undefined,
|
|
192
|
+
lastRefreshed: Date.now(),
|
|
193
|
+
});
|
|
194
|
+
return tokens.access_token;
|
|
195
|
+
}
|
|
196
|
+
return creds.accessToken;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Logout - clear stored credentials
|
|
200
|
+
*/
|
|
201
|
+
export function logout() {
|
|
202
|
+
clearCredentials();
|
|
203
|
+
console.error("Logged out successfully");
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get auth status
|
|
207
|
+
*/
|
|
208
|
+
export function getAuthStatus() {
|
|
209
|
+
const creds = loadCredentials();
|
|
210
|
+
return {
|
|
211
|
+
loggedIn: !!creds?.accessToken,
|
|
212
|
+
endpoint: creds?.sonomaEndpoint,
|
|
213
|
+
expiresAt: creds?.expiresAt ? new Date(creds.expiresAt) : undefined,
|
|
214
|
+
hasRefreshToken: !!creds?.refreshToken,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/auth/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/D,MAAM,aAAa,GAAG,KAAK,CAAC;AAC5B,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAsBzC,SAAS,GAAG,CAAC,KAAc,EAAE,GAAW,EAAE,GAAG,IAAe;IAC1D,IAAI,KAAK;QAAE,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,+BAA+B;AAC/B,KAAK,UAAU,kBAAkB,CAAC,SAAiB;IACjD,MAAM,GAAG,GAAG,GAAG,SAAS,yCAAyC,CAAC;IAClE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,gDAAgD;AAChD,SAAS,YAAY;IACnB,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,0BAA0B;AAC1B,KAAK,UAAU,WAAW,CAAC,GAAW;IACpC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAElC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;IACxB,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,8BAA8B;AAC9B,KAAK,UAAU,cAAc,CAC3B,QAA4B,EAC5B,KAAc;IAEd,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,GAAG,CAAC,KAAK,EAAE,+BAA+B,CAAC,CAAC;IAE5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,qBAAqB,EAAE;QACtD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,WAAW,EAAE,WAAW;YACxB,aAAa,EAAE,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YAC9C,WAAW,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACpD,cAAc,EAAE,CAAC,MAAM,CAAC;YACxB,0BAA0B,EAAE,MAAM,EAAE,4BAA4B;SACjE,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,GAAG,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAEjD,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,SAAS;QACxB,YAAY,EAAE,IAAI,CAAC,aAAa;KACjC,CAAC;AACJ,CAAC;AAED,6CAA6C;AAC7C,KAAK,UAAU,YAAY,CACzB,QAA4B,EAC5B,IAAY,EACZ,YAAoB,EACpB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,cAAc,EAAE;QAC/C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,UAAU,EAAE,oBAAoB;YAChC,IAAI;YACJ,YAAY,EAAE,cAAc,CAAC,aAAa,CAAC;YAC3C,SAAS,EAAE,QAAQ;YACnB,aAAa,EAAE,YAAY;SAC5B,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,2BAA2B;AAC3B,KAAK,UAAU,aAAa,CAC1B,QAA4B,EAC5B,YAAoB,EACpB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,cAAc,EAAE;QAC/C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,YAAY;YAC3B,SAAS,EAAE,QAAQ;SACpB,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAA0B;IACpD,MAAM,EAAE,cAAc,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAElD,GAAG,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;IAErC,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAC1D,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE5C,2DAA2D;IAC3D,IAAI,KAAK,GAAG,eAAe,EAAE,CAAC;IAC9B,IAAI,CAAC,KAAK,EAAE,QAAQ,IAAI,KAAK,CAAC,cAAc,KAAK,cAAc,EAAE,CAAC;QAChE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACrD,KAAK,GAAG;YACN,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,cAAc;SACf,CAAC;QACF,eAAe,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,mBAAmB;IACnB,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAE5B,6BAA6B;IAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;IACzD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAClD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,QAAS,CAAC,CAAC;IACvD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAC3D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;IAC1D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAE1D,4CAA4C;IAC5C,OAAO,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACzD,OAAO,CAAC,KAAK,CAAC,oCAAoC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAE1E,MAAM,eAAe,GAAG,mBAAmB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5E,MAAM,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEtC,uBAAuB;IACvB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;IACrC,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,GAAG,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAC;IAE1C,8BAA8B;IAC9B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAS,CAAC,CAAC;IACzF,GAAG,CAAC,KAAK,EAAE,yBAAyB,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAEzD,iBAAiB;IACjB,eAAe,CAAC;QACd,GAAG,KAAK;QACR,WAAW,EAAE,MAAM,CAAC,YAAY;QAChC,YAAY,EAAE,MAAM,CAAC,aAAa;QAClC,SAAS,EAAE,MAAM,CAAC,UAAU;QAC5B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS;QAChF,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;KAC1B,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAA0B;IAC7D,MAAM,EAAE,cAAc,EAAE,KAAK,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAElD,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,KAAK,CAAC,cAAc,KAAK,cAAc,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,8CAA8C;IAC9C,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC;IAE/E,IAAI,YAAY,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACzD,GAAG,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,QAAkB,CAAC,CAAC;QAE3F,eAAe,CAAC;YACd,GAAG,KAAK;YACR,WAAW,EAAE,MAAM,CAAC,YAAY;YAChC,YAAY,EAAE,MAAM,CAAC,aAAa,IAAI,KAAK,CAAC,YAAY;YACxD,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS;YAChF,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;SAC1B,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,YAAY,CAAC;IAC7B,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM;IACpB,gBAAgB,EAAE,CAAC;IACnB,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAM3B,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAEhC,OAAO;QACL,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW;QAC9B,QAAQ,EAAE,KAAK,EAAE,cAAc;QAC/B,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;QACnE,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY;KACvC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth module exports
|
|
3
|
+
*/
|
|
4
|
+
export { login, logout, getAccessToken, getAuthStatus, type AuthClientOptions } from "./client";
|
|
5
|
+
export { loadCredentials, saveCredentials, clearCredentials, getStoredDeviceId } from "./storage";
|
|
6
|
+
export { startCallbackServer, getCallbackUrl } from "./server";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth module exports
|
|
3
|
+
*/
|
|
4
|
+
export { login, logout, getAccessToken, getAuthStatus } from "./client";
|
|
5
|
+
export { loadCredentials, saveCredentials, clearCredentials, getStoredDeviceId } from "./storage";
|
|
6
|
+
export { startCallbackServer, getCallbackUrl } from "./server";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAA0B,MAAM,UAAU,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local HTTP callback server for OAuth redirect
|
|
3
|
+
*
|
|
4
|
+
* Starts a temporary HTTP server to receive the OAuth authorization code
|
|
5
|
+
* from the browser redirect after user authentication.
|
|
6
|
+
*/
|
|
7
|
+
export interface CallbackResult {
|
|
8
|
+
code: string;
|
|
9
|
+
state?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CallbackServerOptions {
|
|
12
|
+
port?: number;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Start a local HTTP server and wait for OAuth callback
|
|
18
|
+
*
|
|
19
|
+
* @returns Promise that resolves with the authorization code when received
|
|
20
|
+
*/
|
|
21
|
+
export declare function startCallbackServer(options?: CallbackServerOptions): Promise<CallbackResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Get the callback URL for OAuth configuration
|
|
24
|
+
*/
|
|
25
|
+
export declare function getCallbackUrl(port?: number): string;
|
|
26
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/auth/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,cAAc,CAAC,CAsJhG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAe,GAAG,MAAM,CAE1D"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local HTTP callback server for OAuth redirect
|
|
3
|
+
*
|
|
4
|
+
* Starts a temporary HTTP server to receive the OAuth authorization code
|
|
5
|
+
* from the browser redirect after user authentication.
|
|
6
|
+
*/
|
|
7
|
+
import { createServer } from "node:http";
|
|
8
|
+
const DEFAULT_PORT = 19842;
|
|
9
|
+
const TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
10
|
+
/**
|
|
11
|
+
* Start a local HTTP server and wait for OAuth callback
|
|
12
|
+
*
|
|
13
|
+
* @returns Promise that resolves with the authorization code when received
|
|
14
|
+
*/
|
|
15
|
+
export function startCallbackServer(options = {}) {
|
|
16
|
+
const port = options.port || DEFAULT_PORT;
|
|
17
|
+
const timeout = options.timeout || TIMEOUT_MS;
|
|
18
|
+
const debug = options.debug || false;
|
|
19
|
+
const log = (message, ...args) => {
|
|
20
|
+
if (debug) {
|
|
21
|
+
console.error(`[callback-server] ${message}`, ...args);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
let server = null;
|
|
26
|
+
let timeoutId = null;
|
|
27
|
+
const cleanup = () => {
|
|
28
|
+
if (timeoutId) {
|
|
29
|
+
clearTimeout(timeoutId);
|
|
30
|
+
timeoutId = null;
|
|
31
|
+
}
|
|
32
|
+
if (server) {
|
|
33
|
+
server.close();
|
|
34
|
+
server = null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
// Set up timeout
|
|
38
|
+
timeoutId = setTimeout(() => {
|
|
39
|
+
cleanup();
|
|
40
|
+
reject(new Error("OAuth callback timeout - no response received within 5 minutes"));
|
|
41
|
+
}, timeout);
|
|
42
|
+
server = createServer((req, res) => {
|
|
43
|
+
log("Received request:", req.url);
|
|
44
|
+
// Only handle GET /callback
|
|
45
|
+
if (!req.url?.startsWith("/callback")) {
|
|
46
|
+
res.writeHead(404);
|
|
47
|
+
res.end("Not Found");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
51
|
+
const code = url.searchParams.get("code");
|
|
52
|
+
const state = url.searchParams.get("state");
|
|
53
|
+
const error = url.searchParams.get("error");
|
|
54
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
55
|
+
// Handle OAuth error
|
|
56
|
+
if (error) {
|
|
57
|
+
log("OAuth error:", error, errorDescription);
|
|
58
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
59
|
+
res.end(`
|
|
60
|
+
<!DOCTYPE html>
|
|
61
|
+
<html>
|
|
62
|
+
<head>
|
|
63
|
+
<title>Authentication Failed</title>
|
|
64
|
+
<style>
|
|
65
|
+
body { font-family: system-ui, sans-serif; padding: 40px; max-width: 600px; margin: 0 auto; }
|
|
66
|
+
h1 { color: #dc2626; }
|
|
67
|
+
.error { background: #fef2f2; border: 1px solid #fecaca; padding: 16px; border-radius: 8px; }
|
|
68
|
+
code { background: #f3f4f6; padding: 2px 6px; border-radius: 4px; }
|
|
69
|
+
</style>
|
|
70
|
+
</head>
|
|
71
|
+
<body>
|
|
72
|
+
<h1>Authentication Failed</h1>
|
|
73
|
+
<div class="error">
|
|
74
|
+
<p><strong>Error:</strong> <code>${error}</code></p>
|
|
75
|
+
${errorDescription ? `<p>${errorDescription}</p>` : ""}
|
|
76
|
+
</div>
|
|
77
|
+
<p>You can close this window and try again.</p>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
80
|
+
`);
|
|
81
|
+
cleanup();
|
|
82
|
+
reject(new Error(`OAuth error: ${error}${errorDescription ? ` - ${errorDescription}` : ""}`));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Handle missing code
|
|
86
|
+
if (!code) {
|
|
87
|
+
log("Missing authorization code");
|
|
88
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
89
|
+
res.end(`
|
|
90
|
+
<!DOCTYPE html>
|
|
91
|
+
<html>
|
|
92
|
+
<head>
|
|
93
|
+
<title>Authentication Failed</title>
|
|
94
|
+
<style>
|
|
95
|
+
body { font-family: system-ui, sans-serif; padding: 40px; max-width: 600px; margin: 0 auto; }
|
|
96
|
+
h1 { color: #dc2626; }
|
|
97
|
+
</style>
|
|
98
|
+
</head>
|
|
99
|
+
<body>
|
|
100
|
+
<h1>Authentication Failed</h1>
|
|
101
|
+
<p>No authorization code received. Please try again.</p>
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
`);
|
|
105
|
+
cleanup();
|
|
106
|
+
reject(new Error("No authorization code received"));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Success!
|
|
110
|
+
log("Received authorization code");
|
|
111
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
112
|
+
res.end(`
|
|
113
|
+
<!DOCTYPE html>
|
|
114
|
+
<html>
|
|
115
|
+
<head>
|
|
116
|
+
<title>Authentication Successful</title>
|
|
117
|
+
<style>
|
|
118
|
+
body { font-family: system-ui, sans-serif; padding: 40px; max-width: 600px; margin: 0 auto; }
|
|
119
|
+
h1 { color: #16a34a; }
|
|
120
|
+
.success { background: #f0fdf4; border: 1px solid #bbf7d0; padding: 16px; border-radius: 8px; }
|
|
121
|
+
</style>
|
|
122
|
+
</head>
|
|
123
|
+
<body>
|
|
124
|
+
<h1>Authentication Successful</h1>
|
|
125
|
+
<div class="success">
|
|
126
|
+
<p>You have been authenticated with Sonoma MCP Gateway.</p>
|
|
127
|
+
<p>You can close this window and return to your terminal.</p>
|
|
128
|
+
</div>
|
|
129
|
+
</body>
|
|
130
|
+
</html>
|
|
131
|
+
`);
|
|
132
|
+
cleanup();
|
|
133
|
+
resolve({ code, state: state || undefined });
|
|
134
|
+
});
|
|
135
|
+
server.on("error", (err) => {
|
|
136
|
+
cleanup();
|
|
137
|
+
if (err.code === "EADDRINUSE") {
|
|
138
|
+
reject(new Error(`Port ${port} is already in use. Is another gateway login in progress?`));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
reject(err);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
server.listen(port, "127.0.0.1", () => {
|
|
145
|
+
log(`Callback server listening on http://localhost:${port}/callback`);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the callback URL for OAuth configuration
|
|
151
|
+
*/
|
|
152
|
+
export function getCallbackUrl(port = DEFAULT_PORT) {
|
|
153
|
+
return `http://localhost:${port}/callback`;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/auth/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure token storage for MCP Gateway OAuth credentials
|
|
3
|
+
*
|
|
4
|
+
* Stores tokens in ~/.sonoma/gateway-credentials.json with AES-256-GCM encryption.
|
|
5
|
+
* The encryption key is derived from a machine-specific identifier to prevent
|
|
6
|
+
* tokens from being copied between machines.
|
|
7
|
+
*/
|
|
8
|
+
import type { OAuthTokens, OAuthClientInformationMixed } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
9
|
+
export interface StoredCredentials {
|
|
10
|
+
accessToken?: string;
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
expiresAt?: number;
|
|
13
|
+
tokenType?: string;
|
|
14
|
+
scope?: string;
|
|
15
|
+
clientId?: string;
|
|
16
|
+
clientSecret?: string;
|
|
17
|
+
clientSecretExpiresAt?: number;
|
|
18
|
+
codeVerifier?: string;
|
|
19
|
+
sonomaEndpoint?: string;
|
|
20
|
+
lastRefreshed?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load stored credentials from disk
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadCredentials(): StoredCredentials | null;
|
|
26
|
+
/**
|
|
27
|
+
* Save credentials to disk (encrypted)
|
|
28
|
+
*/
|
|
29
|
+
export declare function saveCredentials(credentials: StoredCredentials): void;
|
|
30
|
+
/**
|
|
31
|
+
* Clear all stored credentials
|
|
32
|
+
*/
|
|
33
|
+
export declare function clearCredentials(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Convert stored credentials to MCP SDK OAuthTokens format
|
|
36
|
+
*/
|
|
37
|
+
export declare function toOAuthTokens(creds: StoredCredentials): OAuthTokens | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Convert MCP SDK OAuthTokens to stored credentials format
|
|
40
|
+
*/
|
|
41
|
+
export declare function fromOAuthTokens(tokens: OAuthTokens, existing?: StoredCredentials): StoredCredentials;
|
|
42
|
+
/**
|
|
43
|
+
* Convert stored credentials to MCP SDK client information format
|
|
44
|
+
*/
|
|
45
|
+
export declare function toClientInformation(creds: StoredCredentials): OAuthClientInformationMixed | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Check if stored tokens are expired or about to expire
|
|
48
|
+
*/
|
|
49
|
+
export declare function isTokenExpired(creds: StoredCredentials, bufferMs?: number): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Check if we have a valid refresh token
|
|
52
|
+
*/
|
|
53
|
+
export declare function hasRefreshToken(creds: StoredCredentials): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Get device ID for telemetry
|
|
56
|
+
*/
|
|
57
|
+
export declare function getStoredDeviceId(): string;
|
|
58
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AAYzG,MAAM,WAAW,iBAAiB;IAEhC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAgFD;;GAEG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CAc1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAOpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAY/E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAYpG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,2BAA2B,GAAG,SAAS,CAUrG;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,SAAQ,GAAG,OAAO,CAMlF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|