@uipath/auth 1.195.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/index.js CHANGED
@@ -19153,16 +19153,33 @@ var require_dist = __commonJS((exports) => {
19153
19153
  });
19154
19154
 
19155
19155
  // src/getBaseHtml.ts
19156
- var getBaseHtml = ({ title, message, type }) => {
19156
+ var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
19157
+ switch (char) {
19158
+ case "&":
19159
+ return "&amp;";
19160
+ case "<":
19161
+ return "&lt;";
19162
+ case ">":
19163
+ return "&gt;";
19164
+ case '"':
19165
+ return "&quot;";
19166
+ case "'":
19167
+ return "&#39;";
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>${title} - UiPath CLI</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>${title}</h1>
19292
- <p>${message}</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>
@@ -19405,7 +19422,7 @@ __export(exports_browser_strategy, {
19405
19422
  });
19406
19423
 
19407
19424
  class BrowserAuthStrategy {
19408
- async execute(url, _redirectUri, expectedState) {
19425
+ async execute(url, _redirectUri, expectedState, _opts) {
19409
19426
  const global2 = getGlobalThis();
19410
19427
  if (!global2?.window) {
19411
19428
  throw new Error("Browser environment required for authentication");
@@ -19480,10 +19497,11 @@ __export(exports_node_strategy, {
19480
19497
  });
19481
19498
 
19482
19499
  class NodeAuthStrategy {
19483
- async execute(url, redirectUri, expectedState) {
19500
+ async execute(url, redirectUri, expectedState, opts) {
19484
19501
  const fs7 = getFileSystem();
19485
19502
  const callbackUrl = await startServer({
19486
19503
  redirectUri,
19504
+ timeoutMs: opts?.timeoutMs,
19487
19505
  onListening: async () => {
19488
19506
  let safeUrl = "";
19489
19507
  for (const ch of url) {
@@ -19491,6 +19509,13 @@ class NodeAuthStrategy {
19491
19509
  if (c > 31 && (c < 128 || c > 159))
19492
19510
  safeUrl += ch;
19493
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
+ }
19494
19519
  const [openError] = await catchError(fs7.utils.open(url));
19495
19520
  if (!openError)
19496
19521
  return;
@@ -19572,6 +19597,12 @@ var normalizeAndValidateBaseUrl = (rawUrl) => {
19572
19597
  }
19573
19598
  return url.pathname.length > 1 ? url.origin : baseUrl;
19574
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
+ };
19575
19606
  var resolveConfigAsync = async ({
19576
19607
  customAuthority,
19577
19608
  customClientId,
@@ -19602,7 +19633,7 @@ var resolveConfigAsync = async ({
19602
19633
  clientSecret = fileAuth.clientSecret;
19603
19634
  }
19604
19635
  const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && Boolean(clientSecret);
19605
- const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : isExternalAppAuth ? [] : DEFAULT_SCOPES;
19636
+ const scopes = resolveScopes(isExternalAppAuth, customScopes, fileAuth.scopes);
19606
19637
  return {
19607
19638
  clientId,
19608
19639
  clientSecret,
@@ -20249,6 +20280,105 @@ var exchangeCodeForTokens = async ({
20249
20280
  };
20250
20281
  // src/loginStatus.ts
20251
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
+ }
20252
20382
  // src/utils/jwt.ts
20253
20383
  class InvalidIssuerError extends Error {
20254
20384
  expected;
@@ -20377,23 +20507,74 @@ var readAuthFromEnv = () => {
20377
20507
  organizationId,
20378
20508
  tenantName,
20379
20509
  tenantId,
20380
- expiration
20510
+ expiration,
20511
+ source: "env" /* Env */
20381
20512
  };
20382
20513
  };
20383
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
+
20384
20574
  // src/robotClientFallback.ts
20385
20575
  init_src();
20386
20576
  var DEFAULT_TIMEOUT_MS = 1000;
20387
20577
  var CLOSE_TIMEOUT_MS = 500;
20388
- var NOTICE_SENTINEL = Symbol.for("@uipath/auth/robotFallbackNoticePrinted");
20389
- var printNoticeOnce = () => {
20390
- const slot = globalThis;
20391
- if (slot[NOTICE_SENTINEL])
20392
- return;
20393
- slot[NOTICE_SENTINEL] = true;
20394
- catchError(() => process.stderr.write(`Using UiPath Robot credentials. Run 'uip login' for a dedicated session.
20395
- `));
20396
- };
20397
20578
  var ROBOT_USER_SERVICES_PIPE = "UiPathUserServices";
20398
20579
  var ROBOT_USER_SERVICES_ALTERNATE_PIPE = `${ROBOT_USER_SERVICES_PIPE}Alternate`;
20399
20580
  var PIPE_NAME_MAX_LENGTH = 103;
@@ -20438,11 +20619,11 @@ var parseResourceUrl = (url) => {
20438
20619
  if (error || !parsed)
20439
20620
  return;
20440
20621
  const segments = parsed.pathname.split("/").filter(Boolean);
20441
- const organizationName = segments[0];
20442
- const tenantName = segments[1];
20443
- if (!organizationName || !tenantName)
20444
- return;
20445
- return { baseUrl: parsed.origin, organizationName, tenantName };
20622
+ return {
20623
+ baseUrl: parsed.origin,
20624
+ organizationName: segments[0],
20625
+ tenantName: segments[1]
20626
+ };
20446
20627
  };
20447
20628
  var defaultLoadModule = async () => {
20448
20629
  const [error, mod] = await catchError(() => Promise.resolve().then(() => __toESM(require_dist(), 1)));
@@ -20493,6 +20674,7 @@ var tryRobotClientFallback = async (options = {}) => {
20493
20674
  }
20494
20675
  let organizationIdFromToken;
20495
20676
  let tenantIdFromToken;
20677
+ let issuerFromToken;
20496
20678
  const [jwtError, claims] = catchError(() => parseJWT(accessToken));
20497
20679
  if (!jwtError && claims) {
20498
20680
  const rawOrgId = claims.prtId ?? claims.organizationId ?? claims.prt_id;
@@ -20503,15 +20685,19 @@ var tryRobotClientFallback = async (options = {}) => {
20503
20685
  if (typeof tenantClaim === "string" && tenantClaim.length > 0) {
20504
20686
  tenantIdFromToken = tenantClaim;
20505
20687
  }
20688
+ const issClaim = claims.iss;
20689
+ if (typeof issClaim === "string" && issClaim.length > 0) {
20690
+ issuerFromToken = issClaim;
20691
+ }
20506
20692
  }
20507
- printNoticeOnce();
20508
20693
  return {
20509
20694
  accessToken,
20510
20695
  baseUrl: parsedUrl.baseUrl,
20511
20696
  organizationName: parsedUrl.organizationName,
20512
20697
  organizationId: organizationIdFromToken ?? parsedUrl.organizationName,
20513
20698
  tenantName: parsedUrl.tenantName,
20514
- tenantId: tenantIdFromToken
20699
+ tenantId: tenantIdFromToken,
20700
+ issuer: issuerFromToken
20515
20701
  };
20516
20702
  } catch {
20517
20703
  return;
@@ -20614,7 +20800,7 @@ var probeAsync = async (fs7, candidate) => {
20614
20800
  };
20615
20801
  }
20616
20802
  };
20617
- var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME) => {
20803
+ var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME, opts) => {
20618
20804
  const fs7 = getFileSystem();
20619
20805
  if (fs7.path.isAbsolute(envFilePath)) {
20620
20806
  const probe2 = await probeAsync(fs7, envFilePath);
@@ -20625,7 +20811,7 @@ var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME) =>
20625
20811
  ...probe2.unusable ? { unusable: probe2.unusable } : {}
20626
20812
  };
20627
20813
  }
20628
- const cwd = fs7.env.cwd();
20814
+ const cwd = opts?.cwd ?? fs7.env.cwd();
20629
20815
  let searchDir = cwd;
20630
20816
  while (true) {
20631
20817
  const candidate = fs7.path.join(searchDir, envFilePath);
@@ -20655,8 +20841,8 @@ var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME) =>
20655
20841
  ...probe.unusable ? { unusable: probe.unusable } : {}
20656
20842
  };
20657
20843
  };
20658
- var resolveEnvFilePathAsync = async (envFilePath = DEFAULT_ENV_FILENAME) => {
20659
- const location = await resolveEnvFileLocationAsync(envFilePath);
20844
+ var resolveEnvFilePathAsync = async (envFilePath = DEFAULT_ENV_FILENAME, opts) => {
20845
+ const location = await resolveEnvFileLocationAsync(envFilePath, opts);
20660
20846
  if (location.exists) {
20661
20847
  return { absolutePath: location.absolutePath };
20662
20848
  }
@@ -20733,19 +20919,329 @@ var LoginStatusSource;
20733
20919
  ((LoginStatusSource2) => {
20734
20920
  LoginStatusSource2["File"] = "file";
20735
20921
  LoginStatusSource2["Robot"] = "robot";
20922
+ LoginStatusSource2["Env"] = "env";
20736
20923
  })(LoginStatusSource ||= {});
20737
- function normalizeTokenRefreshFailure() {
20738
- return "stored refresh token is invalid or expired";
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);
20739
21013
  }
20740
- function normalizeTokenRefreshUnavailableFailure() {
20741
- return "token refresh failed before authentication completed";
21014
+ async function resolveBorrowedRobotStatus(robotFallback) {
21015
+ const robotCreds = await robotFallback();
21016
+ return robotCreds ? buildRobotStatus(robotCreds) : { loginStatus: "Not logged in" };
20742
21017
  }
20743
- function errorMessage(error) {
20744
- return error instanceof Error ? error.message : String(error);
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
+ }
20745
21051
  }
20746
21052
  function computeExpirationThreshold(ensureTokenValidityMinutes) {
20747
21053
  return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
20748
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
+ }
20749
21245
  async function runRefreshLocked(inputs) {
20750
21246
  const {
20751
21247
  absolutePath,
@@ -20755,7 +21251,10 @@ async function runRefreshLocked(inputs) {
20755
21251
  loadEnvFile,
20756
21252
  saveEnvFile,
20757
21253
  refreshFn,
20758
- resolveConfig
21254
+ resolveConfig,
21255
+ loadBreaker,
21256
+ saveBreaker,
21257
+ clearBreaker
20759
21258
  } = inputs;
20760
21259
  const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
20761
21260
  let fresh;
@@ -20778,6 +21277,7 @@ async function runRefreshLocked(inputs) {
20778
21277
  const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
20779
21278
  const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
20780
21279
  if (freshAccess && freshExp && freshExp > expirationThreshold) {
21280
+ await clearBreaker(absolutePath);
20781
21281
  return {
20782
21282
  kind: "ok",
20783
21283
  accessToken: freshAccess,
@@ -20801,8 +21301,21 @@ async function runRefreshLocked(inputs) {
20801
21301
  refreshedRefresh = refreshed.refreshToken;
20802
21302
  } catch (error) {
20803
21303
  const isOAuthFailure = isTokenRefreshOAuthFailure(error);
20804
- 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.";
20805
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
+ }
20806
21319
  return {
20807
21320
  kind: "fail",
20808
21321
  status: {
@@ -20831,6 +21344,7 @@ async function runRefreshLocked(inputs) {
20831
21344
  }
20832
21345
  };
20833
21346
  }
21347
+ await clearBreaker(absolutePath);
20834
21348
  try {
20835
21349
  await saveEnvFile({
20836
21350
  envPath: absolutePath,
@@ -20863,215 +21377,21 @@ async function runRefreshLocked(inputs) {
20863
21377
  };
20864
21378
  }
20865
21379
  }
20866
- var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
20867
- const {
20868
- resolveEnvFilePath = resolveEnvFilePathAsync,
20869
- loadEnvFile = loadEnvFileAsync,
20870
- saveEnvFile = saveEnvFileAsync,
20871
- getFs = getFileSystem,
20872
- refreshToken: refreshTokenFn = refreshAccessToken,
20873
- resolveConfig = resolveConfigAsync,
20874
- robotFallback = tryRobotClientFallback
20875
- } = deps;
20876
- if (isRobotAuthEnforced()) {
20877
- if (isEnvAuthEnabled()) {
20878
- throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
20879
- }
20880
- const robotCreds = await robotFallback({ force: true });
20881
- if (!robotCreds) {
20882
- return {
20883
- loginStatus: "Not logged in",
20884
- 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.`
20885
- };
20886
- }
20887
- const expiration2 = getTokenExpiration(robotCreds.accessToken);
20888
- return {
20889
- loginStatus: "Logged in",
20890
- accessToken: robotCreds.accessToken,
20891
- baseUrl: robotCreds.baseUrl,
20892
- organizationName: robotCreds.organizationName,
20893
- organizationId: robotCreds.organizationId,
20894
- tenantName: robotCreds.tenantName,
20895
- tenantId: robotCreds.tenantId,
20896
- expiration: expiration2,
20897
- source: "robot" /* Robot */
20898
- };
20899
- }
20900
- if (isEnvAuthEnabled()) {
20901
- return readAuthFromEnv();
20902
- }
20903
- const { envFilePath = DEFAULT_ENV_FILENAME, ensureTokenValidityMinutes } = options;
20904
- const { absolutePath } = await resolveEnvFilePath(envFilePath);
20905
- if (absolutePath === undefined) {
20906
- const robotCreds = await robotFallback();
20907
- if (robotCreds) {
20908
- const expiration2 = getTokenExpiration(robotCreds.accessToken);
20909
- const status = {
20910
- loginStatus: "Logged in",
20911
- accessToken: robotCreds.accessToken,
20912
- baseUrl: robotCreds.baseUrl,
20913
- organizationName: robotCreds.organizationName,
20914
- organizationId: robotCreds.organizationId,
20915
- tenantName: robotCreds.tenantName,
20916
- tenantId: robotCreds.tenantId,
20917
- expiration: expiration2,
20918
- source: "robot" /* Robot */
20919
- };
20920
- return status;
20921
- }
20922
- return { loginStatus: "Not logged in" };
20923
- }
20924
- let credentials;
20925
- try {
20926
- credentials = await loadEnvFile({ envPath: absolutePath });
20927
- } catch (error) {
20928
- if (isFileNotFoundError(error)) {
20929
- return { loginStatus: "Not logged in" };
20930
- }
20931
- throw error;
20932
- }
20933
- if (!credentials.UIPATH_ACCESS_TOKEN) {
20934
- return { loginStatus: "Not logged in" };
20935
- }
20936
- let accessToken = credentials.UIPATH_ACCESS_TOKEN;
20937
- let refreshToken = credentials.UIPATH_REFRESH_TOKEN;
20938
- let expiration = getTokenExpiration(accessToken);
20939
- let persistenceWarning;
20940
- let lockReleaseFailed = false;
20941
- let tokenRefresh;
20942
- const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
20943
- const tryGlobalCredsHint = async () => {
20944
- const fs7 = getFs();
20945
- const globalPath = fs7.path.join(fs7.env.homedir(), envFilePath);
20946
- if (absolutePath === globalPath)
20947
- return;
20948
- if (!await fs7.exists(globalPath))
20949
- return;
20950
- try {
20951
- const globalCreds = await loadEnvFile({ envPath: globalPath });
20952
- if (!globalCreds.UIPATH_ACCESS_TOKEN)
20953
- return;
20954
- const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
20955
- if (globalExp && globalExp <= new Date)
20956
- return;
20957
- 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.`;
20958
- } catch {
20959
- return;
20960
- }
20961
- };
20962
- if (expiration && expiration <= outerThreshold && refreshToken) {
20963
- let release;
20964
- try {
20965
- release = await getFs().acquireLock(absolutePath);
20966
- } catch (error) {
20967
- const msg = errorMessage(error);
20968
- const globalHint = await tryGlobalCredsHint();
20969
- if (globalHint) {
20970
- return {
20971
- loginStatus: "Expired",
20972
- accessToken,
20973
- refreshToken,
20974
- baseUrl: credentials.UIPATH_URL,
20975
- organizationName: credentials.UIPATH_ORGANIZATION_NAME,
20976
- organizationId: credentials.UIPATH_ORGANIZATION_ID,
20977
- tenantName: credentials.UIPATH_TENANT_NAME,
20978
- tenantId: credentials.UIPATH_TENANT_ID,
20979
- expiration,
20980
- source: "file" /* File */,
20981
- hint: globalHint,
20982
- tokenRefresh: {
20983
- attempted: false,
20984
- success: false,
20985
- errorMessage: `lock acquisition failed: ${msg}`
20986
- }
20987
- };
20988
- }
20989
- return {
20990
- loginStatus: "Refresh Failed",
20991
- 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.",
20992
- tokenRefresh: {
20993
- attempted: false,
20994
- success: false,
20995
- errorMessage: `lock acquisition failed: ${msg}`
20996
- }
20997
- };
20998
- }
20999
- let lockedFailure;
21000
- try {
21001
- const outcome = await runRefreshLocked({
21002
- absolutePath,
21003
- refreshToken,
21004
- customAuthority: credentials.UIPATH_URL,
21005
- ensureTokenValidityMinutes,
21006
- loadEnvFile,
21007
- saveEnvFile,
21008
- refreshFn: refreshTokenFn,
21009
- resolveConfig
21010
- });
21011
- if (outcome.kind === "fail") {
21012
- lockedFailure = outcome.status;
21013
- } else {
21014
- accessToken = outcome.accessToken;
21015
- refreshToken = outcome.refreshToken;
21016
- expiration = outcome.expiration;
21017
- tokenRefresh = outcome.tokenRefresh;
21018
- if (outcome.persistenceWarning) {
21019
- persistenceWarning = outcome.persistenceWarning;
21020
- }
21021
- }
21022
- } finally {
21023
- try {
21024
- await release();
21025
- } catch {
21026
- lockReleaseFailed = true;
21027
- }
21028
- }
21029
- if (lockedFailure) {
21030
- const globalHint = await tryGlobalCredsHint();
21031
- const base = globalHint ? {
21032
- ...lockedFailure,
21033
- loginStatus: "Expired",
21034
- hint: globalHint
21035
- } : lockedFailure;
21036
- return lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base;
21037
- }
21038
- }
21039
- const result = {
21040
- loginStatus: expiration && expiration <= new Date ? "Expired" : "Logged in",
21041
- accessToken,
21042
- refreshToken,
21043
- baseUrl: credentials.UIPATH_URL,
21044
- organizationName: credentials.UIPATH_ORGANIZATION_NAME,
21045
- organizationId: credentials.UIPATH_ORGANIZATION_ID,
21046
- tenantName: credentials.UIPATH_TENANT_NAME,
21047
- tenantId: credentials.UIPATH_TENANT_ID,
21048
- expiration,
21049
- source: "file" /* File */,
21050
- ...persistenceWarning ? { hint: persistenceWarning, persistenceFailed: true } : {},
21051
- ...lockReleaseFailed ? { lockReleaseFailed: true } : {},
21052
- ...tokenRefresh ? { tokenRefresh } : {}
21053
- };
21054
- if (result.loginStatus === "Expired") {
21055
- const globalHint = await tryGlobalCredsHint();
21056
- if (globalHint) {
21057
- result.hint = globalHint;
21058
- }
21059
- }
21060
- return result;
21061
- };
21062
- var isFileNotFoundError = (error) => {
21063
- if (!(error instanceof Object))
21064
- return false;
21065
- return error.code === "ENOENT";
21066
- };
21067
- var getLoginStatusAsync = async (options = {}) => {
21068
- return getLoginStatusWithDeps(options);
21069
- };
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
+ }
21070
21389
 
21071
21390
  // src/authContext.ts
21072
21391
  var getAuthContext = async (options = {}) => {
21073
21392
  const status = await getLoginStatusAsync({
21074
- ensureTokenValidityMinutes: options.ensureTokenValidityMinutes
21393
+ ensureTokenValidityMinutes: options.ensureTokenValidityMinutes,
21394
+ envFilePath: options.envFilePath
21075
21395
  });
21076
21396
  if (status.loginStatus !== "Logged in" || !status.baseUrl || !status.accessToken) {
21077
21397
  throw new Error(status.hint ? `Not logged in. ${status.hint}` : "Not logged in. Run 'uip login' first.");
@@ -21098,7 +21418,43 @@ var getAuthContext = async (options = {}) => {
21098
21418
  tenantName
21099
21419
  };
21100
21420
  };
21421
+ var getAuthEnv = async (options = {}) => {
21422
+ const authEnv = {};
21423
+ let status;
21424
+ try {
21425
+ status = await getLoginStatusAsync(options);
21426
+ } catch {
21427
+ return { authEnv };
21428
+ }
21429
+ if (status.loginStatus === "Logged in" && status.accessToken) {
21430
+ authEnv.UIPATH_ACCESS_TOKEN = status.accessToken;
21431
+ if (status.baseUrl) {
21432
+ const org = status.organizationName || status.organizationId;
21433
+ const tenant = status.tenantName || status.tenantId;
21434
+ if (org && tenant) {
21435
+ authEnv.UIPATH_URL = `${status.baseUrl.replace(/\/+$/, "")}/${org}/${tenant}`;
21436
+ }
21437
+ }
21438
+ if (status.organizationId)
21439
+ authEnv.UIPATH_ORGANIZATION_ID = status.organizationId;
21440
+ if (status.organizationName)
21441
+ authEnv.UIPATH_ORGANIZATION_NAME = status.organizationName;
21442
+ if (status.tenantId)
21443
+ authEnv.UIPATH_TENANT_ID = status.tenantId;
21444
+ if (status.tenantName)
21445
+ authEnv.UIPATH_TENANT_NAME = status.tenantName;
21446
+ }
21447
+ return { authEnv, loginStatus: status };
21448
+ };
21101
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
+ }
21102
21458
  var clientCredentialsLogin = async ({
21103
21459
  clientId,
21104
21460
  clientSecret,
@@ -21146,9 +21502,9 @@ Troubleshooting:` + `
21146
21502
  • The requested scopes may not be available for your account` + `
21147
21503
  • Try using default scopes or contact your UiPath administrator`;
21148
21504
  }
21149
- throw new Error(`Client Credentials authentication failed (${tokenResponse.status})
21505
+ throw new ClientCredentialsAuthenticationError(`Client Credentials authentication failed (${tokenResponse.status})
21150
21506
  ` + `Error: ${errorType}
21151
- ` + `Details: ${errorDesc}${troubleshooting}`);
21507
+ ` + `Details: ${errorDesc}${troubleshooting}`, tokenResponse.status);
21152
21508
  }
21153
21509
  return {
21154
21510
  UIPATH_ACCESS_TOKEN: tokenData.access_token,
@@ -21178,6 +21534,49 @@ var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId)
21178
21534
  };
21179
21535
 
21180
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
+ }
21181
21580
  var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTenantName, interactive, deps = {}) => {
21182
21581
  const {
21183
21582
  fetchTenantsAndOrgs = fetchTenantsAndOrganizations,
@@ -21190,13 +21589,10 @@ var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTe
21190
21589
  }
21191
21590
  const tenantNames = tenants.map((tenant) => tenant.name);
21192
21591
  let selectedIndex;
21193
- if (targetTenantName) {
21592
+ if (targetTenantName !== undefined) {
21194
21593
  selectedIndex = tenants.findIndex((tenant) => tenant.name === targetTenantName);
21195
21594
  if (selectedIndex === -1) {
21196
- const availableTenants = tenants.map((t) => t.name).join(", ");
21197
- throw new Error(`Invalid tenant requested: "${targetTenantName}"
21198
- ` + `Organization: ${organization.name}
21199
- ` + `Available tenants: ${availableTenants}`);
21595
+ throw new InvalidTenantError(targetTenantName, organization.name, tenantNames);
21200
21596
  }
21201
21597
  } else if (tenants.length === 1) {
21202
21598
  selectedIndex = 0;
@@ -21206,7 +21602,7 @@ var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTe
21206
21602
  }
21207
21603
  selectedIndex = await selectFromList(tenantNames, "Select a tenant:");
21208
21604
  } else {
21209
- selectedIndex = 0;
21605
+ throw new TenantSelectionRequiredError(organization.name, tenantNames);
21210
21606
  }
21211
21607
  const selectedTenant = tenants[selectedIndex];
21212
21608
  return [selectedTenant.name, selectedTenant.id, organization.name];
@@ -21234,7 +21630,9 @@ var interactiveLoginWithDeps = async (options, deps) => {
21234
21630
  tenant,
21235
21631
  organization,
21236
21632
  interactive,
21237
- onEvent
21633
+ onEvent,
21634
+ timeoutMs,
21635
+ noBrowser
21238
21636
  } = options;
21239
21637
  const emit = (event) => {
21240
21638
  if (!onEvent)
@@ -21243,12 +21641,22 @@ var interactiveLoginWithDeps = async (options, deps) => {
21243
21641
  onEvent(event);
21244
21642
  } catch {}
21245
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
+ };
21246
21651
  const config = await resolveConfig({
21247
21652
  customAuthority: authority,
21248
21653
  customClientId: clientId,
21249
21654
  customClientSecret: clientSecret
21250
21655
  });
21251
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
+ }
21252
21660
  const authPromise = resolvedSecret ? (async () => {
21253
21661
  return await clientCredentials({
21254
21662
  clientId: config.clientId,
@@ -21262,7 +21670,10 @@ var interactiveLoginWithDeps = async (options, deps) => {
21262
21670
  clientId,
21263
21671
  clientSecret,
21264
21672
  scope,
21265
- organization
21673
+ organization,
21674
+ timeoutMs,
21675
+ noBrowser,
21676
+ onAuthUrl: noBrowser ? (url) => deliverAuthUrl(url) : undefined
21266
21677
  });
21267
21678
  return {
21268
21679
  UIPATH_ACCESS_TOKEN: authTokens.accessToken,
@@ -21293,10 +21704,12 @@ var interactiveLoginWithDeps = async (options, deps) => {
21293
21704
  credentials.UIPATH_TENANT_NAME = tenantName;
21294
21705
  credentials.UIPATH_TENANT_ID = tenantId;
21295
21706
  } catch (error) {
21296
- emit({
21297
- type: "tenant-fetch-failed",
21298
- message: error instanceof Error ? error.message : String(error)
21299
- });
21707
+ if (!isTenantSelectionError(error)) {
21708
+ emit({
21709
+ type: "tenant-fetch-failed",
21710
+ message: error instanceof Error ? error.message : String(error)
21711
+ });
21712
+ }
21300
21713
  throw error;
21301
21714
  }
21302
21715
  const fs7 = getFs();
@@ -21361,10 +21774,12 @@ init_src();
21361
21774
  async function logoutWithDeps(options, deps = {}) {
21362
21775
  const {
21363
21776
  resolveEnvFilePath = resolveEnvFilePathAsync,
21364
- getFileSystem: getFs = getFileSystem
21777
+ getFileSystem: getFs = getFileSystem,
21778
+ clearBreaker = clearRefreshBreaker,
21779
+ getActiveProfileFilePath = getActiveAuthProfileFilePath
21365
21780
  } = deps;
21366
21781
  const fs7 = getFs();
21367
- const { absolutePath } = await resolveEnvFilePath(options.file);
21782
+ const { absolutePath } = await resolveEnvFilePath(options.file ?? getActiveProfileFilePath());
21368
21783
  if (absolutePath && await fs7.exists(absolutePath)) {
21369
21784
  let release;
21370
21785
  try {
@@ -21376,6 +21791,7 @@ async function logoutWithDeps(options, deps = {}) {
21376
21791
  }
21377
21792
  try {
21378
21793
  await fs7.rm(absolutePath);
21794
+ await clearBreaker(absolutePath);
21379
21795
  } finally {
21380
21796
  if (release) {
21381
21797
  await release().catch(() => {});
@@ -21405,7 +21821,10 @@ var authenticate = async ({
21405
21821
  clientSecret,
21406
21822
  redirectUri,
21407
21823
  scope,
21408
- organization
21824
+ organization,
21825
+ timeoutMs,
21826
+ noBrowser,
21827
+ onAuthUrl
21409
21828
  }) => {
21410
21829
  const config = await resolveConfigAsync({
21411
21830
  customAuthority: baseUrl,
@@ -21455,7 +21874,7 @@ var authenticate = async ({
21455
21874
  const { NodeAuthStrategy: NodeAuthStrategy2 } = await Promise.resolve().then(() => (init_node_strategy(), exports_node_strategy));
21456
21875
  strategy = new NodeAuthStrategy2;
21457
21876
  }
21458
- const code = await strategy.execute(authUrl, effectiveRedirectUriUrl, state);
21877
+ const code = await strategy.execute(authUrl, effectiveRedirectUriUrl, state, { timeoutMs, noBrowser, onAuthUrl });
21459
21878
  return await exchangeCodeForTokens({
21460
21879
  code,
21461
21880
  codeVerifier: code_verifier,
@@ -21467,17 +21886,25 @@ var authenticate = async ({
21467
21886
  };
21468
21887
  export {
21469
21888
  setAuthFileConfig,
21889
+ setActiveAuthProfile,
21470
21890
  selectTenantWithDeps,
21891
+ saveRefreshBreaker,
21471
21892
  saveEnvFileAsync,
21893
+ runWithAuthProfile,
21472
21894
  resolveEnvFilePathAsync,
21473
21895
  resolveEnvFileLocationAsync,
21896
+ resolveAuthProfileFilePath,
21897
+ refreshTokenFingerprint,
21474
21898
  refreshAccessToken,
21475
21899
  readAuthFromEnv,
21476
21900
  parseJWT,
21901
+ normalizeAuthProfileName,
21477
21902
  logoutWithDeps,
21478
21903
  logout,
21904
+ loadRefreshBreaker,
21479
21905
  loadEnvFileAsync,
21480
21906
  isTokenRefreshOAuthFailure,
21907
+ isTenantSelectionError,
21481
21908
  isRobotAuthEnforced,
21482
21909
  isNode,
21483
21910
  isEnvAuthEnabled,
@@ -21486,18 +21913,33 @@ export {
21486
21913
  interactiveLogin,
21487
21914
  getLoginStatusWithDeps,
21488
21915
  getLoginStatusAsync,
21916
+ getAuthEnv,
21489
21917
  getAuthContext,
21918
+ getActiveAuthProfileFilePath,
21919
+ getActiveAuthProfile,
21490
21920
  fetchTenantsAndOrganizations,
21491
21921
  clientCredentialsLogin,
21922
+ clearRefreshBreaker,
21923
+ clearActiveAuthProfile,
21492
21924
  authenticate,
21493
21925
  TokenRefreshOAuthError,
21926
+ TenantSelectionRequiredError,
21927
+ TenantSelectionError,
21928
+ TENANT_SELECTION_REQUIRED_CODE,
21494
21929
  LoginStatusSource,
21930
+ InvalidTenantError,
21495
21931
  InvalidBaseUrlError,
21932
+ INVALID_TENANT_CODE,
21496
21933
  EnvAuthConfigError,
21497
21934
  ENV_AUTH_VARS,
21498
21935
  ENV_AUTH_ENABLE_VAR,
21499
21936
  ENFORCE_ROBOT_AUTH_VAR,
21500
21937
  DEFAULT_ENV_FILENAME,
21938
+ DEFAULT_AUTH_PROFILE,
21501
21939
  DEFAULT_AUTH_FILENAME,
21940
+ ClientCredentialsAuthenticationError,
21941
+ AuthProfileValidationError,
21502
21942
  AUTH_TIMEOUT_ERROR_CODE
21503
21943
  };
21944
+
21945
+ //# debugId=B1F2D5989CE27CF464756E2164756E21