@uipath/auth 1.196.0 → 1.197.0-preview.59
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/authProfile.d.ts +11 -0
- package/dist/clientCredentials.d.ts +4 -0
- package/dist/envAuth.d.ts +1 -1
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +18688 -243
- package/dist/index.d.ts +11 -2
- package/dist/index.js +648 -248
- package/dist/interactive.d.ts +11 -0
- package/dist/loginStatus.d.ts +14 -5
- package/dist/logout.d.ts +4 -0
- package/dist/refreshCircuitBreaker.d.ts +42 -0
- package/dist/selectTenant.d.ts +35 -0
- package/dist/strategies/auth-strategy.d.ts +16 -5
- package/dist/strategies/node-strategy.d.ts +2 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -19153,16 +19153,33 @@ var require_dist = __commonJS((exports) => {
|
|
|
19153
19153
|
});
|
|
19154
19154
|
|
|
19155
19155
|
// src/getBaseHtml.ts
|
|
19156
|
-
var
|
|
19156
|
+
var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
|
|
19157
|
+
switch (char) {
|
|
19158
|
+
case "&":
|
|
19159
|
+
return "&";
|
|
19160
|
+
case "<":
|
|
19161
|
+
return "<";
|
|
19162
|
+
case ">":
|
|
19163
|
+
return ">";
|
|
19164
|
+
case '"':
|
|
19165
|
+
return """;
|
|
19166
|
+
case "'":
|
|
19167
|
+
return "'";
|
|
19168
|
+
default:
|
|
19169
|
+
return char;
|
|
19170
|
+
}
|
|
19171
|
+
}), getBaseHtml = ({ title, message, type }) => {
|
|
19157
19172
|
const icon = type === "success" ? "✓" : "✕";
|
|
19158
19173
|
const iconClass = type === "success" ? "icon-success" : "icon-error";
|
|
19174
|
+
const safeTitle = escapeHtml(title);
|
|
19175
|
+
const safeMessage = escapeHtml(message);
|
|
19159
19176
|
return `
|
|
19160
19177
|
<!DOCTYPE html>
|
|
19161
19178
|
<html lang="en">
|
|
19162
19179
|
<head>
|
|
19163
19180
|
<meta charset="UTF-8">
|
|
19164
19181
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
19165
|
-
<title>${
|
|
19182
|
+
<title>${safeTitle} - UiPath CLI</title>
|
|
19166
19183
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
19167
19184
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
19168
19185
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400&family=Poppins:wght@600&display=swap" rel="stylesheet">
|
|
@@ -19288,8 +19305,8 @@ var getBaseHtml = ({ title, message, type }) => {
|
|
|
19288
19305
|
</div>
|
|
19289
19306
|
</div>
|
|
19290
19307
|
<div class="icon ${iconClass}">${icon}</div>
|
|
19291
|
-
<h1>${
|
|
19292
|
-
<p>${
|
|
19308
|
+
<h1>${safeTitle}</h1>
|
|
19309
|
+
<p>${safeMessage}</p>
|
|
19293
19310
|
<div class="footer">You can close this window</div>
|
|
19294
19311
|
</div>
|
|
19295
19312
|
</div>
|
|
@@ -19492,6 +19509,13 @@ class NodeAuthStrategy {
|
|
|
19492
19509
|
if (c > 31 && (c < 128 || c > 159))
|
|
19493
19510
|
safeUrl += ch;
|
|
19494
19511
|
}
|
|
19512
|
+
if (opts?.noBrowser) {
|
|
19513
|
+
if (!opts.onAuthUrl) {
|
|
19514
|
+
throw new Error("Headless login (noBrowser) requires an onAuthUrl handler " + "to surface the authorize URL, but none was provided.");
|
|
19515
|
+
}
|
|
19516
|
+
opts.onAuthUrl(safeUrl);
|
|
19517
|
+
return;
|
|
19518
|
+
}
|
|
19495
19519
|
const [openError] = await catchError(fs7.utils.open(url));
|
|
19496
19520
|
if (!openError)
|
|
19497
19521
|
return;
|
|
@@ -19573,6 +19597,12 @@ var normalizeAndValidateBaseUrl = (rawUrl) => {
|
|
|
19573
19597
|
}
|
|
19574
19598
|
return url.pathname.length > 1 ? url.origin : baseUrl;
|
|
19575
19599
|
};
|
|
19600
|
+
var resolveScopes = (isExternalAppAuth, customScopes, fileScopes) => {
|
|
19601
|
+
const requestedScopes = customScopes?.length ? customScopes : fileScopes ?? [];
|
|
19602
|
+
if (isExternalAppAuth)
|
|
19603
|
+
return requestedScopes;
|
|
19604
|
+
return [...new Set([...DEFAULT_SCOPES, ...requestedScopes])];
|
|
19605
|
+
};
|
|
19576
19606
|
var resolveConfigAsync = async ({
|
|
19577
19607
|
customAuthority,
|
|
19578
19608
|
customClientId,
|
|
@@ -19603,7 +19633,7 @@ var resolveConfigAsync = async ({
|
|
|
19603
19633
|
clientSecret = fileAuth.clientSecret;
|
|
19604
19634
|
}
|
|
19605
19635
|
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && Boolean(clientSecret);
|
|
19606
|
-
const scopes =
|
|
19636
|
+
const scopes = resolveScopes(isExternalAppAuth, customScopes, fileAuth.scopes);
|
|
19607
19637
|
return {
|
|
19608
19638
|
clientId,
|
|
19609
19639
|
clientSecret,
|
|
@@ -20250,6 +20280,105 @@ var exchangeCodeForTokens = async ({
|
|
|
20250
20280
|
};
|
|
20251
20281
|
// src/loginStatus.ts
|
|
20252
20282
|
init_src();
|
|
20283
|
+
|
|
20284
|
+
// src/authProfile.ts
|
|
20285
|
+
init_src();
|
|
20286
|
+
init_constants();
|
|
20287
|
+
var DEFAULT_AUTH_PROFILE = "default";
|
|
20288
|
+
var PROFILE_DIR = "profiles";
|
|
20289
|
+
var PROFILE_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
20290
|
+
var ACTIVE_AUTH_PROFILE_KEY = Symbol.for("@uipath/auth/ActiveAuthProfile");
|
|
20291
|
+
var AUTH_PROFILE_STORAGE_KEY = Symbol.for("@uipath/auth/ProfileStorage");
|
|
20292
|
+
var globalSlot2 = globalThis;
|
|
20293
|
+
function isAuthProfileStorage(value) {
|
|
20294
|
+
return value !== null && typeof value === "object" && "getStore" in value && "run" in value;
|
|
20295
|
+
}
|
|
20296
|
+
function createProfileStorage() {
|
|
20297
|
+
const [error, mod] = catchError(() => __require("node:async_hooks"));
|
|
20298
|
+
if (error || typeof mod?.AsyncLocalStorage !== "function") {
|
|
20299
|
+
return {
|
|
20300
|
+
getStore: () => {
|
|
20301
|
+
return;
|
|
20302
|
+
},
|
|
20303
|
+
run: (_store, fn) => fn()
|
|
20304
|
+
};
|
|
20305
|
+
}
|
|
20306
|
+
return new mod.AsyncLocalStorage;
|
|
20307
|
+
}
|
|
20308
|
+
function getProfileStorage() {
|
|
20309
|
+
const existing = globalSlot2[AUTH_PROFILE_STORAGE_KEY];
|
|
20310
|
+
if (isAuthProfileStorage(existing)) {
|
|
20311
|
+
return existing;
|
|
20312
|
+
}
|
|
20313
|
+
const storage = createProfileStorage();
|
|
20314
|
+
globalSlot2[AUTH_PROFILE_STORAGE_KEY] = storage;
|
|
20315
|
+
return storage;
|
|
20316
|
+
}
|
|
20317
|
+
var profileStorage = getProfileStorage();
|
|
20318
|
+
|
|
20319
|
+
class AuthProfileValidationError extends Error {
|
|
20320
|
+
constructor(message) {
|
|
20321
|
+
super(message);
|
|
20322
|
+
this.name = "AuthProfileValidationError";
|
|
20323
|
+
}
|
|
20324
|
+
}
|
|
20325
|
+
function normalizeAuthProfileName(profile) {
|
|
20326
|
+
if (profile === undefined || profile === DEFAULT_AUTH_PROFILE) {
|
|
20327
|
+
return;
|
|
20328
|
+
}
|
|
20329
|
+
if (profile.length === 0 || profile === "." || profile === ".." || !PROFILE_NAME_RE.test(profile)) {
|
|
20330
|
+
throw new AuthProfileValidationError(`Invalid profile name "${profile}". Profile names may contain only letters, numbers, '.', '_', and '-'.`);
|
|
20331
|
+
}
|
|
20332
|
+
return profile;
|
|
20333
|
+
}
|
|
20334
|
+
function setActiveAuthProfile(profile) {
|
|
20335
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
20336
|
+
const scopedState = profileStorage.getStore();
|
|
20337
|
+
if (scopedState !== undefined) {
|
|
20338
|
+
if (normalized === undefined) {
|
|
20339
|
+
delete scopedState.profile;
|
|
20340
|
+
return;
|
|
20341
|
+
}
|
|
20342
|
+
scopedState.profile = normalized;
|
|
20343
|
+
return;
|
|
20344
|
+
}
|
|
20345
|
+
if (normalized === undefined) {
|
|
20346
|
+
delete globalSlot2[ACTIVE_AUTH_PROFILE_KEY];
|
|
20347
|
+
return;
|
|
20348
|
+
}
|
|
20349
|
+
globalSlot2[ACTIVE_AUTH_PROFILE_KEY] = { profile: normalized };
|
|
20350
|
+
}
|
|
20351
|
+
function clearActiveAuthProfile() {
|
|
20352
|
+
const scopedState = profileStorage.getStore();
|
|
20353
|
+
if (scopedState !== undefined) {
|
|
20354
|
+
delete scopedState.profile;
|
|
20355
|
+
return;
|
|
20356
|
+
}
|
|
20357
|
+
delete globalSlot2[ACTIVE_AUTH_PROFILE_KEY];
|
|
20358
|
+
}
|
|
20359
|
+
function getActiveAuthProfile() {
|
|
20360
|
+
const scopedState = profileStorage.getStore();
|
|
20361
|
+
if (scopedState !== undefined) {
|
|
20362
|
+
return scopedState.profile;
|
|
20363
|
+
}
|
|
20364
|
+
return globalSlot2[ACTIVE_AUTH_PROFILE_KEY]?.profile;
|
|
20365
|
+
}
|
|
20366
|
+
function runWithAuthProfile(profile, fn) {
|
|
20367
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
20368
|
+
return profileStorage.run(normalized === undefined ? {} : { profile: normalized }, fn);
|
|
20369
|
+
}
|
|
20370
|
+
function resolveAuthProfileFilePath(profile) {
|
|
20371
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
20372
|
+
if (normalized === undefined) {
|
|
20373
|
+
throw new AuthProfileValidationError(`"${DEFAULT_AUTH_PROFILE}" is the built-in profile and does not have a profile file path.`);
|
|
20374
|
+
}
|
|
20375
|
+
const fs7 = getFileSystem();
|
|
20376
|
+
return fs7.path.join(fs7.env.homedir(), UIPATH_HOME_DIR, PROFILE_DIR, normalized, AUTH_FILENAME);
|
|
20377
|
+
}
|
|
20378
|
+
function getActiveAuthProfileFilePath() {
|
|
20379
|
+
const profile = getActiveAuthProfile();
|
|
20380
|
+
return profile ? resolveAuthProfileFilePath(profile) : undefined;
|
|
20381
|
+
}
|
|
20253
20382
|
// src/utils/jwt.ts
|
|
20254
20383
|
class InvalidIssuerError extends Error {
|
|
20255
20384
|
expected;
|
|
@@ -20378,23 +20507,74 @@ var readAuthFromEnv = () => {
|
|
|
20378
20507
|
organizationId,
|
|
20379
20508
|
tenantName,
|
|
20380
20509
|
tenantId,
|
|
20381
|
-
expiration
|
|
20510
|
+
expiration,
|
|
20511
|
+
source: "env" /* Env */
|
|
20382
20512
|
};
|
|
20383
20513
|
};
|
|
20384
20514
|
|
|
20515
|
+
// src/refreshCircuitBreaker.ts
|
|
20516
|
+
init_src();
|
|
20517
|
+
var BREAKER_SUFFIX = ".refresh-state";
|
|
20518
|
+
var BACKOFF_BASE_MS = 60000;
|
|
20519
|
+
var BACKOFF_CAP_MS = 60 * 60 * 1000;
|
|
20520
|
+
var SURFACE_WINDOW_MS = 60 * 60 * 1000;
|
|
20521
|
+
async function refreshTokenFingerprint(refreshToken) {
|
|
20522
|
+
const bytes = new TextEncoder().encode(refreshToken);
|
|
20523
|
+
if (globalThis.crypto?.subtle) {
|
|
20524
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
20525
|
+
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
20526
|
+
}
|
|
20527
|
+
const { createHash } = await import("node:crypto");
|
|
20528
|
+
return createHash("sha256").update(refreshToken).digest("hex").slice(0, 16);
|
|
20529
|
+
}
|
|
20530
|
+
function breakerPathFor(authPath) {
|
|
20531
|
+
return `${authPath}${BREAKER_SUFFIX}`;
|
|
20532
|
+
}
|
|
20533
|
+
async function loadRefreshBreaker(authPath) {
|
|
20534
|
+
const fs7 = getFileSystem();
|
|
20535
|
+
try {
|
|
20536
|
+
const content = await fs7.readFile(breakerPathFor(authPath), "utf-8");
|
|
20537
|
+
if (!content)
|
|
20538
|
+
return {};
|
|
20539
|
+
const parsed = JSON.parse(content);
|
|
20540
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
20541
|
+
} catch {
|
|
20542
|
+
return {};
|
|
20543
|
+
}
|
|
20544
|
+
}
|
|
20545
|
+
async function saveRefreshBreaker(authPath, state) {
|
|
20546
|
+
try {
|
|
20547
|
+
const fs7 = getFileSystem();
|
|
20548
|
+
const path3 = breakerPathFor(authPath);
|
|
20549
|
+
await fs7.mkdir(fs7.path.dirname(path3));
|
|
20550
|
+
const tempPath = `${path3}.tmp`;
|
|
20551
|
+
await fs7.writeFile(tempPath, JSON.stringify(state));
|
|
20552
|
+
await fs7.rename(tempPath, path3);
|
|
20553
|
+
} catch {}
|
|
20554
|
+
}
|
|
20555
|
+
async function clearRefreshBreaker(authPath) {
|
|
20556
|
+
const fs7 = getFileSystem();
|
|
20557
|
+
const path3 = breakerPathFor(authPath);
|
|
20558
|
+
try {
|
|
20559
|
+
if (await fs7.exists(path3)) {
|
|
20560
|
+
await fs7.rm(path3);
|
|
20561
|
+
}
|
|
20562
|
+
} catch {}
|
|
20563
|
+
}
|
|
20564
|
+
function nextBackoffMs(attempts) {
|
|
20565
|
+
const shift = Math.max(0, attempts - 1);
|
|
20566
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** shift, BACKOFF_CAP_MS);
|
|
20567
|
+
}
|
|
20568
|
+
function shouldSurface(state, nowMs) {
|
|
20569
|
+
if (state.lastSurfacedAtMs === undefined)
|
|
20570
|
+
return true;
|
|
20571
|
+
return nowMs - state.lastSurfacedAtMs >= SURFACE_WINDOW_MS;
|
|
20572
|
+
}
|
|
20573
|
+
|
|
20385
20574
|
// src/robotClientFallback.ts
|
|
20386
20575
|
init_src();
|
|
20387
20576
|
var DEFAULT_TIMEOUT_MS = 1000;
|
|
20388
20577
|
var CLOSE_TIMEOUT_MS = 500;
|
|
20389
|
-
var NOTICE_SENTINEL = Symbol.for("@uipath/auth/robotFallbackNoticePrinted");
|
|
20390
|
-
var printNoticeOnce = () => {
|
|
20391
|
-
const slot = globalThis;
|
|
20392
|
-
if (slot[NOTICE_SENTINEL])
|
|
20393
|
-
return;
|
|
20394
|
-
slot[NOTICE_SENTINEL] = true;
|
|
20395
|
-
catchError(() => process.stderr.write(`Using UiPath Robot credentials. Run 'uip login' for a dedicated session.
|
|
20396
|
-
`));
|
|
20397
|
-
};
|
|
20398
20578
|
var ROBOT_USER_SERVICES_PIPE = "UiPathUserServices";
|
|
20399
20579
|
var ROBOT_USER_SERVICES_ALTERNATE_PIPE = `${ROBOT_USER_SERVICES_PIPE}Alternate`;
|
|
20400
20580
|
var PIPE_NAME_MAX_LENGTH = 103;
|
|
@@ -20510,7 +20690,6 @@ var tryRobotClientFallback = async (options = {}) => {
|
|
|
20510
20690
|
issuerFromToken = issClaim;
|
|
20511
20691
|
}
|
|
20512
20692
|
}
|
|
20513
|
-
printNoticeOnce();
|
|
20514
20693
|
return {
|
|
20515
20694
|
accessToken,
|
|
20516
20695
|
baseUrl: parsedUrl.baseUrl,
|
|
@@ -20740,19 +20919,329 @@ var LoginStatusSource;
|
|
|
20740
20919
|
((LoginStatusSource2) => {
|
|
20741
20920
|
LoginStatusSource2["File"] = "file";
|
|
20742
20921
|
LoginStatusSource2["Robot"] = "robot";
|
|
20922
|
+
LoginStatusSource2["Env"] = "env";
|
|
20743
20923
|
})(LoginStatusSource ||= {});
|
|
20744
|
-
|
|
20745
|
-
return
|
|
20924
|
+
var getLoginStatusAsync = async (options = {}) => {
|
|
20925
|
+
return getLoginStatusWithDeps(options);
|
|
20926
|
+
};
|
|
20927
|
+
var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
20928
|
+
const {
|
|
20929
|
+
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
20930
|
+
loadEnvFile = loadEnvFileAsync,
|
|
20931
|
+
saveEnvFile = saveEnvFileAsync,
|
|
20932
|
+
getFs = getFileSystem,
|
|
20933
|
+
refreshToken: refreshTokenFn = refreshAccessToken,
|
|
20934
|
+
resolveConfig = resolveConfigAsync,
|
|
20935
|
+
robotFallback = tryRobotClientFallback,
|
|
20936
|
+
loadBreaker = loadRefreshBreaker,
|
|
20937
|
+
saveBreaker = saveRefreshBreaker,
|
|
20938
|
+
clearBreaker = clearRefreshBreaker
|
|
20939
|
+
} = deps;
|
|
20940
|
+
if (isRobotAuthEnforced()) {
|
|
20941
|
+
return resolveRobotEnforcedStatus(robotFallback);
|
|
20942
|
+
}
|
|
20943
|
+
if (isEnvAuthEnabled()) {
|
|
20944
|
+
return readAuthFromEnv();
|
|
20945
|
+
}
|
|
20946
|
+
const activeProfile = getActiveAuthProfile();
|
|
20947
|
+
const activeProfileFilePath = getActiveAuthProfileFilePath();
|
|
20948
|
+
const usingActiveProfile = activeProfile !== undefined && (options.envFilePath === undefined || options.envFilePath === activeProfileFilePath);
|
|
20949
|
+
const envFilePath = options.envFilePath ?? activeProfileFilePath ?? DEFAULT_ENV_FILENAME;
|
|
20950
|
+
const { ensureTokenValidityMinutes } = options;
|
|
20951
|
+
const { absolutePath } = await resolveEnvFilePath(envFilePath);
|
|
20952
|
+
if (absolutePath === undefined) {
|
|
20953
|
+
if (usingActiveProfile) {
|
|
20954
|
+
return {
|
|
20955
|
+
loginStatus: "Not logged in",
|
|
20956
|
+
hint: `No credentials found for profile "${activeProfile}". Run 'uip login --profile ${activeProfile}' to authenticate this profile.`
|
|
20957
|
+
};
|
|
20958
|
+
}
|
|
20959
|
+
return resolveBorrowedRobotStatus(robotFallback);
|
|
20960
|
+
}
|
|
20961
|
+
const loaded = await loadFileCredentials(loadEnvFile, absolutePath);
|
|
20962
|
+
if ("status" in loaded) {
|
|
20963
|
+
return loaded.status;
|
|
20964
|
+
}
|
|
20965
|
+
const { credentials } = loaded;
|
|
20966
|
+
const globalHint = () => usingActiveProfile ? Promise.resolve(undefined) : getGlobalCredsHint(getFs, loadEnvFile, absolutePath, envFilePath);
|
|
20967
|
+
const expiration = getTokenExpiration(credentials.UIPATH_ACCESS_TOKEN);
|
|
20968
|
+
const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
20969
|
+
let tokens = {
|
|
20970
|
+
accessToken: credentials.UIPATH_ACCESS_TOKEN,
|
|
20971
|
+
refreshToken: credentials.UIPATH_REFRESH_TOKEN,
|
|
20972
|
+
expiration,
|
|
20973
|
+
lockReleaseFailed: false
|
|
20974
|
+
};
|
|
20975
|
+
const refreshToken = credentials.UIPATH_REFRESH_TOKEN;
|
|
20976
|
+
if (expiration && expiration <= outerThreshold && refreshToken) {
|
|
20977
|
+
const refreshed = await attemptRefresh({
|
|
20978
|
+
absolutePath,
|
|
20979
|
+
credentials,
|
|
20980
|
+
accessToken: credentials.UIPATH_ACCESS_TOKEN,
|
|
20981
|
+
refreshToken,
|
|
20982
|
+
expiration,
|
|
20983
|
+
ensureTokenValidityMinutes,
|
|
20984
|
+
getFs,
|
|
20985
|
+
loadEnvFile,
|
|
20986
|
+
saveEnvFile,
|
|
20987
|
+
refreshFn: refreshTokenFn,
|
|
20988
|
+
resolveConfig,
|
|
20989
|
+
loadBreaker,
|
|
20990
|
+
saveBreaker,
|
|
20991
|
+
clearBreaker,
|
|
20992
|
+
globalHint
|
|
20993
|
+
});
|
|
20994
|
+
if (refreshed.kind === "terminal") {
|
|
20995
|
+
return refreshed.status;
|
|
20996
|
+
}
|
|
20997
|
+
tokens = refreshed.tokens;
|
|
20998
|
+
}
|
|
20999
|
+
return buildFileStatus(tokens, credentials, globalHint);
|
|
21000
|
+
};
|
|
21001
|
+
async function resolveRobotEnforcedStatus(robotFallback) {
|
|
21002
|
+
if (isEnvAuthEnabled()) {
|
|
21003
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
|
|
21004
|
+
}
|
|
21005
|
+
const robotCreds = await robotFallback({ force: true });
|
|
21006
|
+
if (!robotCreds) {
|
|
21007
|
+
return {
|
|
21008
|
+
loginStatus: "Not logged in",
|
|
21009
|
+
hint: `${ENFORCE_ROBOT_AUTH_VAR}=true but the UiPath Robot ` + `session is unavailable. Start and sign in to the Assistant, ` + `or unset ${ENFORCE_ROBOT_AUTH_VAR} to fall back to file or ` + `env-var authentication.`
|
|
21010
|
+
};
|
|
21011
|
+
}
|
|
21012
|
+
return buildRobotStatus(robotCreds);
|
|
20746
21013
|
}
|
|
20747
|
-
function
|
|
20748
|
-
|
|
21014
|
+
async function resolveBorrowedRobotStatus(robotFallback) {
|
|
21015
|
+
const robotCreds = await robotFallback();
|
|
21016
|
+
return robotCreds ? buildRobotStatus(robotCreds) : { loginStatus: "Not logged in" };
|
|
20749
21017
|
}
|
|
20750
|
-
function
|
|
20751
|
-
|
|
21018
|
+
async function loadFileCredentials(loadEnvFile, absolutePath) {
|
|
21019
|
+
let credentials;
|
|
21020
|
+
try {
|
|
21021
|
+
credentials = await loadEnvFile({ envPath: absolutePath });
|
|
21022
|
+
} catch (error) {
|
|
21023
|
+
if (isFileNotFoundError(error)) {
|
|
21024
|
+
return { status: { loginStatus: "Not logged in" } };
|
|
21025
|
+
}
|
|
21026
|
+
throw error;
|
|
21027
|
+
}
|
|
21028
|
+
if (!credentials.UIPATH_ACCESS_TOKEN) {
|
|
21029
|
+
return { status: { loginStatus: "Not logged in" } };
|
|
21030
|
+
}
|
|
21031
|
+
return { credentials };
|
|
21032
|
+
}
|
|
21033
|
+
async function getGlobalCredsHint(getFs, loadEnvFile, absolutePath, envFilePath) {
|
|
21034
|
+
const fs7 = getFs();
|
|
21035
|
+
const globalPath = fs7.path.join(fs7.env.homedir(), envFilePath);
|
|
21036
|
+
if (absolutePath === globalPath)
|
|
21037
|
+
return;
|
|
21038
|
+
if (!await fs7.exists(globalPath))
|
|
21039
|
+
return;
|
|
21040
|
+
try {
|
|
21041
|
+
const globalCreds = await loadEnvFile({ envPath: globalPath });
|
|
21042
|
+
if (!globalCreds.UIPATH_ACCESS_TOKEN)
|
|
21043
|
+
return;
|
|
21044
|
+
const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
|
|
21045
|
+
if (globalExp && globalExp <= new Date)
|
|
21046
|
+
return;
|
|
21047
|
+
return `Local credentials file at ${absolutePath} has expired credentials. Valid credentials exist in ${globalPath}. Remove the local file or run 'uip login' to re-authenticate.`;
|
|
21048
|
+
} catch {
|
|
21049
|
+
return;
|
|
21050
|
+
}
|
|
20752
21051
|
}
|
|
20753
21052
|
function computeExpirationThreshold(ensureTokenValidityMinutes) {
|
|
20754
21053
|
return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
|
|
20755
21054
|
}
|
|
21055
|
+
async function attemptRefresh(ctx) {
|
|
21056
|
+
const shortCircuit = await circuitBreakerShortCircuit(ctx);
|
|
21057
|
+
if (shortCircuit) {
|
|
21058
|
+
return { kind: "terminal", status: shortCircuit };
|
|
21059
|
+
}
|
|
21060
|
+
let release;
|
|
21061
|
+
try {
|
|
21062
|
+
release = await ctx.getFs().acquireLock(ctx.absolutePath);
|
|
21063
|
+
} catch (error) {
|
|
21064
|
+
return {
|
|
21065
|
+
kind: "terminal",
|
|
21066
|
+
status: await lockAcquireFailureStatus(ctx, error)
|
|
21067
|
+
};
|
|
21068
|
+
}
|
|
21069
|
+
let lockedFailure;
|
|
21070
|
+
let lockReleaseFailed = false;
|
|
21071
|
+
let success;
|
|
21072
|
+
try {
|
|
21073
|
+
const outcome = await runRefreshLocked({
|
|
21074
|
+
absolutePath: ctx.absolutePath,
|
|
21075
|
+
refreshToken: ctx.refreshToken,
|
|
21076
|
+
customAuthority: ctx.credentials.UIPATH_URL,
|
|
21077
|
+
ensureTokenValidityMinutes: ctx.ensureTokenValidityMinutes,
|
|
21078
|
+
loadEnvFile: ctx.loadEnvFile,
|
|
21079
|
+
saveEnvFile: ctx.saveEnvFile,
|
|
21080
|
+
refreshFn: ctx.refreshFn,
|
|
21081
|
+
resolveConfig: ctx.resolveConfig,
|
|
21082
|
+
loadBreaker: ctx.loadBreaker,
|
|
21083
|
+
saveBreaker: ctx.saveBreaker,
|
|
21084
|
+
clearBreaker: ctx.clearBreaker
|
|
21085
|
+
});
|
|
21086
|
+
if (outcome.kind === "fail") {
|
|
21087
|
+
lockedFailure = outcome.status;
|
|
21088
|
+
} else {
|
|
21089
|
+
success = outcome;
|
|
21090
|
+
}
|
|
21091
|
+
} finally {
|
|
21092
|
+
try {
|
|
21093
|
+
await release();
|
|
21094
|
+
} catch {
|
|
21095
|
+
lockReleaseFailed = true;
|
|
21096
|
+
}
|
|
21097
|
+
}
|
|
21098
|
+
if (lockedFailure) {
|
|
21099
|
+
const globalHint = await ctx.globalHint();
|
|
21100
|
+
const base = globalHint ? { ...lockedFailure, loginStatus: "Expired", hint: globalHint } : lockedFailure;
|
|
21101
|
+
return {
|
|
21102
|
+
kind: "terminal",
|
|
21103
|
+
status: lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base
|
|
21104
|
+
};
|
|
21105
|
+
}
|
|
21106
|
+
return {
|
|
21107
|
+
kind: "refreshed",
|
|
21108
|
+
tokens: {
|
|
21109
|
+
accessToken: success?.accessToken,
|
|
21110
|
+
refreshToken: success?.refreshToken,
|
|
21111
|
+
expiration: success?.expiration,
|
|
21112
|
+
tokenRefresh: success?.tokenRefresh,
|
|
21113
|
+
persistenceWarning: success?.persistenceWarning,
|
|
21114
|
+
lockReleaseFailed
|
|
21115
|
+
}
|
|
21116
|
+
};
|
|
21117
|
+
}
|
|
21118
|
+
async function buildFileStatus(tokens, credentials, globalHint) {
|
|
21119
|
+
const result = {
|
|
21120
|
+
loginStatus: tokens.expiration && tokens.expiration <= new Date ? "Expired" : "Logged in",
|
|
21121
|
+
accessToken: tokens.accessToken,
|
|
21122
|
+
refreshToken: tokens.refreshToken,
|
|
21123
|
+
baseUrl: credentials.UIPATH_URL,
|
|
21124
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
21125
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
21126
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
21127
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
21128
|
+
expiration: tokens.expiration,
|
|
21129
|
+
source: "file" /* File */,
|
|
21130
|
+
...tokens.persistenceWarning ? { hint: tokens.persistenceWarning, persistenceFailed: true } : {},
|
|
21131
|
+
...tokens.lockReleaseFailed ? { lockReleaseFailed: true } : {},
|
|
21132
|
+
...tokens.tokenRefresh ? { tokenRefresh: tokens.tokenRefresh } : {}
|
|
21133
|
+
};
|
|
21134
|
+
if (result.loginStatus === "Expired") {
|
|
21135
|
+
const hint = await globalHint();
|
|
21136
|
+
if (hint) {
|
|
21137
|
+
result.hint = hint;
|
|
21138
|
+
}
|
|
21139
|
+
}
|
|
21140
|
+
return result;
|
|
21141
|
+
}
|
|
21142
|
+
function buildRobotStatus(robotCreds) {
|
|
21143
|
+
return {
|
|
21144
|
+
loginStatus: "Logged in",
|
|
21145
|
+
accessToken: robotCreds.accessToken,
|
|
21146
|
+
baseUrl: robotCreds.baseUrl,
|
|
21147
|
+
organizationName: robotCreds.organizationName,
|
|
21148
|
+
organizationId: robotCreds.organizationId,
|
|
21149
|
+
tenantName: robotCreds.tenantName,
|
|
21150
|
+
tenantId: robotCreds.tenantId,
|
|
21151
|
+
issuer: robotCreds.issuer,
|
|
21152
|
+
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
21153
|
+
source: "robot" /* Robot */
|
|
21154
|
+
};
|
|
21155
|
+
}
|
|
21156
|
+
var isFileNotFoundError = (error) => {
|
|
21157
|
+
if (!(error instanceof Object))
|
|
21158
|
+
return false;
|
|
21159
|
+
return error.code === "ENOENT";
|
|
21160
|
+
};
|
|
21161
|
+
async function circuitBreakerShortCircuit(ctx) {
|
|
21162
|
+
const {
|
|
21163
|
+
absolutePath,
|
|
21164
|
+
refreshToken,
|
|
21165
|
+
accessToken,
|
|
21166
|
+
credentials,
|
|
21167
|
+
expiration,
|
|
21168
|
+
loadBreaker,
|
|
21169
|
+
saveBreaker,
|
|
21170
|
+
clearBreaker
|
|
21171
|
+
} = ctx;
|
|
21172
|
+
const fingerprint = await refreshTokenFingerprint(refreshToken);
|
|
21173
|
+
const breaker = await loadBreaker(absolutePath).catch(() => ({}));
|
|
21174
|
+
if (breaker.deadTokenFp && breaker.deadTokenFp !== fingerprint) {
|
|
21175
|
+
await clearBreaker(absolutePath);
|
|
21176
|
+
breaker.deadTokenFp = undefined;
|
|
21177
|
+
}
|
|
21178
|
+
const nowMs = Date.now();
|
|
21179
|
+
const tokenIsDead = breaker.deadTokenFp === fingerprint;
|
|
21180
|
+
const inBackoff = breaker.backoffUntilMs !== undefined && nowMs < breaker.backoffUntilMs;
|
|
21181
|
+
if (!tokenIsDead && !inBackoff)
|
|
21182
|
+
return;
|
|
21183
|
+
const globalHint = await ctx.globalHint();
|
|
21184
|
+
const suppressed = !shouldSurface(breaker, nowMs);
|
|
21185
|
+
if (!suppressed) {
|
|
21186
|
+
await saveBreaker(absolutePath, {
|
|
21187
|
+
...breaker,
|
|
21188
|
+
lastSurfacedAtMs: nowMs
|
|
21189
|
+
});
|
|
21190
|
+
}
|
|
21191
|
+
const deadHint = "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired. In a non-interactive context, authenticate with: uip login --client-id <id> --client-secret <secret> -t <tenant>.";
|
|
21192
|
+
const backoffHint = "Token refresh is temporarily backed off after a recent network error and will retry automatically once the backoff window elapses.";
|
|
21193
|
+
return {
|
|
21194
|
+
loginStatus: globalHint ? "Expired" : "Refresh Failed",
|
|
21195
|
+
...globalHint ? {
|
|
21196
|
+
accessToken,
|
|
21197
|
+
refreshToken,
|
|
21198
|
+
baseUrl: credentials.UIPATH_URL,
|
|
21199
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
21200
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
21201
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
21202
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
21203
|
+
expiration,
|
|
21204
|
+
source: "file" /* File */
|
|
21205
|
+
} : {},
|
|
21206
|
+
hint: globalHint ?? (tokenIsDead ? deadHint : backoffHint),
|
|
21207
|
+
refreshCircuitOpen: true,
|
|
21208
|
+
refreshTelemetrySuppressed: suppressed,
|
|
21209
|
+
tokenRefresh: { attempted: false, success: false }
|
|
21210
|
+
};
|
|
21211
|
+
}
|
|
21212
|
+
async function lockAcquireFailureStatus(ctx, error) {
|
|
21213
|
+
const msg = errorMessage(error);
|
|
21214
|
+
const globalHint = await ctx.globalHint();
|
|
21215
|
+
if (globalHint) {
|
|
21216
|
+
return {
|
|
21217
|
+
loginStatus: "Expired",
|
|
21218
|
+
accessToken: ctx.accessToken,
|
|
21219
|
+
refreshToken: ctx.refreshToken,
|
|
21220
|
+
baseUrl: ctx.credentials.UIPATH_URL,
|
|
21221
|
+
organizationName: ctx.credentials.UIPATH_ORGANIZATION_NAME,
|
|
21222
|
+
organizationId: ctx.credentials.UIPATH_ORGANIZATION_ID,
|
|
21223
|
+
tenantName: ctx.credentials.UIPATH_TENANT_NAME,
|
|
21224
|
+
tenantId: ctx.credentials.UIPATH_TENANT_ID,
|
|
21225
|
+
expiration: ctx.expiration,
|
|
21226
|
+
source: "file" /* File */,
|
|
21227
|
+
hint: globalHint,
|
|
21228
|
+
tokenRefresh: {
|
|
21229
|
+
attempted: false,
|
|
21230
|
+
success: false,
|
|
21231
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
21232
|
+
}
|
|
21233
|
+
};
|
|
21234
|
+
}
|
|
21235
|
+
return {
|
|
21236
|
+
loginStatus: "Refresh Failed",
|
|
21237
|
+
hint: "Could not acquire the auth-file lock — too many concurrent `uip` processes, or a permission issue on the auth directory. Retry, or run 'uip login' to re-authenticate.",
|
|
21238
|
+
tokenRefresh: {
|
|
21239
|
+
attempted: false,
|
|
21240
|
+
success: false,
|
|
21241
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
21242
|
+
}
|
|
21243
|
+
};
|
|
21244
|
+
}
|
|
20756
21245
|
async function runRefreshLocked(inputs) {
|
|
20757
21246
|
const {
|
|
20758
21247
|
absolutePath,
|
|
@@ -20762,7 +21251,10 @@ async function runRefreshLocked(inputs) {
|
|
|
20762
21251
|
loadEnvFile,
|
|
20763
21252
|
saveEnvFile,
|
|
20764
21253
|
refreshFn,
|
|
20765
|
-
resolveConfig
|
|
21254
|
+
resolveConfig,
|
|
21255
|
+
loadBreaker,
|
|
21256
|
+
saveBreaker,
|
|
21257
|
+
clearBreaker
|
|
20766
21258
|
} = inputs;
|
|
20767
21259
|
const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
20768
21260
|
let fresh;
|
|
@@ -20785,6 +21277,7 @@ async function runRefreshLocked(inputs) {
|
|
|
20785
21277
|
const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
|
|
20786
21278
|
const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
|
|
20787
21279
|
if (freshAccess && freshExp && freshExp > expirationThreshold) {
|
|
21280
|
+
await clearBreaker(absolutePath);
|
|
20788
21281
|
return {
|
|
20789
21282
|
kind: "ok",
|
|
20790
21283
|
accessToken: freshAccess,
|
|
@@ -20808,8 +21301,21 @@ async function runRefreshLocked(inputs) {
|
|
|
20808
21301
|
refreshedRefresh = refreshed.refreshToken;
|
|
20809
21302
|
} catch (error) {
|
|
20810
21303
|
const isOAuthFailure = isTokenRefreshOAuthFailure(error);
|
|
20811
|
-
const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
|
|
21304
|
+
const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired. In a non-interactive context, authenticate with: uip login --client-id <id> --client-secret <secret> -t <tenant>." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
|
|
20812
21305
|
const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
|
|
21306
|
+
const fp = await refreshTokenFingerprint(tokenForIdP);
|
|
21307
|
+
if (isOAuthFailure) {
|
|
21308
|
+
await saveBreaker(absolutePath, { deadTokenFp: fp });
|
|
21309
|
+
} else {
|
|
21310
|
+
const prior = await loadBreaker(absolutePath).catch(() => ({}));
|
|
21311
|
+
const attempts = (prior.attempts ?? 0) + 1;
|
|
21312
|
+
await saveBreaker(absolutePath, {
|
|
21313
|
+
...prior,
|
|
21314
|
+
deadTokenFp: undefined,
|
|
21315
|
+
attempts,
|
|
21316
|
+
backoffUntilMs: Date.now() + nextBackoffMs(attempts)
|
|
21317
|
+
});
|
|
21318
|
+
}
|
|
20813
21319
|
return {
|
|
20814
21320
|
kind: "fail",
|
|
20815
21321
|
status: {
|
|
@@ -20838,6 +21344,7 @@ async function runRefreshLocked(inputs) {
|
|
|
20838
21344
|
}
|
|
20839
21345
|
};
|
|
20840
21346
|
}
|
|
21347
|
+
await clearBreaker(absolutePath);
|
|
20841
21348
|
try {
|
|
20842
21349
|
await saveEnvFile({
|
|
20843
21350
|
envPath: absolutePath,
|
|
@@ -20870,212 +21377,15 @@ async function runRefreshLocked(inputs) {
|
|
|
20870
21377
|
};
|
|
20871
21378
|
}
|
|
20872
21379
|
}
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
} = deps;
|
|
20883
|
-
if (isRobotAuthEnforced()) {
|
|
20884
|
-
if (isEnvAuthEnabled()) {
|
|
20885
|
-
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
|
|
20886
|
-
}
|
|
20887
|
-
const robotCreds = await robotFallback({ force: true });
|
|
20888
|
-
if (!robotCreds) {
|
|
20889
|
-
return {
|
|
20890
|
-
loginStatus: "Not logged in",
|
|
20891
|
-
hint: `${ENFORCE_ROBOT_AUTH_VAR}=true but the UiPath Robot ` + `session is unavailable. Start and sign in to the Assistant, ` + `or unset ${ENFORCE_ROBOT_AUTH_VAR} to fall back to file or ` + `env-var authentication.`
|
|
20892
|
-
};
|
|
20893
|
-
}
|
|
20894
|
-
const expiration2 = getTokenExpiration(robotCreds.accessToken);
|
|
20895
|
-
return {
|
|
20896
|
-
loginStatus: "Logged in",
|
|
20897
|
-
accessToken: robotCreds.accessToken,
|
|
20898
|
-
baseUrl: robotCreds.baseUrl,
|
|
20899
|
-
organizationName: robotCreds.organizationName,
|
|
20900
|
-
organizationId: robotCreds.organizationId,
|
|
20901
|
-
tenantName: robotCreds.tenantName,
|
|
20902
|
-
tenantId: robotCreds.tenantId,
|
|
20903
|
-
issuer: robotCreds.issuer,
|
|
20904
|
-
expiration: expiration2,
|
|
20905
|
-
source: "robot" /* Robot */
|
|
20906
|
-
};
|
|
20907
|
-
}
|
|
20908
|
-
if (isEnvAuthEnabled()) {
|
|
20909
|
-
return readAuthFromEnv();
|
|
20910
|
-
}
|
|
20911
|
-
const { envFilePath = DEFAULT_ENV_FILENAME, ensureTokenValidityMinutes } = options;
|
|
20912
|
-
const { absolutePath } = await resolveEnvFilePath(envFilePath);
|
|
20913
|
-
if (absolutePath === undefined) {
|
|
20914
|
-
const robotCreds = await robotFallback();
|
|
20915
|
-
if (robotCreds) {
|
|
20916
|
-
const expiration2 = getTokenExpiration(robotCreds.accessToken);
|
|
20917
|
-
const status = {
|
|
20918
|
-
loginStatus: "Logged in",
|
|
20919
|
-
accessToken: robotCreds.accessToken,
|
|
20920
|
-
baseUrl: robotCreds.baseUrl,
|
|
20921
|
-
organizationName: robotCreds.organizationName,
|
|
20922
|
-
organizationId: robotCreds.organizationId,
|
|
20923
|
-
tenantName: robotCreds.tenantName,
|
|
20924
|
-
tenantId: robotCreds.tenantId,
|
|
20925
|
-
issuer: robotCreds.issuer,
|
|
20926
|
-
expiration: expiration2,
|
|
20927
|
-
source: "robot" /* Robot */
|
|
20928
|
-
};
|
|
20929
|
-
return status;
|
|
20930
|
-
}
|
|
20931
|
-
return { loginStatus: "Not logged in" };
|
|
20932
|
-
}
|
|
20933
|
-
let credentials;
|
|
20934
|
-
try {
|
|
20935
|
-
credentials = await loadEnvFile({ envPath: absolutePath });
|
|
20936
|
-
} catch (error) {
|
|
20937
|
-
if (isFileNotFoundError(error)) {
|
|
20938
|
-
return { loginStatus: "Not logged in" };
|
|
20939
|
-
}
|
|
20940
|
-
throw error;
|
|
20941
|
-
}
|
|
20942
|
-
if (!credentials.UIPATH_ACCESS_TOKEN) {
|
|
20943
|
-
return { loginStatus: "Not logged in" };
|
|
20944
|
-
}
|
|
20945
|
-
let accessToken = credentials.UIPATH_ACCESS_TOKEN;
|
|
20946
|
-
let refreshToken = credentials.UIPATH_REFRESH_TOKEN;
|
|
20947
|
-
let expiration = getTokenExpiration(accessToken);
|
|
20948
|
-
let persistenceWarning;
|
|
20949
|
-
let lockReleaseFailed = false;
|
|
20950
|
-
let tokenRefresh;
|
|
20951
|
-
const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
20952
|
-
const tryGlobalCredsHint = async () => {
|
|
20953
|
-
const fs7 = getFs();
|
|
20954
|
-
const globalPath = fs7.path.join(fs7.env.homedir(), envFilePath);
|
|
20955
|
-
if (absolutePath === globalPath)
|
|
20956
|
-
return;
|
|
20957
|
-
if (!await fs7.exists(globalPath))
|
|
20958
|
-
return;
|
|
20959
|
-
try {
|
|
20960
|
-
const globalCreds = await loadEnvFile({ envPath: globalPath });
|
|
20961
|
-
if (!globalCreds.UIPATH_ACCESS_TOKEN)
|
|
20962
|
-
return;
|
|
20963
|
-
const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
|
|
20964
|
-
if (globalExp && globalExp <= new Date)
|
|
20965
|
-
return;
|
|
20966
|
-
return `Local credentials file at ${absolutePath} has expired credentials. Valid credentials exist in ${globalPath}. Remove the local file or run 'uip login' to re-authenticate.`;
|
|
20967
|
-
} catch {
|
|
20968
|
-
return;
|
|
20969
|
-
}
|
|
20970
|
-
};
|
|
20971
|
-
if (expiration && expiration <= outerThreshold && refreshToken) {
|
|
20972
|
-
let release;
|
|
20973
|
-
try {
|
|
20974
|
-
release = await getFs().acquireLock(absolutePath);
|
|
20975
|
-
} catch (error) {
|
|
20976
|
-
const msg = errorMessage(error);
|
|
20977
|
-
const globalHint = await tryGlobalCredsHint();
|
|
20978
|
-
if (globalHint) {
|
|
20979
|
-
return {
|
|
20980
|
-
loginStatus: "Expired",
|
|
20981
|
-
accessToken,
|
|
20982
|
-
refreshToken,
|
|
20983
|
-
baseUrl: credentials.UIPATH_URL,
|
|
20984
|
-
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
20985
|
-
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
20986
|
-
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
20987
|
-
tenantId: credentials.UIPATH_TENANT_ID,
|
|
20988
|
-
expiration,
|
|
20989
|
-
source: "file" /* File */,
|
|
20990
|
-
hint: globalHint,
|
|
20991
|
-
tokenRefresh: {
|
|
20992
|
-
attempted: false,
|
|
20993
|
-
success: false,
|
|
20994
|
-
errorMessage: `lock acquisition failed: ${msg}`
|
|
20995
|
-
}
|
|
20996
|
-
};
|
|
20997
|
-
}
|
|
20998
|
-
return {
|
|
20999
|
-
loginStatus: "Refresh Failed",
|
|
21000
|
-
hint: "Could not acquire the auth-file lock — too many concurrent `uip` processes, or a permission issue on the auth directory. Retry, or run 'uip login' to re-authenticate.",
|
|
21001
|
-
tokenRefresh: {
|
|
21002
|
-
attempted: false,
|
|
21003
|
-
success: false,
|
|
21004
|
-
errorMessage: `lock acquisition failed: ${msg}`
|
|
21005
|
-
}
|
|
21006
|
-
};
|
|
21007
|
-
}
|
|
21008
|
-
let lockedFailure;
|
|
21009
|
-
try {
|
|
21010
|
-
const outcome = await runRefreshLocked({
|
|
21011
|
-
absolutePath,
|
|
21012
|
-
refreshToken,
|
|
21013
|
-
customAuthority: credentials.UIPATH_URL,
|
|
21014
|
-
ensureTokenValidityMinutes,
|
|
21015
|
-
loadEnvFile,
|
|
21016
|
-
saveEnvFile,
|
|
21017
|
-
refreshFn: refreshTokenFn,
|
|
21018
|
-
resolveConfig
|
|
21019
|
-
});
|
|
21020
|
-
if (outcome.kind === "fail") {
|
|
21021
|
-
lockedFailure = outcome.status;
|
|
21022
|
-
} else {
|
|
21023
|
-
accessToken = outcome.accessToken;
|
|
21024
|
-
refreshToken = outcome.refreshToken;
|
|
21025
|
-
expiration = outcome.expiration;
|
|
21026
|
-
tokenRefresh = outcome.tokenRefresh;
|
|
21027
|
-
if (outcome.persistenceWarning) {
|
|
21028
|
-
persistenceWarning = outcome.persistenceWarning;
|
|
21029
|
-
}
|
|
21030
|
-
}
|
|
21031
|
-
} finally {
|
|
21032
|
-
try {
|
|
21033
|
-
await release();
|
|
21034
|
-
} catch {
|
|
21035
|
-
lockReleaseFailed = true;
|
|
21036
|
-
}
|
|
21037
|
-
}
|
|
21038
|
-
if (lockedFailure) {
|
|
21039
|
-
const globalHint = await tryGlobalCredsHint();
|
|
21040
|
-
const base = globalHint ? {
|
|
21041
|
-
...lockedFailure,
|
|
21042
|
-
loginStatus: "Expired",
|
|
21043
|
-
hint: globalHint
|
|
21044
|
-
} : lockedFailure;
|
|
21045
|
-
return lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base;
|
|
21046
|
-
}
|
|
21047
|
-
}
|
|
21048
|
-
const result = {
|
|
21049
|
-
loginStatus: expiration && expiration <= new Date ? "Expired" : "Logged in",
|
|
21050
|
-
accessToken,
|
|
21051
|
-
refreshToken,
|
|
21052
|
-
baseUrl: credentials.UIPATH_URL,
|
|
21053
|
-
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
21054
|
-
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
21055
|
-
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
21056
|
-
tenantId: credentials.UIPATH_TENANT_ID,
|
|
21057
|
-
expiration,
|
|
21058
|
-
source: "file" /* File */,
|
|
21059
|
-
...persistenceWarning ? { hint: persistenceWarning, persistenceFailed: true } : {},
|
|
21060
|
-
...lockReleaseFailed ? { lockReleaseFailed: true } : {},
|
|
21061
|
-
...tokenRefresh ? { tokenRefresh } : {}
|
|
21062
|
-
};
|
|
21063
|
-
if (result.loginStatus === "Expired") {
|
|
21064
|
-
const globalHint = await tryGlobalCredsHint();
|
|
21065
|
-
if (globalHint) {
|
|
21066
|
-
result.hint = globalHint;
|
|
21067
|
-
}
|
|
21068
|
-
}
|
|
21069
|
-
return result;
|
|
21070
|
-
};
|
|
21071
|
-
var isFileNotFoundError = (error) => {
|
|
21072
|
-
if (!(error instanceof Object))
|
|
21073
|
-
return false;
|
|
21074
|
-
return error.code === "ENOENT";
|
|
21075
|
-
};
|
|
21076
|
-
var getLoginStatusAsync = async (options = {}) => {
|
|
21077
|
-
return getLoginStatusWithDeps(options);
|
|
21078
|
-
};
|
|
21380
|
+
function normalizeTokenRefreshFailure() {
|
|
21381
|
+
return "stored refresh token is invalid or expired";
|
|
21382
|
+
}
|
|
21383
|
+
function normalizeTokenRefreshUnavailableFailure() {
|
|
21384
|
+
return "token refresh failed before authentication completed";
|
|
21385
|
+
}
|
|
21386
|
+
function errorMessage(error) {
|
|
21387
|
+
return error instanceof Error ? error.message : String(error);
|
|
21388
|
+
}
|
|
21079
21389
|
|
|
21080
21390
|
// src/authContext.ts
|
|
21081
21391
|
var getAuthContext = async (options = {}) => {
|
|
@@ -21137,6 +21447,14 @@ var getAuthEnv = async (options = {}) => {
|
|
|
21137
21447
|
return { authEnv, loginStatus: status };
|
|
21138
21448
|
};
|
|
21139
21449
|
// src/clientCredentials.ts
|
|
21450
|
+
class ClientCredentialsAuthenticationError extends Error {
|
|
21451
|
+
status;
|
|
21452
|
+
constructor(message, status) {
|
|
21453
|
+
super(message);
|
|
21454
|
+
this.name = "ClientCredentialsAuthenticationError";
|
|
21455
|
+
this.status = status;
|
|
21456
|
+
}
|
|
21457
|
+
}
|
|
21140
21458
|
var clientCredentialsLogin = async ({
|
|
21141
21459
|
clientId,
|
|
21142
21460
|
clientSecret,
|
|
@@ -21184,9 +21502,9 @@ Troubleshooting:` + `
|
|
|
21184
21502
|
• The requested scopes may not be available for your account` + `
|
|
21185
21503
|
• Try using default scopes or contact your UiPath administrator`;
|
|
21186
21504
|
}
|
|
21187
|
-
throw new
|
|
21505
|
+
throw new ClientCredentialsAuthenticationError(`Client Credentials authentication failed (${tokenResponse.status})
|
|
21188
21506
|
` + `Error: ${errorType}
|
|
21189
|
-
` + `Details: ${errorDesc}${troubleshooting}
|
|
21507
|
+
` + `Details: ${errorDesc}${troubleshooting}`, tokenResponse.status);
|
|
21190
21508
|
}
|
|
21191
21509
|
return {
|
|
21192
21510
|
UIPATH_ACCESS_TOKEN: tokenData.access_token,
|
|
@@ -21216,6 +21534,49 @@ var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId)
|
|
|
21216
21534
|
};
|
|
21217
21535
|
|
|
21218
21536
|
// src/selectTenant.ts
|
|
21537
|
+
var TENANT_SELECTION_REQUIRED_CODE = "TENANT_SELECTION_REQUIRED";
|
|
21538
|
+
var INVALID_TENANT_CODE = "INVALID_TENANT";
|
|
21539
|
+
|
|
21540
|
+
class TenantSelectionError extends Error {
|
|
21541
|
+
availableTenants;
|
|
21542
|
+
organizationName;
|
|
21543
|
+
constructor(message, organizationName, availableTenants) {
|
|
21544
|
+
super(message);
|
|
21545
|
+
this.organizationName = organizationName;
|
|
21546
|
+
this.availableTenants = availableTenants;
|
|
21547
|
+
}
|
|
21548
|
+
}
|
|
21549
|
+
|
|
21550
|
+
class TenantSelectionRequiredError extends TenantSelectionError {
|
|
21551
|
+
code = TENANT_SELECTION_REQUIRED_CODE;
|
|
21552
|
+
constructor(organizationName, availableTenants) {
|
|
21553
|
+
super(`Multiple tenants available in organization "${organizationName}"; none selected.`, organizationName, availableTenants);
|
|
21554
|
+
this.name = "TenantSelectionRequiredError";
|
|
21555
|
+
}
|
|
21556
|
+
}
|
|
21557
|
+
|
|
21558
|
+
class InvalidTenantError extends TenantSelectionError {
|
|
21559
|
+
code = INVALID_TENANT_CODE;
|
|
21560
|
+
requestedTenant;
|
|
21561
|
+
constructor(requestedTenant, organizationName, availableTenants) {
|
|
21562
|
+
super(`Invalid tenant requested: "${requestedTenant}"
|
|
21563
|
+
` + `Organization: ${organizationName}
|
|
21564
|
+
` + `Available tenants: ${availableTenants.join(", ")}`, organizationName, availableTenants);
|
|
21565
|
+
this.name = "InvalidTenantError";
|
|
21566
|
+
this.requestedTenant = requestedTenant;
|
|
21567
|
+
}
|
|
21568
|
+
}
|
|
21569
|
+
var TENANT_SELECTION_CODES = new Set([
|
|
21570
|
+
TENANT_SELECTION_REQUIRED_CODE,
|
|
21571
|
+
INVALID_TENANT_CODE
|
|
21572
|
+
]);
|
|
21573
|
+
function isTenantSelectionError(error) {
|
|
21574
|
+
if (!(error instanceof Object) || !("code" in error)) {
|
|
21575
|
+
return false;
|
|
21576
|
+
}
|
|
21577
|
+
const code = error.code;
|
|
21578
|
+
return typeof code === "string" && TENANT_SELECTION_CODES.has(code) && Array.isArray(error.availableTenants);
|
|
21579
|
+
}
|
|
21219
21580
|
var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTenantName, interactive, deps = {}) => {
|
|
21220
21581
|
const {
|
|
21221
21582
|
fetchTenantsAndOrgs = fetchTenantsAndOrganizations,
|
|
@@ -21228,13 +21589,10 @@ var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTe
|
|
|
21228
21589
|
}
|
|
21229
21590
|
const tenantNames = tenants.map((tenant) => tenant.name);
|
|
21230
21591
|
let selectedIndex;
|
|
21231
|
-
if (targetTenantName) {
|
|
21592
|
+
if (targetTenantName !== undefined) {
|
|
21232
21593
|
selectedIndex = tenants.findIndex((tenant) => tenant.name === targetTenantName);
|
|
21233
21594
|
if (selectedIndex === -1) {
|
|
21234
|
-
|
|
21235
|
-
throw new Error(`Invalid tenant requested: "${targetTenantName}"
|
|
21236
|
-
` + `Organization: ${organization.name}
|
|
21237
|
-
` + `Available tenants: ${availableTenants}`);
|
|
21595
|
+
throw new InvalidTenantError(targetTenantName, organization.name, tenantNames);
|
|
21238
21596
|
}
|
|
21239
21597
|
} else if (tenants.length === 1) {
|
|
21240
21598
|
selectedIndex = 0;
|
|
@@ -21244,7 +21602,7 @@ var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTe
|
|
|
21244
21602
|
}
|
|
21245
21603
|
selectedIndex = await selectFromList(tenantNames, "Select a tenant:");
|
|
21246
21604
|
} else {
|
|
21247
|
-
|
|
21605
|
+
throw new TenantSelectionRequiredError(organization.name, tenantNames);
|
|
21248
21606
|
}
|
|
21249
21607
|
const selectedTenant = tenants[selectedIndex];
|
|
21250
21608
|
return [selectedTenant.name, selectedTenant.id, organization.name];
|
|
@@ -21273,7 +21631,8 @@ var interactiveLoginWithDeps = async (options, deps) => {
|
|
|
21273
21631
|
organization,
|
|
21274
21632
|
interactive,
|
|
21275
21633
|
onEvent,
|
|
21276
|
-
timeoutMs
|
|
21634
|
+
timeoutMs,
|
|
21635
|
+
noBrowser
|
|
21277
21636
|
} = options;
|
|
21278
21637
|
const emit = (event) => {
|
|
21279
21638
|
if (!onEvent)
|
|
@@ -21282,12 +21641,22 @@ var interactiveLoginWithDeps = async (options, deps) => {
|
|
|
21282
21641
|
onEvent(event);
|
|
21283
21642
|
} catch {}
|
|
21284
21643
|
};
|
|
21644
|
+
const deliverAuthUrl = (url) => {
|
|
21645
|
+
try {
|
|
21646
|
+
onEvent?.({ type: "auth-url", url });
|
|
21647
|
+
} catch (error) {
|
|
21648
|
+
throw new Error("Failed to deliver the authorize URL to the onEvent subscriber; " + "cannot continue headless login.", { cause: error });
|
|
21649
|
+
}
|
|
21650
|
+
};
|
|
21285
21651
|
const config = await resolveConfig({
|
|
21286
21652
|
customAuthority: authority,
|
|
21287
21653
|
customClientId: clientId,
|
|
21288
21654
|
customClientSecret: clientSecret
|
|
21289
21655
|
});
|
|
21290
21656
|
const { clientSecret: resolvedSecret } = config;
|
|
21657
|
+
if (noBrowser && !resolvedSecret && !onEvent) {
|
|
21658
|
+
throw new Error("noBrowser login requires an onEvent subscriber to receive the " + "auth-url event — the authorize URL is delivered through it.");
|
|
21659
|
+
}
|
|
21291
21660
|
const authPromise = resolvedSecret ? (async () => {
|
|
21292
21661
|
return await clientCredentials({
|
|
21293
21662
|
clientId: config.clientId,
|
|
@@ -21302,7 +21671,9 @@ var interactiveLoginWithDeps = async (options, deps) => {
|
|
|
21302
21671
|
clientSecret,
|
|
21303
21672
|
scope,
|
|
21304
21673
|
organization,
|
|
21305
|
-
timeoutMs
|
|
21674
|
+
timeoutMs,
|
|
21675
|
+
noBrowser,
|
|
21676
|
+
onAuthUrl: noBrowser ? (url) => deliverAuthUrl(url) : undefined
|
|
21306
21677
|
});
|
|
21307
21678
|
return {
|
|
21308
21679
|
UIPATH_ACCESS_TOKEN: authTokens.accessToken,
|
|
@@ -21333,10 +21704,12 @@ var interactiveLoginWithDeps = async (options, deps) => {
|
|
|
21333
21704
|
credentials.UIPATH_TENANT_NAME = tenantName;
|
|
21334
21705
|
credentials.UIPATH_TENANT_ID = tenantId;
|
|
21335
21706
|
} catch (error) {
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
|
|
21339
|
-
|
|
21707
|
+
if (!isTenantSelectionError(error)) {
|
|
21708
|
+
emit({
|
|
21709
|
+
type: "tenant-fetch-failed",
|
|
21710
|
+
message: error instanceof Error ? error.message : String(error)
|
|
21711
|
+
});
|
|
21712
|
+
}
|
|
21340
21713
|
throw error;
|
|
21341
21714
|
}
|
|
21342
21715
|
const fs7 = getFs();
|
|
@@ -21401,10 +21774,12 @@ init_src();
|
|
|
21401
21774
|
async function logoutWithDeps(options, deps = {}) {
|
|
21402
21775
|
const {
|
|
21403
21776
|
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
21404
|
-
getFileSystem: getFs = getFileSystem
|
|
21777
|
+
getFileSystem: getFs = getFileSystem,
|
|
21778
|
+
clearBreaker = clearRefreshBreaker,
|
|
21779
|
+
getActiveProfileFilePath = getActiveAuthProfileFilePath
|
|
21405
21780
|
} = deps;
|
|
21406
21781
|
const fs7 = getFs();
|
|
21407
|
-
const { absolutePath } = await resolveEnvFilePath(options.file);
|
|
21782
|
+
const { absolutePath } = await resolveEnvFilePath(options.file ?? getActiveProfileFilePath());
|
|
21408
21783
|
if (absolutePath && await fs7.exists(absolutePath)) {
|
|
21409
21784
|
let release;
|
|
21410
21785
|
try {
|
|
@@ -21416,6 +21791,7 @@ async function logoutWithDeps(options, deps = {}) {
|
|
|
21416
21791
|
}
|
|
21417
21792
|
try {
|
|
21418
21793
|
await fs7.rm(absolutePath);
|
|
21794
|
+
await clearBreaker(absolutePath);
|
|
21419
21795
|
} finally {
|
|
21420
21796
|
if (release) {
|
|
21421
21797
|
await release().catch(() => {});
|
|
@@ -21446,7 +21822,9 @@ var authenticate = async ({
|
|
|
21446
21822
|
redirectUri,
|
|
21447
21823
|
scope,
|
|
21448
21824
|
organization,
|
|
21449
|
-
timeoutMs
|
|
21825
|
+
timeoutMs,
|
|
21826
|
+
noBrowser,
|
|
21827
|
+
onAuthUrl
|
|
21450
21828
|
}) => {
|
|
21451
21829
|
const config = await resolveConfigAsync({
|
|
21452
21830
|
customAuthority: baseUrl,
|
|
@@ -21496,7 +21874,7 @@ var authenticate = async ({
|
|
|
21496
21874
|
const { NodeAuthStrategy: NodeAuthStrategy2 } = await Promise.resolve().then(() => (init_node_strategy(), exports_node_strategy));
|
|
21497
21875
|
strategy = new NodeAuthStrategy2;
|
|
21498
21876
|
}
|
|
21499
|
-
const code = await strategy.execute(authUrl, effectiveRedirectUriUrl, state, { timeoutMs });
|
|
21877
|
+
const code = await strategy.execute(authUrl, effectiveRedirectUriUrl, state, { timeoutMs, noBrowser, onAuthUrl });
|
|
21500
21878
|
return await exchangeCodeForTokens({
|
|
21501
21879
|
code,
|
|
21502
21880
|
codeVerifier: code_verifier,
|
|
@@ -21508,17 +21886,25 @@ var authenticate = async ({
|
|
|
21508
21886
|
};
|
|
21509
21887
|
export {
|
|
21510
21888
|
setAuthFileConfig,
|
|
21889
|
+
setActiveAuthProfile,
|
|
21511
21890
|
selectTenantWithDeps,
|
|
21891
|
+
saveRefreshBreaker,
|
|
21512
21892
|
saveEnvFileAsync,
|
|
21893
|
+
runWithAuthProfile,
|
|
21513
21894
|
resolveEnvFilePathAsync,
|
|
21514
21895
|
resolveEnvFileLocationAsync,
|
|
21896
|
+
resolveAuthProfileFilePath,
|
|
21897
|
+
refreshTokenFingerprint,
|
|
21515
21898
|
refreshAccessToken,
|
|
21516
21899
|
readAuthFromEnv,
|
|
21517
21900
|
parseJWT,
|
|
21901
|
+
normalizeAuthProfileName,
|
|
21518
21902
|
logoutWithDeps,
|
|
21519
21903
|
logout,
|
|
21904
|
+
loadRefreshBreaker,
|
|
21520
21905
|
loadEnvFileAsync,
|
|
21521
21906
|
isTokenRefreshOAuthFailure,
|
|
21907
|
+
isTenantSelectionError,
|
|
21522
21908
|
isRobotAuthEnforced,
|
|
21523
21909
|
isNode,
|
|
21524
21910
|
isEnvAuthEnabled,
|
|
@@ -21529,17 +21915,31 @@ export {
|
|
|
21529
21915
|
getLoginStatusAsync,
|
|
21530
21916
|
getAuthEnv,
|
|
21531
21917
|
getAuthContext,
|
|
21918
|
+
getActiveAuthProfileFilePath,
|
|
21919
|
+
getActiveAuthProfile,
|
|
21532
21920
|
fetchTenantsAndOrganizations,
|
|
21533
21921
|
clientCredentialsLogin,
|
|
21922
|
+
clearRefreshBreaker,
|
|
21923
|
+
clearActiveAuthProfile,
|
|
21534
21924
|
authenticate,
|
|
21535
21925
|
TokenRefreshOAuthError,
|
|
21926
|
+
TenantSelectionRequiredError,
|
|
21927
|
+
TenantSelectionError,
|
|
21928
|
+
TENANT_SELECTION_REQUIRED_CODE,
|
|
21536
21929
|
LoginStatusSource,
|
|
21930
|
+
InvalidTenantError,
|
|
21537
21931
|
InvalidBaseUrlError,
|
|
21932
|
+
INVALID_TENANT_CODE,
|
|
21538
21933
|
EnvAuthConfigError,
|
|
21539
21934
|
ENV_AUTH_VARS,
|
|
21540
21935
|
ENV_AUTH_ENABLE_VAR,
|
|
21541
21936
|
ENFORCE_ROBOT_AUTH_VAR,
|
|
21542
21937
|
DEFAULT_ENV_FILENAME,
|
|
21938
|
+
DEFAULT_AUTH_PROFILE,
|
|
21543
21939
|
DEFAULT_AUTH_FILENAME,
|
|
21940
|
+
ClientCredentialsAuthenticationError,
|
|
21941
|
+
AuthProfileValidationError,
|
|
21544
21942
|
AUTH_TIMEOUT_ERROR_CODE
|
|
21545
21943
|
};
|
|
21944
|
+
|
|
21945
|
+
//# debugId=B1F2D5989CE27CF464756E2164756E21
|