offrouter-core 0.2.1 → 0.3.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/dist/audit.d.ts +137 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +320 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/credential-chain.d.ts +84 -0
- package/dist/auth/credential-chain.d.ts.map +1 -0
- package/dist/auth/credential-chain.js +150 -0
- package/dist/auth/credential-chain.js.map +1 -0
- package/dist/auth/index.d.ts +10 -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/keychain.d.ts +60 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +104 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-pkce.d.ts +185 -0
- package/dist/auth/oauth-pkce.d.ts.map +1 -0
- package/dist/auth/oauth-pkce.js +558 -0
- package/dist/auth/oauth-pkce.js.map +1 -0
- package/dist/auth/oauth-server.d.ts +38 -0
- package/dist/auth/oauth-server.d.ts.map +1 -0
- package/dist/auth/oauth-server.js +295 -0
- package/dist/auth/oauth-server.js.map +1 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +355 -0
- package/dist/config.js.map +1 -0
- package/dist/delegation.d.ts +123 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +455 -0
- package/dist/delegation.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +45 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +318 -0
- package/dist/policy.js.map +1 -0
- package/{src/protocol.ts → dist/protocol.d.ts} +2 -2
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +6 -0
- package/dist/protocol.js.map +1 -0
- package/dist/providers/adapter.d.ts +137 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/adapter.js +163 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/catalog-data.d.ts +128 -0
- package/dist/providers/catalog-data.d.ts.map +1 -0
- package/dist/providers/catalog-data.js +397 -0
- package/dist/providers/catalog-data.js.map +1 -0
- package/dist/providers/catalog.d.ts +63 -0
- package/dist/providers/catalog.d.ts.map +1 -0
- package/dist/providers/catalog.js +394 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/fake.d.ts +46 -0
- package/dist/providers/fake.d.ts.map +1 -0
- package/dist/providers/fake.js +234 -0
- package/dist/providers/fake.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +65 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +434 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/proxy/responses-mapper.d.ts +366 -0
- package/dist/proxy/responses-mapper.d.ts.map +1 -0
- package/dist/proxy/responses-mapper.js +517 -0
- package/dist/proxy/responses-mapper.js.map +1 -0
- package/dist/proxy/responses-server.d.ts +29 -0
- package/dist/proxy/responses-server.d.ts.map +1 -0
- package/dist/proxy/responses-server.js +360 -0
- package/dist/proxy/responses-server.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/schemas.d.ts +560 -0
- package/dist/schemas.d.ts.map +1 -0
- package/{src/schemas.ts → dist/schemas.js} +83 -103
- package/dist/schemas.js.map +1 -0
- package/dist/secrets.d.ts +112 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +326 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-file.d.ts +59 -0
- package/dist/usage-file.d.ts.map +1 -0
- package/dist/usage-file.js +316 -0
- package/dist/usage-file.js.map +1 -0
- package/dist/usage.d.ts +235 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +517 -0
- package/dist/usage.js.map +1 -0
- package/package.json +7 -2
- package/src/audit.test.ts +0 -302
- package/src/audit.ts +0 -553
- package/src/auth/credential-chain.test.ts +0 -326
- package/src/auth/credential-chain.ts +0 -235
- package/src/auth/index.ts +0 -45
- package/src/auth/keychain.test.ts +0 -265
- package/src/auth/keychain.ts +0 -168
- package/src/auth/oauth-pkce.test.ts +0 -329
- package/src/auth/oauth-pkce.ts +0 -571
- package/src/auth/oauth-server.test.ts +0 -83
- package/src/auth/oauth-server.ts +0 -296
- package/src/config.test.ts +0 -479
- package/src/config.ts +0 -505
- package/src/delegation.test.ts +0 -368
- package/src/delegation.ts +0 -605
- package/src/index.ts +0 -280
- package/src/policy.test.ts +0 -634
- package/src/policy.ts +0 -420
- package/src/providers/adapter.test.ts +0 -293
- package/src/providers/adapter.ts +0 -328
- package/src/providers/catalog-data.test.ts +0 -258
- package/src/providers/catalog-data.ts +0 -498
- package/src/providers/catalog.test.ts +0 -84
- package/src/providers/catalog.ts +0 -483
- package/src/providers/fake.ts +0 -312
- package/src/providers/openai-compatible.test.ts +0 -366
- package/src/providers/openai-compatible.ts +0 -554
- package/src/proxy/responses-mapper.test.ts +0 -290
- package/src/proxy/responses-mapper.ts +0 -736
- package/src/proxy/responses-server.test.ts +0 -322
- package/src/proxy/responses-server.ts +0 -469
- package/src/router.test.ts +0 -699
- package/src/router.ts +0 -352
- package/src/schemas.test.ts +0 -291
- package/src/secrets.test.ts +0 -271
- package/src/secrets.ts +0 -461
- package/src/types.ts +0 -173
- package/src/usage-file.test.ts +0 -243
- package/src/usage-file.ts +0 -435
- package/src/usage.test.ts +0 -335
- package/src/usage.ts +0 -859
- package/tsconfig.json +0 -9
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth PKCE and device code flow for OffRouter.
|
|
3
|
+
*
|
|
4
|
+
* Supports PKCE browser callback flow (Anthropic, OpenAI) and
|
|
5
|
+
* device code flow (xAI/Grok, GitHub Copilot).
|
|
6
|
+
* Gemini/Z.ai are API-key only and do NOT implement OAuth.
|
|
7
|
+
*
|
|
8
|
+
* Never auto-triggers OAuth. Requires explicit user configuration.
|
|
9
|
+
* Tokens are stored in the SecretStore (keychain or file fallback).
|
|
10
|
+
*/
|
|
11
|
+
import { randomBytes } from "node:crypto";
|
|
12
|
+
import { Redacted } from "./credential-chain.js";
|
|
13
|
+
import { setOAuthTokens } from "./keychain.js";
|
|
14
|
+
import { startCallbackServer } from "./oauth-server.js";
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// PKCE code generation
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Generate a random code verifier for PKCE (RFC 7636).
|
|
20
|
+
* Uses base64url encoding of 32 random bytes (43 chars).
|
|
21
|
+
*/
|
|
22
|
+
export function generateCodeVerifier() {
|
|
23
|
+
const bytes = randomBytes(32);
|
|
24
|
+
let binary = "";
|
|
25
|
+
for (const byte of bytes) {
|
|
26
|
+
binary += String.fromCharCode(byte);
|
|
27
|
+
}
|
|
28
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generate a S256 code challenge from a verifier (RFC 7636).
|
|
32
|
+
* Uses Web Crypto API (available in Node >= 22, Workers, browsers).
|
|
33
|
+
*/
|
|
34
|
+
export async function generateCodeChallenge(verifier) {
|
|
35
|
+
const encoder = new TextEncoder();
|
|
36
|
+
const data = encoder.encode(verifier);
|
|
37
|
+
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
38
|
+
const base64 = btoa(String.fromCharCode(...new Uint8Array(digest)));
|
|
39
|
+
return base64
|
|
40
|
+
.replace(/\+/g, "-")
|
|
41
|
+
.replace(/\//g, "_")
|
|
42
|
+
.replace(/=+$/, "");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* OAuth PKCE configurations for supported providers.
|
|
46
|
+
* Anthropic and OpenAI use PKCE browser callback flow.
|
|
47
|
+
* xAI (Grok) and GitHub Copilot use device code flow.
|
|
48
|
+
*/
|
|
49
|
+
export const PKCE_CONFIGS = {
|
|
50
|
+
anthropic: {
|
|
51
|
+
authorizeUrl: "https://claude.ai/oauth/authorize",
|
|
52
|
+
tokenUrl: "https://platform.claude.com/v1/oauth/token",
|
|
53
|
+
scopes: [
|
|
54
|
+
"org:create_api_key",
|
|
55
|
+
"user:profile",
|
|
56
|
+
"user:inference",
|
|
57
|
+
"user:sessions:claude_code",
|
|
58
|
+
"user:mcp_servers",
|
|
59
|
+
"user:file_upload",
|
|
60
|
+
],
|
|
61
|
+
clientIdEnvVar: "OFFROUTER_ANTHROPIC_CLIENT_ID",
|
|
62
|
+
defaultClientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
63
|
+
callbackPort: 53692,
|
|
64
|
+
callbackPath: "/callback",
|
|
65
|
+
},
|
|
66
|
+
openai: {
|
|
67
|
+
authorizeUrl: "https://auth.openai.com/oauth/authorize",
|
|
68
|
+
tokenUrl: "https://auth.openai.com/oauth/token",
|
|
69
|
+
scopes: ["openid", "profile", "email", "offline_access"],
|
|
70
|
+
clientIdEnvVar: "OFFROUTER_OPENAI_CLIENT_ID",
|
|
71
|
+
defaultClientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
72
|
+
callbackPort: 1455,
|
|
73
|
+
callbackPath: "/auth/callback",
|
|
74
|
+
},
|
|
75
|
+
xai: {
|
|
76
|
+
tokenUrl: "https://auth.x.ai/oauth2/token",
|
|
77
|
+
scopes: ["openid", "profile", "email", "offline_access", "grok-cli:access", "api:access"],
|
|
78
|
+
clientIdEnvVar: "OFFROUTER_XAI_CLIENT_ID",
|
|
79
|
+
defaultClientId: "b1a00492-073a-47ea-816f-4c329264a828",
|
|
80
|
+
deviceCodeUrl: "https://auth.x.ai/oauth2/device/code",
|
|
81
|
+
},
|
|
82
|
+
"github-copilot": {
|
|
83
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
84
|
+
scopes: [],
|
|
85
|
+
clientIdEnvVar: "OFFROUTER_GITHUB_COPILOT_CLIENT_ID",
|
|
86
|
+
defaultClientId: "Iv1.b507a08c87ecfe98",
|
|
87
|
+
deviceCodeUrl: "https://github.com/login/device/code",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Returns true if the provider supports OAuth (opt-in).
|
|
92
|
+
*/
|
|
93
|
+
export function isOAuthProvider(providerId) {
|
|
94
|
+
return providerId in PKCE_CONFIGS;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns true if the provider uses device code flow instead of PKCE browser callback.
|
|
98
|
+
*/
|
|
99
|
+
export function isDeviceCodeProvider(providerId) {
|
|
100
|
+
const config = PKCE_CONFIGS[providerId];
|
|
101
|
+
return !!config?.deviceCodeUrl;
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// OAuth session store (in-memory, out-of-band — NOT embedded in state)
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
/** Time-to-live for a stored OAuth session (10 minutes). */
|
|
107
|
+
const SESSION_TTL_MS = 10 * 60 * 1000;
|
|
108
|
+
/** Module-level session store: nonce → session. */
|
|
109
|
+
const sessions = new Map();
|
|
110
|
+
/**
|
|
111
|
+
* Retrieve and delete a session by nonce (one-shot consume).
|
|
112
|
+
* Returns null if the nonce is unknown, already consumed, or expired.
|
|
113
|
+
*/
|
|
114
|
+
export function consumeOAuthSession(nonce) {
|
|
115
|
+
const session = sessions.get(nonce);
|
|
116
|
+
if (!session)
|
|
117
|
+
return null;
|
|
118
|
+
sessions.delete(nonce);
|
|
119
|
+
if (Date.now() - session.createdAt > SESSION_TTL_MS) {
|
|
120
|
+
return null; // expired
|
|
121
|
+
}
|
|
122
|
+
return session;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Cancel / discard a session without consuming it.
|
|
126
|
+
*/
|
|
127
|
+
export function cancelOAuthSession(nonce) {
|
|
128
|
+
sessions.delete(nonce);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create an OAuth state string that encodes provider metadata.
|
|
132
|
+
* Used to validate the callback matches the initiated flow.
|
|
133
|
+
*/
|
|
134
|
+
export function createOAuthState(_provider) {
|
|
135
|
+
// Simple nonce string (not JSON) - some providers reject complex state values.
|
|
136
|
+
void _provider;
|
|
137
|
+
return randomBytes(16).toString("hex");
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Start an OAuth PKCE flow for the given provider.
|
|
141
|
+
* Returns the authorization URL and state for redirect.
|
|
142
|
+
* Never auto-triggers; requires explicit user configuration.
|
|
143
|
+
*/
|
|
144
|
+
export async function startOAuthFlow(provider, options) {
|
|
145
|
+
const config = PKCE_CONFIGS[provider];
|
|
146
|
+
if (!config || !config.authorizeUrl) {
|
|
147
|
+
throw new Error(`OAuth not supported for provider: ${provider}. ` +
|
|
148
|
+
`OAuth is only available for anthropic and openai providers.`);
|
|
149
|
+
}
|
|
150
|
+
const clientId = options.clientId ??
|
|
151
|
+
(config.clientIdEnvVar ? process.env[config.clientIdEnvVar] : undefined) ??
|
|
152
|
+
config.defaultClientId;
|
|
153
|
+
if (!clientId) {
|
|
154
|
+
throw new Error(`Client ID required for ${provider} OAuth. ` +
|
|
155
|
+
`Set the ${config.clientIdEnvVar ?? "client id"} environment variable ` +
|
|
156
|
+
`or pass it explicitly.`);
|
|
157
|
+
}
|
|
158
|
+
const codeVerifier = generateCodeVerifier();
|
|
159
|
+
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
160
|
+
// Use the verifier as the state (same pattern as pi/other CLIs).
|
|
161
|
+
// The callback server validates state === verifier.
|
|
162
|
+
const state = codeVerifier;
|
|
163
|
+
const params = new URLSearchParams({
|
|
164
|
+
code: "true",
|
|
165
|
+
response_type: "code",
|
|
166
|
+
client_id: clientId,
|
|
167
|
+
redirect_uri: options.redirectUri,
|
|
168
|
+
code_challenge: codeChallenge,
|
|
169
|
+
code_challenge_method: "S256",
|
|
170
|
+
state,
|
|
171
|
+
scope: config.scopes.join(" "),
|
|
172
|
+
});
|
|
173
|
+
const authorizeUrl = `${config.authorizeUrl}?${params.toString()}`;
|
|
174
|
+
// Store the verifier in the session, keyed by the state (= verifier).
|
|
175
|
+
// The callback validates state === verifier, then we consume the session.
|
|
176
|
+
sessions.set(state, {
|
|
177
|
+
codeVerifier,
|
|
178
|
+
redirectUri: options.redirectUri,
|
|
179
|
+
nonce: state,
|
|
180
|
+
createdAt: Date.now(),
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
authorizeUrl,
|
|
184
|
+
state, // simple nonce string, no secrets
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Exchange an authorization code for tokens.
|
|
189
|
+
* Called after the user completes the OAuth flow in their browser.
|
|
190
|
+
*/
|
|
191
|
+
export async function exchangeCodeForTokens(provider, code, statePayload, options) {
|
|
192
|
+
const config = PKCE_CONFIGS[provider];
|
|
193
|
+
if (!config) {
|
|
194
|
+
throw new Error(`OAuth not supported for provider: ${provider}`);
|
|
195
|
+
}
|
|
196
|
+
// statePayload is now a simple nonce string (not JSON).
|
|
197
|
+
const session = consumeOAuthSession(statePayload);
|
|
198
|
+
if (!session) {
|
|
199
|
+
throw new Error("OAuth session expired, already consumed, or not found. Please restart the OAuth flow.");
|
|
200
|
+
}
|
|
201
|
+
if (!session.codeVerifier) {
|
|
202
|
+
throw new Error("Missing code verifier in OAuth session");
|
|
203
|
+
}
|
|
204
|
+
const clientId = options.clientId ??
|
|
205
|
+
resolveClientId(provider) ??
|
|
206
|
+
"";
|
|
207
|
+
// Anthropic uses JSON body; all others use form-encoded.
|
|
208
|
+
// Anthropic requires the state field in the token exchange to match the
|
|
209
|
+
// state sent in the authorize URL.
|
|
210
|
+
const useJson = provider === "anthropic";
|
|
211
|
+
const tokenBody = useJson
|
|
212
|
+
? JSON.stringify({
|
|
213
|
+
grant_type: "authorization_code",
|
|
214
|
+
client_id: clientId,
|
|
215
|
+
code,
|
|
216
|
+
state: statePayload,
|
|
217
|
+
redirect_uri: session.redirectUri,
|
|
218
|
+
code_verifier: session.codeVerifier,
|
|
219
|
+
})
|
|
220
|
+
: new URLSearchParams({
|
|
221
|
+
grant_type: "authorization_code",
|
|
222
|
+
code,
|
|
223
|
+
redirect_uri: session.redirectUri,
|
|
224
|
+
client_id: clientId,
|
|
225
|
+
code_verifier: session.codeVerifier,
|
|
226
|
+
}).toString();
|
|
227
|
+
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
228
|
+
const response = await fetchImpl(config.tokenUrl, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: {
|
|
231
|
+
"content-type": useJson
|
|
232
|
+
? "application/json"
|
|
233
|
+
: "application/x-www-form-urlencoded",
|
|
234
|
+
accept: "application/json",
|
|
235
|
+
},
|
|
236
|
+
body: tokenBody,
|
|
237
|
+
});
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
const body = await response.text().catch(() => "");
|
|
240
|
+
throw new Error(`Token exchange failed for ${provider}: HTTP ${response.status} ${body}`);
|
|
241
|
+
}
|
|
242
|
+
const json = (await response.json());
|
|
243
|
+
if (!json.access_token) {
|
|
244
|
+
throw new Error(`Token exchange for ${provider} returned no access_token`);
|
|
245
|
+
}
|
|
246
|
+
// Store tokens in secret store (structured)
|
|
247
|
+
if (options.store) {
|
|
248
|
+
const obtainedAt = Date.now();
|
|
249
|
+
await setOAuthTokens(provider, {
|
|
250
|
+
accessToken: json.access_token,
|
|
251
|
+
refreshToken: json.refresh_token,
|
|
252
|
+
expiresAt: json.expires_in
|
|
253
|
+
? obtainedAt + json.expires_in * 1000
|
|
254
|
+
: undefined,
|
|
255
|
+
scope: json.scope,
|
|
256
|
+
obtainedAt,
|
|
257
|
+
}, options.store);
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
accessToken: new Redacted(json.access_token),
|
|
261
|
+
refreshToken: json.refresh_token
|
|
262
|
+
? new Redacted(json.refresh_token)
|
|
263
|
+
: undefined,
|
|
264
|
+
expiresIn: json.expires_in,
|
|
265
|
+
scope: json.scope,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Login orchestration (callback server + browser + token exchange)
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
/** Default loopback ports for supported providers. */
|
|
272
|
+
const DEFAULT_CALLBACK_PORTS = {
|
|
273
|
+
anthropic: 53692,
|
|
274
|
+
openai: 1455,
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Return the default callback port for a provider, or 0 (OS-assigned).
|
|
278
|
+
*/
|
|
279
|
+
export function defaultCallbackPort(provider) {
|
|
280
|
+
return DEFAULT_CALLBACK_PORTS[provider] ?? 0;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Resolve the client id for a provider from process env (or a provided env
|
|
284
|
+
* override). Returns undefined if the env var is not set.
|
|
285
|
+
*/
|
|
286
|
+
export function resolveClientId(provider, env = process.env) {
|
|
287
|
+
const config = PKCE_CONFIGS[provider];
|
|
288
|
+
if (!config)
|
|
289
|
+
return undefined;
|
|
290
|
+
return env[config.clientIdEnvVar ?? ""] ?? config.defaultClientId;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Run the full OAuth PKCE login flow:
|
|
294
|
+
* 1. Start a local callback server on 127.0.0.1
|
|
295
|
+
* 2. Build the authorize URL with PKCE challenge + state
|
|
296
|
+
* 3. Notify caller (onAuthorizeUrl) and attempt browser open
|
|
297
|
+
* 4. Wait for the provider callback (code + state)
|
|
298
|
+
* 5. Exchange code for tokens
|
|
299
|
+
* 6. Store structured tokens in the secret store
|
|
300
|
+
* 7. Clean up the server
|
|
301
|
+
*/
|
|
302
|
+
export async function runOAuthLogin(opts) {
|
|
303
|
+
const config = PKCE_CONFIGS[opts.provider];
|
|
304
|
+
if (!config) {
|
|
305
|
+
throw new Error(`OAuth not supported for provider: ${opts.provider}. ` +
|
|
306
|
+
`OAuth is only available for anthropic and openai providers.`);
|
|
307
|
+
}
|
|
308
|
+
const clientId = opts.clientId ?? resolveClientId(opts.provider) ?? undefined;
|
|
309
|
+
if (!clientId) {
|
|
310
|
+
const envHint = config.clientIdEnvVar
|
|
311
|
+
? `Set the ${config.clientIdEnvVar} environment variable or pass it explicitly.`
|
|
312
|
+
: "A client id is required for this provider.";
|
|
313
|
+
throw new Error(`Client ID required for ${opts.provider} OAuth. ${envHint}`);
|
|
314
|
+
}
|
|
315
|
+
// 1. Start callback server
|
|
316
|
+
const port = opts.port ?? defaultCallbackPort(opts.provider);
|
|
317
|
+
const callbackPath = config.callbackPath ?? "/callback";
|
|
318
|
+
const server = await startCallbackServer(port, { callbackPath });
|
|
319
|
+
try {
|
|
320
|
+
const redirectUri = server.url;
|
|
321
|
+
// 2. Build the authorize URL with PKCE
|
|
322
|
+
const flow = await startOAuthFlow(opts.provider, {
|
|
323
|
+
redirectUri,
|
|
324
|
+
clientId,
|
|
325
|
+
});
|
|
326
|
+
// 3. Notify and attempt to open browser
|
|
327
|
+
opts.onAuthorizeUrl?.(flow.authorizeUrl);
|
|
328
|
+
if (opts.openBrowser) {
|
|
329
|
+
try {
|
|
330
|
+
await opts.openBrowser(flow.authorizeUrl);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Best-effort only.
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// 4. Wait for callback (validates expected state)
|
|
337
|
+
const { code } = await server.waitForCode({
|
|
338
|
+
expectedState: flow.state,
|
|
339
|
+
timeoutMs: opts.timeoutMs,
|
|
340
|
+
signal: opts.signal,
|
|
341
|
+
});
|
|
342
|
+
// 5. Exchange code for tokens (also stores them)
|
|
343
|
+
const tokens = await exchangeCodeForTokens(opts.provider, code, flow.state, {
|
|
344
|
+
store: opts.store,
|
|
345
|
+
fetch: opts.fetch,
|
|
346
|
+
});
|
|
347
|
+
// 6. Tokens are already stored by exchangeCodeForTokens.
|
|
348
|
+
// Compute expiresAt from expiresIn for the result.
|
|
349
|
+
const expiresAt = tokens.expiresIn
|
|
350
|
+
? Date.now() + tokens.expiresIn * 1000
|
|
351
|
+
: undefined;
|
|
352
|
+
return {
|
|
353
|
+
provider: opts.provider,
|
|
354
|
+
redirectUri,
|
|
355
|
+
expiresAt,
|
|
356
|
+
scope: tokens.scope,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
// 7. Clean up
|
|
361
|
+
await server.close();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// Device code flow (xAI, GitHub Copilot)
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
/**
|
|
368
|
+
* Run the device code flow for providers that don't support PKCE browser callback.
|
|
369
|
+
*
|
|
370
|
+
* 1. Requests a device code from the provider
|
|
371
|
+
* 2. Prints the user code and verification URL
|
|
372
|
+
* 3. Attempts to open the browser to the verification URL
|
|
373
|
+
* 4. Polls the token endpoint until the user authorizes
|
|
374
|
+
* 5. Stores tokens in the secret store
|
|
375
|
+
*/
|
|
376
|
+
export async function startDeviceCodeFlow(provider, options) {
|
|
377
|
+
const config = PKCE_CONFIGS[provider];
|
|
378
|
+
if (!config || !config.deviceCodeUrl) {
|
|
379
|
+
throw new Error(`Device code flow not supported for provider: ${provider}`);
|
|
380
|
+
}
|
|
381
|
+
const clientId = options.clientId ??
|
|
382
|
+
(config.clientIdEnvVar ? process.env[config.clientIdEnvVar] : undefined) ??
|
|
383
|
+
config.defaultClientId;
|
|
384
|
+
if (!clientId) {
|
|
385
|
+
throw new Error(`Client ID required for ${provider} device code flow. ` +
|
|
386
|
+
`Set the ${config.clientIdEnvVar ?? "client id"} environment variable ` +
|
|
387
|
+
`or pass it explicitly.`);
|
|
388
|
+
}
|
|
389
|
+
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
390
|
+
// 1. Request device code
|
|
391
|
+
const deviceCodeParams = new URLSearchParams({
|
|
392
|
+
client_id: clientId,
|
|
393
|
+
scope: config.scopes.join(" "),
|
|
394
|
+
});
|
|
395
|
+
const deviceResponse = await fetchImpl(config.deviceCodeUrl, {
|
|
396
|
+
method: "POST",
|
|
397
|
+
headers: {
|
|
398
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
399
|
+
accept: "application/json",
|
|
400
|
+
},
|
|
401
|
+
body: deviceCodeParams.toString(),
|
|
402
|
+
});
|
|
403
|
+
if (!deviceResponse.ok) {
|
|
404
|
+
const body = await deviceResponse.text().catch(() => "");
|
|
405
|
+
throw new Error(`Device code request failed for ${provider}: HTTP ${deviceResponse.status} ${body}`);
|
|
406
|
+
}
|
|
407
|
+
const deviceData = (await deviceResponse.json());
|
|
408
|
+
if (!deviceData.device_code || !deviceData.user_code) {
|
|
409
|
+
throw new Error(`Device code request for ${provider} returned incomplete data`);
|
|
410
|
+
}
|
|
411
|
+
// 2. Notify caller with device code data
|
|
412
|
+
options.onDeviceCode?.(deviceData);
|
|
413
|
+
// 3. Attempt to open browser to verification URL
|
|
414
|
+
const verificationUrl = deviceData.verification_uri_complete ?? deviceData.verification_uri;
|
|
415
|
+
if (options.openBrowser) {
|
|
416
|
+
try {
|
|
417
|
+
await options.openBrowser(verificationUrl);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// Best-effort only.
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// 4. Poll for token
|
|
424
|
+
const pollInterval = (deviceData.interval ?? 5) * 1000;
|
|
425
|
+
const maxWaitMs = 5 * 60 * 1000; // 5 minutes
|
|
426
|
+
const startTime = Date.now();
|
|
427
|
+
while (true) {
|
|
428
|
+
if (options.signal?.aborted) {
|
|
429
|
+
throw new Error("Device code flow was aborted.");
|
|
430
|
+
}
|
|
431
|
+
if (Date.now() - startTime > maxWaitMs) {
|
|
432
|
+
throw new Error(`Device code flow timed out for ${provider} after ${maxWaitMs / 1000}s`);
|
|
433
|
+
}
|
|
434
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
435
|
+
const tokenParams = new URLSearchParams({
|
|
436
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
437
|
+
device_code: deviceData.device_code,
|
|
438
|
+
client_id: clientId,
|
|
439
|
+
});
|
|
440
|
+
const tokenResponse = await fetchImpl(config.tokenUrl, {
|
|
441
|
+
method: "POST",
|
|
442
|
+
headers: {
|
|
443
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
444
|
+
accept: "application/json",
|
|
445
|
+
},
|
|
446
|
+
body: tokenParams.toString(),
|
|
447
|
+
});
|
|
448
|
+
if (!tokenResponse.ok) {
|
|
449
|
+
const body = await tokenResponse.text().catch(() => "");
|
|
450
|
+
// Handle expected polling errors
|
|
451
|
+
if (body.includes("authorization_pending")) {
|
|
452
|
+
continue; // Keep polling
|
|
453
|
+
}
|
|
454
|
+
if (body.includes("slow_down")) {
|
|
455
|
+
// Increase interval by 5 seconds as per RFC
|
|
456
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (body.includes("expired_token")) {
|
|
460
|
+
throw new Error(`Device code expired for ${provider}. Please restart the login flow.`);
|
|
461
|
+
}
|
|
462
|
+
if (body.includes("access_denied")) {
|
|
463
|
+
throw new Error(`Device code flow denied for ${provider}.`);
|
|
464
|
+
}
|
|
465
|
+
throw new Error(`Token polling failed for ${provider}: HTTP ${tokenResponse.status} ${body}`);
|
|
466
|
+
}
|
|
467
|
+
const json = (await tokenResponse.json());
|
|
468
|
+
if (!json.access_token) {
|
|
469
|
+
throw new Error(`Token polling for ${provider} returned no access_token`);
|
|
470
|
+
}
|
|
471
|
+
// 5. Store tokens in secret store
|
|
472
|
+
const obtainedAt = Date.now();
|
|
473
|
+
await setOAuthTokens(provider, {
|
|
474
|
+
accessToken: json.access_token,
|
|
475
|
+
refreshToken: json.refresh_token,
|
|
476
|
+
expiresAt: json.expires_in
|
|
477
|
+
? obtainedAt + json.expires_in * 1000
|
|
478
|
+
: undefined,
|
|
479
|
+
scope: json.scope,
|
|
480
|
+
obtainedAt,
|
|
481
|
+
}, options.store);
|
|
482
|
+
return {
|
|
483
|
+
provider,
|
|
484
|
+
expiresAt: json.expires_in
|
|
485
|
+
? obtainedAt + json.expires_in * 1000
|
|
486
|
+
: undefined,
|
|
487
|
+
scope: json.scope,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Refresh an OAuth token using a refresh token.
|
|
493
|
+
* Updates the stored tokens on success.
|
|
494
|
+
*/
|
|
495
|
+
export async function refreshTokens(provider, refreshToken, store, options) {
|
|
496
|
+
const config = PKCE_CONFIGS[provider];
|
|
497
|
+
if (!config) {
|
|
498
|
+
throw new Error(`OAuth not supported for provider: ${provider}`);
|
|
499
|
+
}
|
|
500
|
+
if (!store) {
|
|
501
|
+
throw new Error("SecretStore is required for token refresh");
|
|
502
|
+
}
|
|
503
|
+
const clientId = config.clientIdEnvVar
|
|
504
|
+
? (process.env[config.clientIdEnvVar] ?? "")
|
|
505
|
+
: "";
|
|
506
|
+
// Anthropic uses JSON body; OpenAI uses form-encoded.
|
|
507
|
+
const useJson = provider === "anthropic";
|
|
508
|
+
const refreshBody = useJson
|
|
509
|
+
? JSON.stringify({
|
|
510
|
+
grant_type: "refresh_token",
|
|
511
|
+
refresh_token: refreshToken,
|
|
512
|
+
client_id: clientId,
|
|
513
|
+
})
|
|
514
|
+
: new URLSearchParams({
|
|
515
|
+
grant_type: "refresh_token",
|
|
516
|
+
refresh_token: refreshToken,
|
|
517
|
+
client_id: clientId,
|
|
518
|
+
}).toString();
|
|
519
|
+
const fetchImpl = options?.fetch ?? globalThis.fetch.bind(globalThis);
|
|
520
|
+
const response = await fetchImpl(config.tokenUrl, {
|
|
521
|
+
method: "POST",
|
|
522
|
+
headers: {
|
|
523
|
+
"content-type": useJson
|
|
524
|
+
? "application/json"
|
|
525
|
+
: "application/x-www-form-urlencoded",
|
|
526
|
+
accept: "application/json",
|
|
527
|
+
},
|
|
528
|
+
body: refreshBody,
|
|
529
|
+
});
|
|
530
|
+
if (!response.ok) {
|
|
531
|
+
const body = await response.text().catch(() => "");
|
|
532
|
+
throw new Error(`Token refresh failed for ${provider}: HTTP ${response.status} ${body}`);
|
|
533
|
+
}
|
|
534
|
+
const json = (await response.json());
|
|
535
|
+
if (!json.access_token) {
|
|
536
|
+
throw new Error(`Token refresh for ${provider} returned no access_token`);
|
|
537
|
+
}
|
|
538
|
+
// Update stored tokens (structured)
|
|
539
|
+
const obtainedAt = Date.now();
|
|
540
|
+
await setOAuthTokens(provider, {
|
|
541
|
+
accessToken: json.access_token,
|
|
542
|
+
refreshToken: json.refresh_token,
|
|
543
|
+
expiresAt: json.expires_in
|
|
544
|
+
? obtainedAt + json.expires_in * 1000
|
|
545
|
+
: undefined,
|
|
546
|
+
scope: json.scope,
|
|
547
|
+
obtainedAt,
|
|
548
|
+
}, store);
|
|
549
|
+
return {
|
|
550
|
+
accessToken: new Redacted(json.access_token),
|
|
551
|
+
refreshToken: json.refresh_token
|
|
552
|
+
? new Redacted(json.refresh_token)
|
|
553
|
+
: undefined,
|
|
554
|
+
expiresIn: json.expires_in,
|
|
555
|
+
scope: json.scope,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
//# sourceMappingURL=oauth-pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-pkce.js","sourceRoot":"","sources":["../../src/auth/oauth-pkce.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface CallbackServerOptions {
|
|
2
|
+
/**
|
|
3
|
+
* State value expected from the provider. Validated in waitForCode against
|
|
4
|
+
* the state returned by the redirect; a mismatch rejects the promise.
|
|
5
|
+
* May also be supplied per-call to waitForCode.
|
|
6
|
+
*/
|
|
7
|
+
expectedState?: string;
|
|
8
|
+
/** Callback path (default: /callback). OpenAI uses /auth/callback. */
|
|
9
|
+
callbackPath?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface WaitForCodeOptions {
|
|
12
|
+
/** Overrides the server-level expectedState for this call. */
|
|
13
|
+
expectedState?: string;
|
|
14
|
+
/** How long to wait for a callback before rejecting (default 5 minutes). */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Optional abort signal to cancel the wait early. */
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
export interface CallbackCode {
|
|
20
|
+
code: string;
|
|
21
|
+
state: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CallbackServerHandle {
|
|
24
|
+
/** Full redirect_uri to register with the provider (http://localhost:PORT/callback). */
|
|
25
|
+
url: string;
|
|
26
|
+
/** Actual bound port (useful when 0 was requested). */
|
|
27
|
+
port: number;
|
|
28
|
+
/** Resolve with {code, state} on a valid callback; reject on timeout/state mismatch. */
|
|
29
|
+
waitForCode(options?: WaitForCodeOptions): Promise<CallbackCode>;
|
|
30
|
+
/** Stop listening and free the port. Idempotent. */
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Start a loopback HTTP server that captures an OAuth authorization code.
|
|
35
|
+
* Pass port 0 to let the OS choose a free port (read handle.port afterwards).
|
|
36
|
+
*/
|
|
37
|
+
export declare function startCallbackServer(port: number, options?: CallbackServerOptions): Promise<CallbackServerHandle>;
|
|
38
|
+
//# sourceMappingURL=oauth-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-server.d.ts","sourceRoot":"","sources":["../../src/auth/oauth-server.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACjE,oDAAoD;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CA8K/B"}
|