130 lines
6.1 KiB
JavaScript
130 lines
6.1 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.userOAuthProvider = userOAuthProvider;
|
|
const credentials_1 = require("../../core/credentials.js");
|
|
const types_1 = require("./types.js");
|
|
const time_1 = require("../../internal/utils/time.js");
|
|
const version_1 = require("../../version.js");
|
|
/**
|
|
* Reads a user-oauth credential file. Returns the cached access token while
|
|
* fresh; on expiry performs a `refresh_token` grant and writes the new
|
|
* tokens back to the credentials file (atomic replace, fsync'd).
|
|
*
|
|
* If `clientId` is empty, the access token is treated as static — the
|
|
* credentials file is read on every call but no refresh is attempted, and
|
|
* an expired token without a `refresh_token` raises.
|
|
*/
|
|
function userOAuthProvider(config) {
|
|
return async (opts) => {
|
|
const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
|
|
await (0, types_1.checkCredentialsFileSafety)(config.credentialsPath, config.onSafetyWarning);
|
|
let raw;
|
|
try {
|
|
raw = await fs.promises.readFile(config.credentialsPath, 'utf-8');
|
|
}
|
|
catch (err) {
|
|
throw new types_1.WorkloadIdentityError(`Credentials file not found at ${config.credentialsPath}: ${err}`);
|
|
}
|
|
let creds;
|
|
try {
|
|
creds = JSON.parse(raw);
|
|
}
|
|
catch (err) {
|
|
throw new types_1.WorkloadIdentityError(`Credentials file at ${config.credentialsPath} is not valid JSON: ${err}`);
|
|
}
|
|
const accessToken = creds.access_token;
|
|
if (!accessToken) {
|
|
throw new types_1.WorkloadIdentityError(`Credentials file at ${config.credentialsPath} must include 'access_token'`);
|
|
}
|
|
// Return cached token if still fresh (or no expiry info), unless the
|
|
// caller is forcing a refresh after a 401 — then go straight to refresh
|
|
// even if the file's expires_at still looks valid.
|
|
const expiresAt = creds.expires_at;
|
|
if (!opts?.forceRefresh &&
|
|
(expiresAt == null || (0, time_1.nowAsSeconds)() < expiresAt - types_1.MANDATORY_REFRESH_THRESHOLD_IN_SECONDS)) {
|
|
return { token: accessToken, expiresAt: expiresAt ?? null };
|
|
}
|
|
const refreshToken = creds.refresh_token;
|
|
if (!config.clientId || !refreshToken) {
|
|
throw new types_1.WorkloadIdentityError(`Access token at ${config.credentialsPath} has expired and no refresh is available ` +
|
|
`(client_id ${config.clientId ? 'set' : 'empty'}, refresh_token ${refreshToken ? 'set' : 'empty'})`);
|
|
}
|
|
(0, types_1.requireSecureTokenEndpoint)(config.baseURL);
|
|
const body = {
|
|
grant_type: types_1.GRANT_TYPE_REFRESH_TOKEN,
|
|
refresh_token: refreshToken,
|
|
client_id: config.clientId,
|
|
};
|
|
const url = `${config.baseURL}${types_1.TOKEN_ENDPOINT}`;
|
|
let resp;
|
|
try {
|
|
resp = await config.fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'anthropic-beta': types_1.OAUTH_API_BETA_HEADER,
|
|
'User-Agent': config.userAgent || `anthropic-sdk-typescript/${version_1.VERSION} userOAuthProvider`,
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
catch (err) {
|
|
throw new types_1.WorkloadIdentityError(`User OAuth refresh failed to reach token endpoint: ${err}`);
|
|
}
|
|
const requestId = resp.headers.get('Request-Id');
|
|
if (!resp.ok) {
|
|
const text = await resp.text().catch(() => '');
|
|
throw new types_1.WorkloadIdentityError(`User OAuth refresh failed (HTTP ${resp.status}): ${(0, types_1.redactSensitive)(text)}`, resp.status, (0, types_1.redactSensitive)(text), requestId);
|
|
}
|
|
const data = await (0, types_1.parseTokenResponse)(resp, requestId);
|
|
const expiresIn = Number(data.expires_in);
|
|
if (!Number.isFinite(expiresIn)) {
|
|
throw new types_1.WorkloadIdentityError(`User OAuth refresh response missing or invalid expires_in: ${JSON.stringify((0, types_1.redactSensitive)(data))}`, resp.status, (0, types_1.redactSensitive)(data), requestId);
|
|
}
|
|
const newExpiresAt = (0, time_1.nowAsSeconds)() + expiresIn;
|
|
const newRefreshToken = data.refresh_token || refreshToken;
|
|
await (0, types_1.writeCredentialsFileAtomic)(config.credentialsPath, {
|
|
...creds,
|
|
version: credentials_1.CREDENTIALS_FILE_VERSION,
|
|
type: 'oauth_token',
|
|
access_token: data.access_token,
|
|
expires_at: newExpiresAt,
|
|
refresh_token: newRefreshToken,
|
|
});
|
|
return { token: data.access_token, expiresAt: newExpiresAt };
|
|
};
|
|
}
|
|
//# sourceMappingURL=user-oauth.js.map
|