@glubean/oauth-code 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 ADDED
@@ -0,0 +1,152 @@
1
+ # @glubean/oauth-code
2
+
3
+ OAuth Authorization Code flow plugin for [Glubean](https://glubean.dev) — interactive token acquisition for explore mode.
4
+
5
+ On first HTTP request, opens the system browser for OAuth login, starts a local callback server on `127.0.0.1`, exchanges the authorization code for tokens, and caches them to disk. Subsequent requests use the cached token, refreshing automatically when expired.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @glubean/oauth-code
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```ts
16
+ import { test, configure } from "@glubean/sdk";
17
+ import { oauthCode } from "@glubean/oauth-code";
18
+
19
+ const { http } = configure({
20
+ http: oauthCode({
21
+ prefixUrl: "https://api.github.com",
22
+ authorizeUrl: "https://github.com/login/oauth/authorize",
23
+ tokenUrl: "https://github.com/login/oauth/access_token",
24
+ clientId: "{{GITHUB_CLIENT_ID}}",
25
+ clientSecret: "{{GITHUB_CLIENT_SECRET}}",
26
+ scopes: ["repo", "read:user"],
27
+ }),
28
+ });
29
+
30
+ export const me = test("github-me", async (ctx) => {
31
+ const res = await http.get("user").json<{ login: string }>();
32
+ ctx.assert(res.login, "got user login");
33
+ });
34
+ ```
35
+
36
+ First run opens the browser for login. Subsequent runs use the cached token.
37
+
38
+ ## How It Works
39
+
40
+ ```
41
+ 1. Test calls http.get("/some-endpoint")
42
+ 2. beforeRequest hook checks for cached token
43
+ ├─ Memory cache valid? → use it
44
+ ├─ Disk cache valid? → use it
45
+ ├─ Has refresh_token? → refresh automatically
46
+ └─ Nothing cached? → start browser flow:
47
+ a. Start local HTTP server on 127.0.0.1 (random port)
48
+ b. Open system browser with authorize URL
49
+ c. User logs in and authorizes
50
+ d. Provider redirects to 127.0.0.1/callback?code=xxx
51
+ e. Exchange code for access_token + refresh_token
52
+ f. Cache to .glubean/tokens/, close server
53
+ 3. Set Authorization: Bearer <token> header
54
+ 4. Request proceeds
55
+ ```
56
+
57
+ ## API
58
+
59
+ ### `oauthCode(options)` — ConfigureHttpOptions factory
60
+
61
+ Returns `ConfigureHttpOptions` for use with `configure({ http })`. All string options support `{{template}}` placeholders resolved from Glubean vars and secrets.
62
+
63
+ | Option | Type | Default | Description |
64
+ |--------|------|---------|-------------|
65
+ | `prefixUrl` | `string` | — | Base URL for API requests |
66
+ | `authorizeUrl` | `string` | — | OAuth authorization endpoint |
67
+ | `tokenUrl` | `string` | — | OAuth token endpoint |
68
+ | `clientId` | `string` | — | Client ID |
69
+ | `clientSecret` | `string?` | — | Client secret (optional for public clients with PKCE) |
70
+ | `scopes` | `string[]?` | — | OAuth scopes |
71
+ | `pkce` | `boolean` | `true` | Enable PKCE with S256 challenge |
72
+ | `cacheDir` | `string` | `".glubean/tokens"` | Token cache directory |
73
+ | `redirectUri` | `string?` | — | Override redirect URI (for tunnel setups, see below) |
74
+ | `port` | `number?` | random | Fixed port for callback server (required with `redirectUri`) |
75
+ | `authorizeParams` | `Record<string, string>?` | — | Extra query parameters for authorize URL |
76
+ | `openBrowser` | `(url: string) => void` | system default | Custom browser opener |
77
+
78
+ ## Token Caching
79
+
80
+ Tokens are cached to `{cacheDir}/{hash}.json` (default: `.glubean/tokens/`). The hash is derived from `clientId + authorizeUrl + scopes` to avoid collisions between providers or different scope sets.
81
+
82
+ Cache files are written with `0600` permissions (owner read/write only).
83
+
84
+ Add `.glubean/tokens/` to your `.gitignore`.
85
+
86
+ ## PKCE
87
+
88
+ PKCE (S256) is enabled by default. This is required by some providers (e.g., Twitter/X) and recommended by RFC 7636 for all public clients. Set `pkce: false` to disable for providers that don't support it.
89
+
90
+ ## Provider Compatibility
91
+
92
+ | Provider | Localhost redirect | Tunnel needed | Notes |
93
+ |----------|-------------------|---------------|-------|
94
+ | GitHub | `127.0.0.1` ✅ | No | Port-flexible |
95
+ | Google | `127.0.0.1` ✅ | No | Port > 1024 |
96
+ | Microsoft | `127.0.0.1` ✅ | No | Port ignored in matching |
97
+ | Spotify | `127.0.0.1` ✅ | No | Port-flexible |
98
+ | Twitter/X | ❌ | Yes | No loopback redirect support |
99
+ | Slack | ❌ | Yes | Requires HTTPS for all redirect URIs |
100
+
101
+ ### Providers that require a tunnel (Twitter/X, Slack)
102
+
103
+ These providers reject `http://127.0.0.1` as a redirect URI. Use an HTTPS tunnel like [ngrok](https://ngrok.com) to forward traffic to the local callback server:
104
+
105
+ ```bash
106
+ # 1. Start a tunnel on a fixed port
107
+ ngrok http 9876
108
+ # → https://abc123.ngrok-free.app
109
+ ```
110
+
111
+ ```ts
112
+ // 2. Register the ngrok URL as a redirect URI with the provider, then:
113
+ const { http } = configure({
114
+ http: oauthCode({
115
+ prefixUrl: "https://api.x.com/2",
116
+ authorizeUrl: "https://twitter.com/i/oauth2/authorize",
117
+ tokenUrl: "https://api.x.com/2/oauth2/token",
118
+ clientId: "{{TWITTER_CLIENT_ID}}",
119
+ scopes: ["tweet.read", "users.read"],
120
+ redirectUri: "https://abc123.ngrok-free.app/callback",
121
+ port: 9876,
122
+ }),
123
+ });
124
+ ```
125
+
126
+ The local server still listens on `127.0.0.1:9876`; ngrok tunnels the HTTPS callback back to it.
127
+
128
+ > **Note:** Postman solves this differently — it hosts its own cloud relay at `oauth.pstmn.io/callback` so users never need a tunnel. A similar Glubean Cloud relay might be added in the future, though this introduces a security trade-off: the relay becomes a credential intermediary that handles authorization codes on behalf of users, significantly expanding the trust boundary.
129
+
130
+ ## Promoting to CI
131
+
132
+ This plugin is designed for **explore mode** (interactive local development). When promoting tests to CI:
133
+
134
+ 1. Replace `oauthCode()` with a non-interactive auth method from `@glubean/auth`:
135
+ - `oauth2.clientCredentials()` — if the provider supports it
136
+ - `oauth2.refreshToken()` — with a pre-provisioned refresh token
137
+ - `bearer()` — with a pre-provisioned access token
138
+ 2. Test logic stays the same — only the `configure({ http })` line changes.
139
+
140
+ ## Scope
141
+
142
+ This is a v1 focused on the authorization code flow for explore mode. Not included:
143
+
144
+ - Device code flow (RFC 8628)
145
+ - Implicit grant (deprecated)
146
+ - Token revocation
147
+ - Multi-account support
148
+ - Custom TLS / proxy configuration
149
+
150
+ ## License
151
+
152
+ MIT
@@ -0,0 +1,71 @@
1
+ import type { ConfigureHttpOptions } from "@glubean/sdk";
2
+ export interface OAuthCodeOptions {
3
+ /** Base URL for API requests — var key or literal */
4
+ prefixUrl: string;
5
+ /** OAuth authorization endpoint URL */
6
+ authorizeUrl: string;
7
+ /** OAuth token endpoint URL */
8
+ tokenUrl: string;
9
+ /** Client ID — literal or `{{SECRET}}` reference */
10
+ clientId: string;
11
+ /** Client secret — literal or `{{SECRET}}` reference (optional for public clients with PKCE) */
12
+ clientSecret?: string;
13
+ /** OAuth scopes */
14
+ scopes?: string[];
15
+ /** Enable PKCE with S256 (default: true) */
16
+ pkce?: boolean;
17
+ /** Token cache directory (default: ".glubean/tokens") */
18
+ cacheDir?: string;
19
+ /**
20
+ * Override the redirect URI sent to the provider.
21
+ * Use with `port` for providers that reject `http://127.0.0.1` (e.g., Slack, Twitter/X).
22
+ * The local callback server still listens on 127.0.0.1; set this to the external URL
23
+ * that tunnels traffic back (e.g., an ngrok HTTPS URL).
24
+ *
25
+ * @example ngrok tunnel
26
+ * ```ts
27
+ * // 1. Run: ngrok http 9876
28
+ * // 2. Register https://abc123.ngrok.io/callback with the provider
29
+ * oauthCode({
30
+ * redirectUri: "https://abc123.ngrok.io/callback",
31
+ * port: 9876,
32
+ * ...
33
+ * })
34
+ * ```
35
+ */
36
+ redirectUri?: string;
37
+ /** Fixed port for the local callback server (default: random). Required when using `redirectUri` with a tunnel. */
38
+ port?: number;
39
+ /** Extra query parameters for the authorize URL */
40
+ authorizeParams?: Record<string, string>;
41
+ /** Custom function to open a URL in the browser (default: system browser via `open`/`xdg-open`) */
42
+ openBrowser?: (url: string) => void;
43
+ }
44
+ export declare function generateCodeVerifier(): string;
45
+ export declare function generateCodeChallenge(verifier: string): string;
46
+ /**
47
+ * OAuth Authorization Code flow for Glubean explore mode.
48
+ *
49
+ * On first HTTP request, opens the system browser for OAuth login,
50
+ * starts a local server to receive the callback, exchanges the code
51
+ * for tokens, and caches them to disk. Subsequent requests use the
52
+ * cached token, refreshing automatically when expired.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { oauthCode } from "@glubean/oauth-code";
57
+ *
58
+ * const { http } = configure({
59
+ * http: oauthCode({
60
+ * prefixUrl: "https://api.github.com",
61
+ * authorizeUrl: "https://github.com/login/oauth/authorize",
62
+ * tokenUrl: "https://github.com/login/oauth/access_token",
63
+ * clientId: "{{GITHUB_CLIENT_ID}}",
64
+ * clientSecret: "{{GITHUB_CLIENT_SECRET}}",
65
+ * scopes: ["repo", "read:user"],
66
+ * }),
67
+ * });
68
+ * ```
69
+ */
70
+ export declare function oauthCode(opts: OAuthCodeOptions): ConfigureHttpOptions;
71
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAsB,MAAM,cAAc,CAAC;AAS7E,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mHAAmH;IACnH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,mGAAmG;IACnG,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAmBD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;AA2ND;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,CA0MtE"}
package/dist/index.js ADDED
@@ -0,0 +1,373 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { createServer } from "node:http";
3
+ import { exec } from "node:child_process";
4
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ // ── Marker Headers ───────────────────────────────────────────────────────────
7
+ const AUTH_URL_H = "X-Glubean-OAuthCode-AuthUrl";
8
+ const TOKEN_URL_H = "X-Glubean-OAuthCode-TokenUrl";
9
+ const CLIENT_ID_H = "X-Glubean-OAuthCode-ClientId";
10
+ const CLIENT_SECRET_H = "X-Glubean-OAuthCode-ClientSecret";
11
+ const ALL_MARKERS = [AUTH_URL_H, TOKEN_URL_H, CLIENT_ID_H, CLIENT_SECRET_H];
12
+ function cleanMarkers(request) {
13
+ const h = new Headers(request.headers);
14
+ for (const m of ALL_MARKERS)
15
+ h.delete(m);
16
+ return h;
17
+ }
18
+ // ── PKCE ─────────────────────────────────────────────────────────────────────
19
+ export function generateCodeVerifier() {
20
+ return randomBytes(32).toString("base64url");
21
+ }
22
+ export function generateCodeChallenge(verifier) {
23
+ return createHash("sha256").update(verifier).digest("base64url");
24
+ }
25
+ // ── Request Rebuild ──────────────────────────────────────────────────────────
26
+ async function rebuildRequest(request, headers) {
27
+ const bodyBuffer = request.body
28
+ ? await request.clone().arrayBuffer()
29
+ : null;
30
+ return new Request(request.url, {
31
+ method: request.method,
32
+ headers,
33
+ body: bodyBuffer,
34
+ redirect: request.redirect,
35
+ signal: request.signal,
36
+ ...(bodyBuffer ? { duplex: "half" } : {}),
37
+ });
38
+ }
39
+ function cacheKey(clientId, authorizeUrl, scopes, authorizeParams) {
40
+ const paramsStr = authorizeParams
41
+ ? Object.keys(authorizeParams).sort().map((k) => `${k}=${authorizeParams[k]}`).join("&")
42
+ : "";
43
+ return createHash("sha256")
44
+ .update(`${clientId}:${authorizeUrl}:${scopes ?? ""}:${paramsStr}`)
45
+ .digest("hex")
46
+ .slice(0, 12);
47
+ }
48
+ async function readCache(dir, key) {
49
+ try {
50
+ const data = await readFile(join(dir, `${key}.json`), "utf-8");
51
+ return JSON.parse(data);
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ async function writeCache(dir, key, token) {
58
+ await mkdir(dir, { recursive: true });
59
+ await writeFile(join(dir, `${key}.json`), JSON.stringify(token, null, 2), { mode: 0o600 });
60
+ }
61
+ function startCallbackServer(port) {
62
+ return new Promise((resolveServer) => {
63
+ let resolveCode;
64
+ let rejectCode;
65
+ let codePromise = null;
66
+ let expectedState = null;
67
+ const server = createServer((req, res) => {
68
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
69
+ if (url.pathname !== "/callback") {
70
+ res.writeHead(404);
71
+ res.end();
72
+ return;
73
+ }
74
+ const error = url.searchParams.get("error");
75
+ if (error) {
76
+ const desc = url.searchParams.get("error_description") ?? error;
77
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
78
+ res.end(html("Authorization Failed", `Error: ${desc}. You can close this window.`));
79
+ rejectCode(new Error(`OAuth authorization failed: ${desc}`));
80
+ return;
81
+ }
82
+ const code = url.searchParams.get("code");
83
+ const state = url.searchParams.get("state");
84
+ if (!code) {
85
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
86
+ res.end(html("Missing Code", "No authorization code received."));
87
+ rejectCode(new Error("OAuth callback missing code parameter"));
88
+ return;
89
+ }
90
+ if (expectedState && state !== expectedState) {
91
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
92
+ res.end(html("Invalid State", "State mismatch — possible CSRF attack."));
93
+ rejectCode(new Error("OAuth state mismatch"));
94
+ return;
95
+ }
96
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
97
+ res.end(html("Authorization Successful", "You can close this window."));
98
+ resolveCode(code);
99
+ });
100
+ server.listen(port ?? 0, "127.0.0.1", () => {
101
+ const addr = server.address();
102
+ resolveServer({
103
+ port: addr.port,
104
+ waitForCode(state) {
105
+ if (!codePromise) {
106
+ expectedState = state;
107
+ codePromise = new Promise((res, rej) => {
108
+ resolveCode = res;
109
+ rejectCode = rej;
110
+ });
111
+ }
112
+ return codePromise;
113
+ },
114
+ close: () => server.close(),
115
+ });
116
+ });
117
+ });
118
+ }
119
+ function html(title, message) {
120
+ return `<!DOCTYPE html><html><head><title>${title}</title></head><body><h2>${title}</h2><p>${message}</p></body></html>`;
121
+ }
122
+ // ── Browser Open ─────────────────────────────────────────────────────────────
123
+ function openBrowser(url) {
124
+ const cmd = process.platform === "darwin" ? "open" :
125
+ process.platform === "win32" ? "start" :
126
+ "xdg-open";
127
+ exec(`${cmd} ${JSON.stringify(url)}`);
128
+ }
129
+ async function exchangeCode(params) {
130
+ const body = new URLSearchParams({
131
+ grant_type: "authorization_code",
132
+ code: params.code,
133
+ redirect_uri: params.redirectUri,
134
+ client_id: params.clientId,
135
+ });
136
+ if (params.clientSecret)
137
+ body.set("client_secret", params.clientSecret);
138
+ if (params.codeVerifier)
139
+ body.set("code_verifier", params.codeVerifier);
140
+ const res = await fetch(params.tokenUrl, {
141
+ method: "POST",
142
+ headers: {
143
+ "Content-Type": "application/x-www-form-urlencoded",
144
+ Accept: "application/json",
145
+ },
146
+ body,
147
+ });
148
+ if (!res.ok) {
149
+ throw new Error(`OAuth token exchange failed (${res.status}): ${await res.text()}`);
150
+ }
151
+ return (await res.json());
152
+ }
153
+ async function refreshAccessToken(params) {
154
+ const body = new URLSearchParams({
155
+ grant_type: "refresh_token",
156
+ refresh_token: params.refreshToken,
157
+ client_id: params.clientId,
158
+ });
159
+ if (params.clientSecret)
160
+ body.set("client_secret", params.clientSecret);
161
+ const res = await fetch(params.tokenUrl, {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/x-www-form-urlencoded",
165
+ Accept: "application/json",
166
+ },
167
+ body,
168
+ });
169
+ if (!res.ok) {
170
+ throw new Error(`OAuth token refresh failed (${res.status}): ${await res.text()}`);
171
+ }
172
+ return (await res.json());
173
+ }
174
+ // ── Main ─────────────────────────────────────────────────────────────────────
175
+ /**
176
+ * OAuth Authorization Code flow for Glubean explore mode.
177
+ *
178
+ * On first HTTP request, opens the system browser for OAuth login,
179
+ * starts a local server to receive the callback, exchanges the code
180
+ * for tokens, and caches them to disk. Subsequent requests use the
181
+ * cached token, refreshing automatically when expired.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * import { oauthCode } from "@glubean/oauth-code";
186
+ *
187
+ * const { http } = configure({
188
+ * http: oauthCode({
189
+ * prefixUrl: "https://api.github.com",
190
+ * authorizeUrl: "https://github.com/login/oauth/authorize",
191
+ * tokenUrl: "https://github.com/login/oauth/access_token",
192
+ * clientId: "{{GITHUB_CLIENT_ID}}",
193
+ * clientSecret: "{{GITHUB_CLIENT_SECRET}}",
194
+ * scopes: ["repo", "read:user"],
195
+ * }),
196
+ * });
197
+ * ```
198
+ */
199
+ export function oauthCode(opts) {
200
+ const usePkce = opts.pkce !== false;
201
+ const cacheDir = opts.cacheDir ?? ".glubean/tokens";
202
+ const scopes = opts.scopes?.join(" ");
203
+ const open = opts.openBrowser ?? openBrowser;
204
+ let cached = null;
205
+ let diskChecked = false;
206
+ let inflight = null;
207
+ const headers = {
208
+ [AUTH_URL_H]: opts.authorizeUrl,
209
+ [TOKEN_URL_H]: opts.tokenUrl,
210
+ [CLIENT_ID_H]: opts.clientId,
211
+ };
212
+ if (opts.clientSecret)
213
+ headers[CLIENT_SECRET_H] = opts.clientSecret;
214
+ /** Read resolved marker values from the request (template-resolved by SDK). */
215
+ function readMarkers(request) {
216
+ return {
217
+ authorizeUrl: request.headers.get(AUTH_URL_H) ?? opts.authorizeUrl,
218
+ tokenUrl: request.headers.get(TOKEN_URL_H) ?? opts.tokenUrl,
219
+ clientId: request.headers.get(CLIENT_ID_H) ?? opts.clientId,
220
+ clientSecret: request.headers.get(CLIENT_SECRET_H) ?? undefined,
221
+ };
222
+ }
223
+ function toCache(data, prev) {
224
+ return {
225
+ accessToken: data.access_token,
226
+ refreshToken: data.refresh_token ?? prev?.refreshToken,
227
+ expiresAt: Date.now() + (data.expires_in ?? 3600) * 1000,
228
+ };
229
+ }
230
+ async function tryRefresh(tokenUrl, refreshToken, clientId, clientSecret) {
231
+ try {
232
+ const data = await refreshAccessToken({ tokenUrl, refreshToken, clientId, clientSecret });
233
+ return toCache(data, cached ?? undefined);
234
+ }
235
+ catch {
236
+ return null;
237
+ }
238
+ }
239
+ async function acquireToken(request) {
240
+ const m = readMarkers(request);
241
+ const key = cacheKey(m.clientId, m.authorizeUrl, scopes, opts.authorizeParams);
242
+ // 1. Memory cache — still valid
243
+ if (cached && cached.expiresAt > Date.now() + 30_000) {
244
+ return cached;
245
+ }
246
+ // 2. Disk cache
247
+ if (!diskChecked) {
248
+ diskChecked = true;
249
+ const disk = await readCache(cacheDir, key);
250
+ if (disk) {
251
+ if (disk.expiresAt > Date.now() + 30_000) {
252
+ cached = disk;
253
+ return cached;
254
+ }
255
+ // Expired but has refresh token
256
+ if (disk.refreshToken) {
257
+ const refreshed = await tryRefresh(m.tokenUrl, disk.refreshToken, m.clientId, m.clientSecret);
258
+ if (refreshed) {
259
+ cached = refreshed;
260
+ await writeCache(cacheDir, key, cached);
261
+ return cached;
262
+ }
263
+ }
264
+ }
265
+ }
266
+ // 3. Memory cache has refresh token
267
+ if (cached?.refreshToken) {
268
+ const refreshed = await tryRefresh(m.tokenUrl, cached.refreshToken, m.clientId, m.clientSecret);
269
+ if (refreshed) {
270
+ cached = refreshed;
271
+ await writeCache(cacheDir, key, cached);
272
+ return cached;
273
+ }
274
+ }
275
+ // 4. Browser flow
276
+ const server = await startCallbackServer(opts.port);
277
+ const redirectUri = opts.redirectUri ?? `http://127.0.0.1:${server.port}/callback`;
278
+ const state = randomBytes(16).toString("hex");
279
+ const authUrl = new URL(m.authorizeUrl);
280
+ authUrl.searchParams.set("response_type", "code");
281
+ authUrl.searchParams.set("client_id", m.clientId);
282
+ authUrl.searchParams.set("redirect_uri", redirectUri);
283
+ authUrl.searchParams.set("state", state);
284
+ if (scopes)
285
+ authUrl.searchParams.set("scope", scopes);
286
+ // Extra authorize params
287
+ if (opts.authorizeParams) {
288
+ for (const [k, v] of Object.entries(opts.authorizeParams)) {
289
+ authUrl.searchParams.set(k, v);
290
+ }
291
+ }
292
+ let codeVerifier;
293
+ if (usePkce) {
294
+ codeVerifier = generateCodeVerifier();
295
+ authUrl.searchParams.set("code_challenge", generateCodeChallenge(codeVerifier));
296
+ authUrl.searchParams.set("code_challenge_method", "S256");
297
+ }
298
+ process.stderr.write(`\n OAuth login required. Opening browser...\n ${authUrl.toString()}\n\n`);
299
+ open(authUrl.toString());
300
+ try {
301
+ const code = await server.waitForCode(state);
302
+ const data = await exchangeCode({
303
+ tokenUrl: m.tokenUrl,
304
+ code,
305
+ redirectUri,
306
+ clientId: m.clientId,
307
+ clientSecret: m.clientSecret,
308
+ codeVerifier,
309
+ });
310
+ cached = toCache(data);
311
+ await writeCache(cacheDir, key, cached);
312
+ return cached;
313
+ }
314
+ finally {
315
+ server.close();
316
+ }
317
+ }
318
+ /** Serialize token acquisition — concurrent callers share the same promise. */
319
+ function ensureToken(request) {
320
+ // Fast path: valid memory cache, no serialization needed
321
+ if (cached && cached.expiresAt > Date.now() + 30_000) {
322
+ return Promise.resolve(cached);
323
+ }
324
+ // Slow path: serialize so only one browser flow / refresh runs at a time
325
+ if (inflight)
326
+ return inflight;
327
+ inflight = acquireToken(request).finally(() => { inflight = null; });
328
+ return inflight;
329
+ }
330
+ return {
331
+ prefixUrl: opts.prefixUrl,
332
+ headers,
333
+ hooks: {
334
+ beforeRequest: [
335
+ async (request) => {
336
+ const token = await ensureToken(request);
337
+ const h = cleanMarkers(request);
338
+ h.set("Authorization", `Bearer ${token.accessToken}`);
339
+ return rebuildRequest(request, h);
340
+ },
341
+ ],
342
+ afterResponse: [
343
+ async (request, _options, response) => {
344
+ if (response.status !== 401 || !cached?.refreshToken)
345
+ return;
346
+ const m = readMarkers(request);
347
+ const refreshed = await tryRefresh(m.tokenUrl, cached.refreshToken, m.clientId, m.clientSecret);
348
+ if (!refreshed)
349
+ return;
350
+ cached = refreshed;
351
+ const key = cacheKey(m.clientId, m.authorizeUrl, scopes, opts.authorizeParams);
352
+ await writeCache(cacheDir, key, cached);
353
+ // Retry the original request with new token
354
+ const h = cleanMarkers(request);
355
+ h.set("Authorization", `Bearer ${refreshed.accessToken}`);
356
+ const rebuilt = await rebuildRequest(request, h);
357
+ const bodyBuffer = request.body
358
+ ? await request.clone().arrayBuffer()
359
+ : null;
360
+ return fetch(rebuilt.url, {
361
+ method: rebuilt.method,
362
+ headers: h,
363
+ body: bodyBuffer,
364
+ redirect: rebuilt.redirect,
365
+ signal: rebuilt.signal,
366
+ ...(bodyBuffer ? { duplex: "half" } : {}),
367
+ });
368
+ },
369
+ ],
370
+ },
371
+ };
372
+ }
373
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@glubean/oauth-code",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "dependencies": {
19
+ "@glubean/sdk": "0.1.25"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "vitest": "^3.2.1",
24
+ "typescript": "^5.8.3"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/glubean/glubean.git",
29
+ "directory": "packages/oauth-code"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -p tsconfig.build.json",
33
+ "test": "vitest run"
34
+ }
35
+ }