@numa-tech/cli-auth 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @numa-tech/cli-auth
2
+
3
+ Shared Node.js OIDC authentication for Numa command-line applications.
4
+
5
+ The package provides Authorization Code + PKCE browser login, private token
6
+ caches, rotation-safe refresh, issuer/context validation, and authenticated
7
+ fetch helpers. Refreshes are serialized by token-cache path so independently
8
+ running Numa and Codex processes do not reuse a single-use refresh token.
9
+
10
+ OAuth `invalid_grant` from a refresh request is exposed as `login_required`.
11
+ Transport errors and identity-provider 5xx responses retain the last cache and
12
+ remain retryable HTTP failures. The package never performs tenant business
13
+ authorization; resource servers remain the authorization boundary.
14
+
15
+ Applications supply resolved OIDC and cache settings:
16
+
17
+ ```ts
18
+ import { ensureAccessToken, type AuthConfig } from "@numa-tech/cli-auth";
19
+
20
+ const config: AuthConfig = {
21
+ discoveryUrl: "https://identity.example/realms/team/.well-known/openid-configuration",
22
+ issuerUrl: "https://identity.example/realms/team",
23
+ clientId: "public-cli",
24
+ scope: "openid profile offline_access",
25
+ tokenFile: "/private/config/keycloak-token.json",
26
+ redirectHost: "127.0.0.1",
27
+ redirectPort: 8765,
28
+ redirectPath: "/callback",
29
+ openBrowser: true
30
+ };
31
+
32
+ const accessToken = await ensureAccessToken(config);
33
+ ```
34
+
35
+ Do not print or log the returned token or the token cache.
@@ -0,0 +1,16 @@
1
+ export type AuthErrorCode = "auth_error" | "http_error" | "invalid_discovery" | "issuer_mismatch" | "login_required" | (string & {});
2
+ export interface AuthErrorOptions extends ErrorOptions {
3
+ status?: number;
4
+ oauthError?: string;
5
+ oauthDescription?: string;
6
+ }
7
+ export declare class AuthError extends Error {
8
+ readonly code: AuthErrorCode;
9
+ readonly status?: number;
10
+ readonly oauthError?: string;
11
+ readonly oauthDescription?: string;
12
+ constructor(message: string, code?: AuthErrorCode, options?: AuthErrorOptions);
13
+ }
14
+ export declare class LoginRequiredError extends AuthError {
15
+ constructor(message?: string, options?: ErrorOptions);
16
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,21 @@
1
+ export class AuthError extends Error {
2
+ code;
3
+ status;
4
+ oauthError;
5
+ oauthDescription;
6
+ constructor(message, code = "auth_error", options = {}) {
7
+ super(message, options);
8
+ this.code = code;
9
+ this.name = "AuthError";
10
+ this.status = options.status;
11
+ this.oauthError = options.oauthError;
12
+ this.oauthDescription = options.oauthDescription;
13
+ }
14
+ }
15
+ export class LoginRequiredError extends AuthError {
16
+ constructor(message = "No usable OIDC login is available.", options = {}) {
17
+ super(message, "login_required", options);
18
+ this.name = "LoginRequiredError";
19
+ }
20
+ }
21
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAOhB,IAAI;IANb,MAAM,CAAU;IAChB,UAAU,CAAU;IACpB,gBAAgB,CAAU;IAEnC,YACE,OAAe,EACC,IAAI,GAAkB,YAAY,EAClD,OAAO,GAAqB,EAAE;QAE9B,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBAHR,IAAI;QAIpB,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACnD,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,SAAS;IAC/C,YAAY,OAAO,GAAG,oCAAoC,EAAE,OAAO,GAAiB,EAAE;QACpF,KAAK,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export * from "./errors.js";
2
+ export * from "./types.js";
3
+ export * from "./oidc.js";
4
+ export { withTokenFileLock, type TokenFileLockOptions } from "./token-lock.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./errors.js";
2
+ export * from "./types.js";
3
+ export * from "./oidc.js";
4
+ export { withTokenFileLock } from "./token-lock.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAA6B,MAAM,iBAAiB,CAAC"}
package/dist/oidc.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { AuthConfig, AuthDependencies, AuthenticatedFetchDependencies, BrowserLoginOptions, LoginSession, OidcDiscovery, TokenCache } from "./types.js";
2
+ export declare function nowSeconds(): number;
3
+ export declare function loadDiscovery(config: AuthConfig, dependencies?: AuthDependencies): Promise<OidcDiscovery>;
4
+ export declare function readTokenCache(config: AuthConfig): Promise<TokenCache | undefined>;
5
+ export declare function deleteTokenCache(config: AuthConfig): Promise<void>;
6
+ export declare function refreshAccessToken(config: AuthConfig, cache: TokenCache, dependencies?: AuthDependencies): Promise<TokenCache>;
7
+ export declare function ensureAccessToken(config: AuthConfig, forceRefresh?: boolean, dependencies?: AuthDependencies): Promise<string>;
8
+ export declare function getUserinfo(config: AuthConfig, discovery: OidcDiscovery, accessToken: string, dependencies?: AuthDependencies): Promise<Record<string, unknown>>;
9
+ export declare function fetchWithKeycloakIdentity(config: AuthConfig, url: string, requestFactory: () => RequestInit, dependencies?: AuthenticatedFetchDependencies): Promise<Response>;
10
+ export declare function whoami(config: AuthConfig, forceRefresh?: boolean, dependencies?: AuthDependencies): Promise<Record<string, unknown>>;
11
+ export declare function startBrowserLogin(config: AuthConfig, options?: BrowserLoginOptions, dependencies?: AuthDependencies): Promise<LoginSession>;
12
+ export declare function completeBrowserLogin(config: AuthConfig, session: LoginSession, options?: BrowserLoginOptions, dependencies?: AuthDependencies): Promise<Record<string, unknown>>;
13
+ export declare function blockingBrowserLogin(config: AuthConfig, options?: BrowserLoginOptions, dependencies?: AuthDependencies): Promise<Record<string, unknown>>;
package/dist/oidc.js ADDED
@@ -0,0 +1,448 @@
1
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
2
+ import { rm } from "node:fs/promises";
3
+ import { createServer } from "node:http";
4
+ import open from "open";
5
+ import { AuthError, LoginRequiredError } from "./errors.js";
6
+ import { readJsonIfExists, writePrivateJson } from "./private-file.js";
7
+ import { withTokenFileLock } from "./token-lock.js";
8
+ const REFRESH_LEEWAY_SECONDS = 60;
9
+ const LOGIN_SESSION_TTL_SECONDS = 300;
10
+ export function nowSeconds() {
11
+ return Math.floor(Date.now() / 1000);
12
+ }
13
+ function dependencyNow(dependencies) {
14
+ return (dependencies.now ?? nowSeconds)();
15
+ }
16
+ async function parseJsonResponse(response, url) {
17
+ const text = await response.text();
18
+ let parsed = {};
19
+ if (text) {
20
+ try {
21
+ parsed = JSON.parse(text);
22
+ }
23
+ catch {
24
+ parsed = { raw: text };
25
+ }
26
+ }
27
+ if (!response.ok) {
28
+ const problem = parsed && typeof parsed === "object"
29
+ ? parsed
30
+ : {};
31
+ const oauthError = typeof problem.error === "string" ? problem.error : undefined;
32
+ const oauthDescription = typeof problem.error_description === "string"
33
+ ? problem.error_description
34
+ : undefined;
35
+ const detail = oauthDescription ?? oauthError
36
+ ?? (typeof problem.raw === "string" ? problem.raw : undefined)
37
+ ?? response.statusText;
38
+ throw new AuthError(`HTTP ${response.status} from ${url}: ${detail}`, "http_error", {
39
+ status: response.status,
40
+ oauthError,
41
+ oauthDescription
42
+ });
43
+ }
44
+ return parsed;
45
+ }
46
+ async function requestJson(dependencies, url, init) {
47
+ const fetchImpl = dependencies.fetch ?? fetch;
48
+ let response;
49
+ try {
50
+ response = await fetchImpl(url, {
51
+ ...init,
52
+ headers: {
53
+ Accept: "application/json",
54
+ ...(init?.body ? { "Content-Type": "application/x-www-form-urlencoded" } : {}),
55
+ ...(init?.headers ?? {})
56
+ }
57
+ });
58
+ }
59
+ catch (error) {
60
+ throw new AuthError(`Unable to reach OIDC endpoint ${url}.`, "http_error", { cause: error });
61
+ }
62
+ return parseJsonResponse(response, url);
63
+ }
64
+ function formBody(values) {
65
+ const body = new URLSearchParams();
66
+ for (const [key, value] of Object.entries(values)) {
67
+ if (value != null && value !== "")
68
+ body.set(key, value);
69
+ }
70
+ return body;
71
+ }
72
+ function addClientAuth(config, values) {
73
+ return formBody({
74
+ ...values,
75
+ client_id: config.clientId,
76
+ client_secret: config.clientSecret
77
+ });
78
+ }
79
+ function normalizeIssuer(value) {
80
+ return value.replace(/\/+$/u, "");
81
+ }
82
+ function validateOidcUrl(value, name) {
83
+ let url;
84
+ try {
85
+ url = new URL(value);
86
+ }
87
+ catch {
88
+ throw new AuthError(`OIDC discovery ${name} is not a valid URL.`, "invalid_discovery");
89
+ }
90
+ const loopback = ["localhost", "127.0.0.1", "::1", "[::1]"].includes(url.hostname);
91
+ if (url.username || url.password || (url.protocol !== "https:" && !(url.protocol === "http:" && loopback))) {
92
+ throw new AuthError(`OIDC discovery ${name} must use HTTPS without embedded credentials (loopback HTTP is allowed).`, "invalid_discovery");
93
+ }
94
+ }
95
+ export async function loadDiscovery(config, dependencies = {}) {
96
+ validateOidcUrl(config.discoveryUrl, "discovery URL");
97
+ const discovery = await requestJson(dependencies, config.discoveryUrl);
98
+ if (config.issuerUrl && normalizeIssuer(discovery.issuer) !== normalizeIssuer(config.issuerUrl)) {
99
+ throw new AuthError(`OIDC discovery issuer mismatch: expected ${config.issuerUrl}, received ${discovery.issuer}.`, "issuer_mismatch");
100
+ }
101
+ for (const [name, value] of [
102
+ ["issuer", discovery.issuer],
103
+ ["authorization_endpoint", discovery.authorization_endpoint],
104
+ ["token_endpoint", discovery.token_endpoint]
105
+ ]) {
106
+ validateOidcUrl(value, name);
107
+ }
108
+ if (discovery.userinfo_endpoint) {
109
+ validateOidcUrl(discovery.userinfo_endpoint, "userinfo_endpoint");
110
+ }
111
+ return discovery;
112
+ }
113
+ function issuerFromDiscoveryUrl(discoveryUrl) {
114
+ return discoveryUrl.replace(/\/\.well-known\/openid-configuration\/?$/u, "");
115
+ }
116
+ function normalizeCache(value, config) {
117
+ if (!value || typeof value !== "object")
118
+ return undefined;
119
+ const raw = value;
120
+ const token = raw.token;
121
+ if (!token || typeof token !== "object")
122
+ return undefined;
123
+ const tokenValue = token;
124
+ if (typeof raw.discoveryUrl !== "string"
125
+ || typeof raw.clientId !== "string"
126
+ || typeof raw.obtainedAt !== "number"
127
+ || typeof raw.expiresAt !== "number"
128
+ || typeof tokenValue.access_token !== "string")
129
+ return undefined;
130
+ const profileTenantId = typeof raw.profileTenantId === "string"
131
+ ? raw.profileTenantId
132
+ : typeof raw.tenantId === "string" ? raw.tenantId : undefined;
133
+ const resourceAudience = typeof raw.resourceAudience === "string"
134
+ ? raw.resourceAudience
135
+ : typeof raw.audience === "string" ? raw.audience : undefined;
136
+ const issuer = typeof raw.issuer === "string"
137
+ ? raw.issuer
138
+ : config.issuerUrl ?? issuerFromDiscoveryUrl(raw.discoveryUrl);
139
+ const scope = typeof raw.scope === "string"
140
+ ? raw.scope
141
+ : typeof tokenValue.scope === "string" ? tokenValue.scope : config.scope;
142
+ return {
143
+ schemaVersion: 1,
144
+ issuer,
145
+ discoveryUrl: raw.discoveryUrl,
146
+ clientId: raw.clientId,
147
+ ...(profileTenantId ? { profileTenantId } : {}),
148
+ ...(resourceAudience ? { resourceAudience } : {}),
149
+ scope,
150
+ obtainedAt: raw.obtainedAt,
151
+ expiresAt: raw.expiresAt,
152
+ ...(typeof raw.refreshExpiresAt === "number" ? { refreshExpiresAt: raw.refreshExpiresAt } : {}),
153
+ token: tokenValue,
154
+ ...(raw.userinfo && typeof raw.userinfo === "object"
155
+ ? { userinfo: raw.userinfo }
156
+ : {})
157
+ };
158
+ }
159
+ export async function readTokenCache(config) {
160
+ return normalizeCache(await readJsonIfExists(config.tokenFile), config);
161
+ }
162
+ async function removeTokenCache(config) {
163
+ await rm(config.tokenFile, { force: true });
164
+ }
165
+ export async function deleteTokenCache(config) {
166
+ await withTokenFileLock(config.tokenFile, () => removeTokenCache(config));
167
+ }
168
+ function cacheCompatible(cache, config) {
169
+ return cache.discoveryUrl === config.discoveryUrl
170
+ && cache.clientId === config.clientId
171
+ && (config.issuerUrl == null
172
+ || normalizeIssuer(cache.issuer) === normalizeIssuer(config.issuerUrl))
173
+ && (config.profileTenantId == null || cache.profileTenantId === config.profileTenantId)
174
+ && (config.resourceAudience == null || cache.resourceAudience === config.resourceAudience);
175
+ }
176
+ function requireCompatibleCache(cache, config) {
177
+ if (!cache)
178
+ throw new LoginRequiredError("No token cache exists. Start login first.");
179
+ if (!cacheCompatible(cache, config)) {
180
+ throw new LoginRequiredError("Token cache was created for a different issuer, client, profile tenant, or resource audience.");
181
+ }
182
+ return cache;
183
+ }
184
+ function cacheAdvanced(previous, current) {
185
+ return previous.obtainedAt !== current.obtainedAt
186
+ || previous.token.access_token !== current.token.access_token
187
+ || previous.token.refresh_token !== current.token.refresh_token;
188
+ }
189
+ function accessTokenFresh(cache, now, leewaySeconds = REFRESH_LEEWAY_SECONDS) {
190
+ return cache.expiresAt > now + leewaySeconds;
191
+ }
192
+ async function saveToken(config, discovery, token, previous, dependencies) {
193
+ const obtainedAt = dependencyNow(dependencies);
194
+ const refreshExpiresAt = token.refresh_expires_in && token.refresh_expires_in > 0
195
+ ? obtainedAt + token.refresh_expires_in
196
+ : previous?.refreshExpiresAt;
197
+ const cache = {
198
+ schemaVersion: 1,
199
+ issuer: discovery.issuer,
200
+ discoveryUrl: config.discoveryUrl,
201
+ clientId: config.clientId,
202
+ ...(config.profileTenantId ? { profileTenantId: config.profileTenantId } : {}),
203
+ ...(config.resourceAudience ? { resourceAudience: config.resourceAudience } : {}),
204
+ scope: token.scope ?? config.scope,
205
+ obtainedAt,
206
+ expiresAt: obtainedAt + (token.expires_in ?? 0),
207
+ ...(refreshExpiresAt ? { refreshExpiresAt } : {}),
208
+ token: {
209
+ ...token,
210
+ refresh_token: token.refresh_token ?? previous?.token.refresh_token
211
+ },
212
+ ...(previous?.userinfo ? { userinfo: previous.userinfo } : {})
213
+ };
214
+ await writePrivateJson(config.tokenFile, cache);
215
+ return cache;
216
+ }
217
+ function refreshRejected(error) {
218
+ return error instanceof AuthError && error.oauthError === "invalid_grant";
219
+ }
220
+ async function refreshAccessTokenLocked(config, cache, dependencies, allowAdvancedRetry) {
221
+ const refreshToken = cache.token.refresh_token;
222
+ if (!refreshToken) {
223
+ await removeTokenCache(config);
224
+ throw new LoginRequiredError("Saved token has no refresh token. Start login again.");
225
+ }
226
+ const discovery = await loadDiscovery(config, dependencies);
227
+ let refreshed;
228
+ try {
229
+ refreshed = await requestJson(dependencies, discovery.token_endpoint, {
230
+ method: "POST",
231
+ body: addClientAuth(config, {
232
+ grant_type: "refresh_token",
233
+ refresh_token: refreshToken
234
+ })
235
+ });
236
+ }
237
+ catch (error) {
238
+ if (!refreshRejected(error))
239
+ throw error;
240
+ const latest = await readTokenCache(config);
241
+ if (latest && cacheCompatible(latest, config) && cacheAdvanced(cache, latest)) {
242
+ if (accessTokenFresh(latest, dependencyNow(dependencies)))
243
+ return latest;
244
+ if (allowAdvancedRetry) {
245
+ return refreshAccessTokenLocked(config, latest, dependencies, false);
246
+ }
247
+ }
248
+ await removeTokenCache(config);
249
+ throw new LoginRequiredError("登录会话已失效,请重新登录。", { cause: error });
250
+ }
251
+ return saveToken(config, discovery, refreshed, cache, dependencies);
252
+ }
253
+ export async function refreshAccessToken(config, cache, dependencies = {}) {
254
+ return withTokenFileLock(config.tokenFile, async () => {
255
+ const latest = requireCompatibleCache(await readTokenCache(config), config);
256
+ if (cacheAdvanced(cache, latest) && accessTokenFresh(latest, dependencyNow(dependencies))) {
257
+ return latest;
258
+ }
259
+ return refreshAccessTokenLocked(config, latest, dependencies, true);
260
+ });
261
+ }
262
+ export async function ensureAccessToken(config, forceRefresh = false, dependencies = {}) {
263
+ const initial = requireCompatibleCache(await readTokenCache(config), config);
264
+ if (!forceRefresh && accessTokenFresh(initial, dependencyNow(dependencies))) {
265
+ return initial.token.access_token;
266
+ }
267
+ return withTokenFileLock(config.tokenFile, async () => {
268
+ const current = requireCompatibleCache(await readTokenCache(config), config);
269
+ if (accessTokenFresh(current, dependencyNow(dependencies))
270
+ && (!forceRefresh || cacheAdvanced(initial, current)))
271
+ return current.token.access_token;
272
+ return (await refreshAccessTokenLocked(config, current, dependencies, true)).token.access_token;
273
+ });
274
+ }
275
+ export async function getUserinfo(config, discovery, accessToken, dependencies = {}) {
276
+ if (!discovery.userinfo_endpoint) {
277
+ throw new AuthError("OIDC discovery does not provide a userinfo endpoint.", "invalid_discovery");
278
+ }
279
+ return requestJson(dependencies, discovery.userinfo_endpoint, {
280
+ headers: { Authorization: `Bearer ${accessToken}` }
281
+ });
282
+ }
283
+ async function updateUserinfoIfCurrent(config, accessToken, userinfo) {
284
+ await withTokenFileLock(config.tokenFile, async () => {
285
+ const current = await readTokenCache(config);
286
+ if (current?.token.access_token !== accessToken)
287
+ return;
288
+ await writePrivateJson(config.tokenFile, { ...current, userinfo });
289
+ });
290
+ }
291
+ export async function fetchWithKeycloakIdentity(config, url, requestFactory, dependencies = {}) {
292
+ const fetchImpl = dependencies.fetch ?? fetch;
293
+ const tokenProvider = dependencies.tokenProvider
294
+ ?? ((providerConfig, forceRefresh) => ensureAccessToken(providerConfig, forceRefresh, dependencies));
295
+ const send = async (forceRefresh) => {
296
+ const token = await tokenProvider(config, forceRefresh);
297
+ const init = requestFactory();
298
+ const headers = new Headers(init.headers);
299
+ headers.set("Authorization", `Bearer ${token}`);
300
+ return fetchImpl(url, { ...init, headers });
301
+ };
302
+ let response = await send(false);
303
+ if (response.status === 401) {
304
+ await response.body?.cancel().catch(() => undefined);
305
+ response = await send(true);
306
+ }
307
+ return response;
308
+ }
309
+ export async function whoami(config, forceRefresh = false, dependencies = {}) {
310
+ const discovery = await loadDiscovery(config, dependencies);
311
+ if (forceRefresh)
312
+ await ensureAccessToken(config, true, dependencies);
313
+ const response = await fetchWithKeycloakIdentity(config, discovery.userinfo_endpoint ?? "", () => ({ headers: { Accept: "application/json" } }), dependencies);
314
+ const info = await parseJsonResponse(response, discovery.userinfo_endpoint ?? "");
315
+ const cache = await readTokenCache(config);
316
+ if (cache)
317
+ await updateUserinfoIfCurrent(config, cache.token.access_token, info);
318
+ return info;
319
+ }
320
+ function pkcePair() {
321
+ const verifier = randomBytes(32).toString("base64url");
322
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
323
+ return { verifier, challenge };
324
+ }
325
+ export async function startBrowserLogin(config, options = {}, dependencies = {}) {
326
+ const discovery = await loadDiscovery(config, dependencies);
327
+ const { verifier, challenge } = pkcePair();
328
+ const state = randomBytes(24).toString("base64url");
329
+ const callback = `http://${config.redirectHost}:${config.redirectPort}${config.redirectPath}`;
330
+ const id = randomUUID();
331
+ let server;
332
+ const createdAt = dependencyNow(dependencies);
333
+ const session = {
334
+ id,
335
+ authorizationUrl: "",
336
+ redirectUri: callback,
337
+ codeVerifier: verifier,
338
+ state,
339
+ createdAt,
340
+ expiresAt: createdAt + LOGIN_SESSION_TTL_SECONDS,
341
+ close: async () => {
342
+ if (!server)
343
+ return;
344
+ await new Promise((resolve, reject) => {
345
+ server?.close((error) => error ? reject(error) : resolve());
346
+ });
347
+ server = undefined;
348
+ }
349
+ };
350
+ const authUrl = new URL(discovery.authorization_endpoint);
351
+ authUrl.searchParams.set("client_id", config.clientId);
352
+ authUrl.searchParams.set("redirect_uri", callback);
353
+ authUrl.searchParams.set("response_type", "code");
354
+ authUrl.searchParams.set("scope", config.scope);
355
+ authUrl.searchParams.set("state", state);
356
+ authUrl.searchParams.set("code_challenge", challenge);
357
+ authUrl.searchParams.set("code_challenge_method", "S256");
358
+ session.authorizationUrl = authUrl.toString();
359
+ server = createServer((request, response) => {
360
+ const requestUrl = new URL(request.url ?? "/", callback);
361
+ if (requestUrl.pathname !== config.redirectPath) {
362
+ response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
363
+ response.end("Not found\n");
364
+ return;
365
+ }
366
+ const returnedState = requestUrl.searchParams.get("state");
367
+ if (returnedState !== state) {
368
+ session.error = "State mismatch";
369
+ }
370
+ else if (requestUrl.searchParams.get("error")) {
371
+ session.error = requestUrl.searchParams.get("error_description")
372
+ ?? requestUrl.searchParams.get("error")
373
+ ?? "Login failed";
374
+ }
375
+ else {
376
+ session.code = requestUrl.searchParams.get("code") ?? undefined;
377
+ }
378
+ const redirect = session.code ? options.successRedirectUrl : options.failureRedirectUrl;
379
+ if (redirect) {
380
+ response.writeHead(303, {
381
+ Location: redirect,
382
+ "Cache-Control": "no-store",
383
+ "Referrer-Policy": "no-referrer"
384
+ });
385
+ response.end();
386
+ return;
387
+ }
388
+ response.writeHead(session.code ? 200 : 400, {
389
+ "Content-Type": "text/plain; charset=utf-8",
390
+ "Cache-Control": "no-store"
391
+ });
392
+ response.end(session.code
393
+ ? "Login complete. You may close this tab.\n"
394
+ : `Login failed: ${session.error}\n`);
395
+ });
396
+ await new Promise((resolve, reject) => {
397
+ server?.once("error", reject);
398
+ server?.listen(config.redirectPort, config.redirectHost, resolve);
399
+ });
400
+ if (config.openBrowser) {
401
+ await (dependencies.openBrowser ?? open)(session.authorizationUrl).catch(() => undefined);
402
+ }
403
+ return session;
404
+ }
405
+ export async function completeBrowserLogin(config, session, options = {}, dependencies = {}) {
406
+ if (session.error)
407
+ throw new AuthError(session.error, "login_failed");
408
+ if (!session.code) {
409
+ if (session.expiresAt <= dependencyNow(dependencies)) {
410
+ await session.close();
411
+ throw new AuthError("Login session expired before callback was received.", "login_expired");
412
+ }
413
+ throw new AuthError("Login callback has not been received yet.", "login_pending");
414
+ }
415
+ const discovery = await loadDiscovery(config, dependencies);
416
+ const token = await requestJson(dependencies, discovery.token_endpoint, {
417
+ method: "POST",
418
+ body: addClientAuth(config, {
419
+ grant_type: "authorization_code",
420
+ code: session.code,
421
+ redirect_uri: session.redirectUri,
422
+ code_verifier: session.codeVerifier
423
+ })
424
+ });
425
+ const saved = await withTokenFileLock(config.tokenFile, () => saveToken(config, discovery, token, undefined, dependencies));
426
+ if (options.loadUserinfo === false) {
427
+ await session.close();
428
+ return {};
429
+ }
430
+ const info = await getUserinfo(config, discovery, saved.token.access_token, dependencies);
431
+ await updateUserinfoIfCurrent(config, saved.token.access_token, info);
432
+ await session.close();
433
+ return info;
434
+ }
435
+ export async function blockingBrowserLogin(config, options = {}, dependencies = {}) {
436
+ const session = await startBrowserLogin(config, options, dependencies);
437
+ process.stderr.write(`Open this URL to log in:\n${session.authorizationUrl}\n`);
438
+ try {
439
+ while (!session.code && !session.error && session.expiresAt > dependencyNow(dependencies)) {
440
+ await new Promise((resolve) => setTimeout(resolve, 500));
441
+ }
442
+ return await completeBrowserLogin(config, session, options, dependencies);
443
+ }
444
+ finally {
445
+ await session.close().catch(() => undefined);
446
+ }
447
+ }
448
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function readJsonIfExists(path: string): Promise<unknown | undefined>;
2
+ export declare function writePrivateJson(path: string, value: unknown): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ export async function readJsonIfExists(path) {
5
+ try {
6
+ return JSON.parse(await readFile(path, "utf8"));
7
+ }
8
+ catch (error) {
9
+ if (error.code === "ENOENT")
10
+ return undefined;
11
+ throw error;
12
+ }
13
+ }
14
+ export async function writePrivateJson(path, value) {
15
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
16
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
17
+ try {
18
+ await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, {
19
+ flag: "wx",
20
+ mode: 0o600
21
+ });
22
+ await rename(temporary, path);
23
+ }
24
+ finally {
25
+ await rm(temporary, { force: true }).catch(() => undefined);
26
+ }
27
+ }
28
+ //# sourceMappingURL=private-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"private-file.js","sourceRoot":"","sources":["../src/private-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACzE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,KAAc;IACjE,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CAAC;IAC/D,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;YAChE,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface TokenFileLockOptions {
2
+ now?: () => number;
3
+ sleep?: (milliseconds: number) => Promise<void>;
4
+ pollMilliseconds?: number;
5
+ timeoutMilliseconds?: number;
6
+ staleMilliseconds?: number;
7
+ }
8
+ export declare function withTokenFileLock<T>(tokenFile: string, task: () => Promise<T>, options?: TokenFileLockOptions): Promise<T>;
@@ -0,0 +1,105 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { AuthError } from "./errors.js";
5
+ const DEFAULT_POLL_MILLISECONDS = 25;
6
+ const DEFAULT_TIMEOUT_MILLISECONDS = 30_000;
7
+ const DEFAULT_STALE_MILLISECONDS = 120_000;
8
+ function defaultSleep(milliseconds) {
9
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
10
+ }
11
+ function processIsAlive(pid) {
12
+ if (!Number.isInteger(pid) || pid <= 0)
13
+ return false;
14
+ try {
15
+ process.kill(pid, 0);
16
+ return true;
17
+ }
18
+ catch (error) {
19
+ return error.code !== "ESRCH";
20
+ }
21
+ }
22
+ async function readOwner(lockDirectory) {
23
+ try {
24
+ const value = JSON.parse(await readFile(`${lockDirectory}/owner.json`, "utf8"));
25
+ return typeof value.id === "string"
26
+ && Number.isInteger(value.pid)
27
+ && typeof value.createdAt === "number"
28
+ ? value
29
+ : undefined;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ async function reclaim(lockDirectory) {
36
+ const staleDirectory = `${lockDirectory}.stale.${process.pid}.${randomUUID()}`;
37
+ try {
38
+ await rename(lockDirectory, staleDirectory);
39
+ }
40
+ catch (error) {
41
+ if (["ENOENT", "EEXIST", "ENOTEMPTY"].includes(error.code ?? "")) {
42
+ return false;
43
+ }
44
+ throw error;
45
+ }
46
+ await rm(staleDirectory, { recursive: true, force: true });
47
+ return true;
48
+ }
49
+ async function mayReclaim(lockDirectory, now, staleMilliseconds) {
50
+ const owner = await readOwner(lockDirectory);
51
+ if (owner)
52
+ return !processIsAlive(owner.pid);
53
+ try {
54
+ const lockStat = await stat(lockDirectory);
55
+ return now - lockStat.mtimeMs >= staleMilliseconds;
56
+ }
57
+ catch (error) {
58
+ if (error.code === "ENOENT")
59
+ return false;
60
+ throw error;
61
+ }
62
+ }
63
+ export async function withTokenFileLock(tokenFile, task, options = {}) {
64
+ const now = options.now ?? Date.now;
65
+ const sleep = options.sleep ?? defaultSleep;
66
+ const pollMilliseconds = options.pollMilliseconds ?? DEFAULT_POLL_MILLISECONDS;
67
+ const timeoutMilliseconds = options.timeoutMilliseconds ?? DEFAULT_TIMEOUT_MILLISECONDS;
68
+ const staleMilliseconds = options.staleMilliseconds ?? DEFAULT_STALE_MILLISECONDS;
69
+ const lockDirectory = `${tokenFile}.lock`;
70
+ const ownerId = randomUUID();
71
+ const deadline = now() + timeoutMilliseconds;
72
+ await mkdir(dirname(tokenFile), { recursive: true, mode: 0o700 });
73
+ while (true) {
74
+ try {
75
+ await mkdir(lockDirectory, { mode: 0o700 });
76
+ await writeFile(`${lockDirectory}/owner.json`, `${JSON.stringify({ id: ownerId, pid: process.pid, createdAt: now() })}\n`, { flag: "wx", mode: 0o600 });
77
+ break;
78
+ }
79
+ catch (error) {
80
+ const code = error.code;
81
+ if (code !== "EEXIST") {
82
+ await rm(lockDirectory, { recursive: true, force: true }).catch(() => undefined);
83
+ throw error;
84
+ }
85
+ if (await mayReclaim(lockDirectory, now(), staleMilliseconds)) {
86
+ await reclaim(lockDirectory);
87
+ continue;
88
+ }
89
+ if (now() >= deadline) {
90
+ throw new AuthError("Timed out waiting for the OIDC token cache lock.");
91
+ }
92
+ await sleep(pollMilliseconds);
93
+ }
94
+ }
95
+ try {
96
+ return await task();
97
+ }
98
+ finally {
99
+ const owner = await readOwner(lockDirectory);
100
+ if (owner?.id === ownerId) {
101
+ await rm(lockDirectory, { recursive: true, force: true });
102
+ }
103
+ }
104
+ }
105
+ //# sourceMappingURL=token-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-lock.js","sourceRoot":"","sources":["../src/token-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ export interface OidcDiscovery {
2
+ issuer: string;
3
+ authorization_endpoint: string;
4
+ token_endpoint: string;
5
+ userinfo_endpoint?: string;
6
+ device_authorization_endpoint?: string;
7
+ end_session_endpoint?: string;
8
+ }
9
+ export interface TokenResponse {
10
+ access_token: string;
11
+ token_type?: string;
12
+ expires_in?: number;
13
+ refresh_token?: string;
14
+ refresh_expires_in?: number;
15
+ id_token?: string;
16
+ scope?: string;
17
+ [name: string]: unknown;
18
+ }
19
+ export interface TokenCache {
20
+ schemaVersion: 1;
21
+ issuer: string;
22
+ discoveryUrl: string;
23
+ clientId: string;
24
+ profileTenantId?: string;
25
+ resourceAudience?: string;
26
+ scope: string;
27
+ obtainedAt: number;
28
+ expiresAt: number;
29
+ refreshExpiresAt?: number;
30
+ token: TokenResponse;
31
+ userinfo?: Record<string, unknown>;
32
+ }
33
+ export interface AuthConfig {
34
+ discoveryUrl: string;
35
+ issuerUrl?: string;
36
+ clientId: string;
37
+ profileTenantId?: string;
38
+ resourceAudience?: string;
39
+ clientSecret?: string;
40
+ scope: string;
41
+ tokenFile: string;
42
+ redirectHost: string;
43
+ redirectPort: number;
44
+ redirectPath: string;
45
+ openBrowser: boolean;
46
+ }
47
+ export interface LoginSession {
48
+ id: string;
49
+ authorizationUrl: string;
50
+ redirectUri: string;
51
+ codeVerifier: string;
52
+ state: string;
53
+ createdAt: number;
54
+ expiresAt: number;
55
+ code?: string;
56
+ error?: string;
57
+ close: () => Promise<void>;
58
+ }
59
+ export interface BrowserLoginOptions {
60
+ successRedirectUrl?: string;
61
+ failureRedirectUrl?: string;
62
+ loadUserinfo?: boolean;
63
+ }
64
+ export interface AuthDependencies {
65
+ fetch?: typeof fetch;
66
+ now?: () => number;
67
+ openBrowser?: (url: string) => Promise<unknown>;
68
+ }
69
+ export type AccessTokenProvider = (config: AuthConfig, forceRefresh?: boolean) => Promise<string>;
70
+ export interface AuthenticatedFetchDependencies extends AuthDependencies {
71
+ tokenProvider?: AccessTokenProvider;
72
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@numa-tech/cli-auth",
3
+ "version": "1.12.1",
4
+ "description": "Shared rotation-safe OIDC authentication for Numa command-line clients.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
18
+ "build": "npm run clean && tsc -p tsconfig.build.json",
19
+ "typecheck": "tsc --noEmit -p tsconfig.json",
20
+ "test": "node --import tsx --test src/*.test.ts",
21
+ "check": "npm run typecheck && npm test",
22
+ "pack:check": "npm pack --dry-run --json --ignore-scripts",
23
+ "prepack": "npm run build"
24
+ },
25
+ "dependencies": {
26
+ "open": "^10.2.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^25.0.0",
30
+ "tsx": "^4.21.0",
31
+ "typescript": "^7.0.2"
32
+ },
33
+ "engines": {
34
+ "node": ">=20.12.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }