@uipath/auth 1.201.0-preview.132 → 1.202.0-preview.134

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.
@@ -8,5 +8,6 @@ export declare const DEFAULT_BASE_URL = "https://cloud.uipath.com";
8
8
  export declare const DEFAULT_AUTH_TIMEOUT_MS: number;
9
9
  export declare const AUTH_TIMEOUT_ERROR_CODE = "EAUTHTIMEOUT";
10
10
  export declare const AUTH_CANCELLED_ERROR_CODE = "EAUTHCANCELLED";
11
+ export declare const AUTH_RECLAIMED_ERROR_CODE = "EAUTHRECLAIMED";
11
12
  /** Localhost OIDC redirect URI. */
12
13
  export declare const DEFAULT_REDIRECT_URI = "http://localhost:8104/oidc/login";
package/dist/envAuth.d.ts CHANGED
@@ -69,7 +69,13 @@ export declare const isRobotAuthEnforced: () => boolean;
69
69
  * is no refresh token in the env-var contract and the CLI has no way
70
70
  * to update a secret it did not mint.
71
71
  *
72
- * baseUrl is derived from the token's `iss` claim so the env-var set
73
- * can stay aligned with the GH #1034 spec (five vars, no URL).
72
+ * For a JWT, baseUrl is derived from the token's `iss` claim, keeping
73
+ * the env-var set aligned with the GH #1034 spec (five vars, no URL).
74
+ * For an opaque token — e.g. a Personal Access Token / reference token,
75
+ * which UiPath resource APIs accept directly as a bearer credential and
76
+ * resolve entirely server-side — there is no claim to read the URL from,
77
+ * so {@link OPAQUE_TOKEN_BASE_URL_VAR} is required instead, and the
78
+ * returned status carries no `expiration` or `identity` (both would
79
+ * require decoding claims the token doesn't have).
74
80
  */
75
81
  export declare const readAuthFromEnv: () => LoginStatus;
@@ -3,5 +3,13 @@ interface GetBaseHtmlProps {
3
3
  message: string;
4
4
  type: "success" | "error";
5
5
  }
6
+ /**
7
+ * Suffix of the `<title>` of every page this server serves. Doubles as the
8
+ * fingerprint the EADDRINUSE reclaim probe (server.ts) uses to recognize a
9
+ * stale `uip login` callback listener, so treat it as a wire format: stale
10
+ * listeners from older builds are recognized only while the rendered title
11
+ * keeps ending in " - UiPath CLI" (unchanged since #59).
12
+ */
13
+ export declare const CALLBACK_PAGE_TITLE_SUFFIX = " - UiPath CLI";
6
14
  export declare const getBaseHtml: ({ title, message, type }: GetBaseHtmlProps) => string;
7
15
  export {};
@@ -48,20 +48,20 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
48
48
  // node:buffer
49
49
  var exports_buffer = {};
50
50
  __export(exports_buffer, {
51
- transcode: () => transcode,
52
- resolveObjectURL: () => resolveObjectURL,
53
- kStringMaxLength: () => kStringMaxLength,
54
- kMaxLength: () => kMaxLength,
55
- isUtf8: () => isUtf8,
56
- isAscii: () => isAscii,
57
- default: () => buffer_default,
58
- constants: () => constants,
59
- btoa: () => btoa,
60
- atob: () => atob2,
61
- INSPECT_MAX_BYTES: () => INSPECT_MAX_BYTES,
62
- File: () => File,
51
+ Blob: () => Blob,
63
52
  Buffer: () => Buffer2,
64
- Blob: () => Blob
53
+ File: () => File,
54
+ INSPECT_MAX_BYTES: () => INSPECT_MAX_BYTES,
55
+ atob: () => atob2,
56
+ btoa: () => btoa,
57
+ constants: () => constants,
58
+ default: () => buffer_default,
59
+ isAscii: () => isAscii,
60
+ isUtf8: () => isUtf8,
61
+ kMaxLength: () => kMaxLength,
62
+ kStringMaxLength: () => kStringMaxLength,
63
+ resolveObjectURL: () => resolveObjectURL,
64
+ transcode: () => transcode
65
65
  });
66
66
  function getLens(b64) {
67
67
  var len2 = b64.length;
@@ -1384,35 +1384,35 @@ var init_buffer = __esm(() => {
1384
1384
  // node:util
1385
1385
  var exports_util = {};
1386
1386
  __export(exports_util, {
1387
- types: () => types,
1388
- promisify: () => promisify,
1389
- log: () => log,
1390
- isUndefined: () => isUndefined,
1391
- isSymbol: () => isSymbol,
1392
- isString: () => isString,
1393
- isRegExp: () => isRegExp,
1394
- isPrimitive: () => isPrimitive,
1395
- isObject: () => isObject,
1396
- isNumber: () => isNumber,
1397
- isNullOrUndefined: () => isNullOrUndefined,
1398
- isNull: () => isNull,
1399
- isFunction: () => isFunction,
1400
- isError: () => isError,
1401
- isDate: () => isDate,
1402
- isBuffer: () => isBuffer,
1403
- isBoolean: () => isBoolean,
1404
- isArray: () => isArray,
1405
- inspect: () => inspect,
1406
- inherits: () => inherits,
1407
- format: () => format,
1408
- deprecate: () => deprecate,
1409
- default: () => util_default,
1410
- debuglog: () => debuglog,
1411
- callbackifyOnRejected: () => callbackifyOnRejected,
1412
- callbackify: () => callbackify,
1413
- _extend: () => _extend,
1387
+ TextDecoder: () => TextDecoder,
1414
1388
  TextEncoder: () => TextEncoder2,
1415
- TextDecoder: () => TextDecoder
1389
+ _extend: () => _extend,
1390
+ callbackify: () => callbackify,
1391
+ callbackifyOnRejected: () => callbackifyOnRejected,
1392
+ debuglog: () => debuglog,
1393
+ default: () => util_default,
1394
+ deprecate: () => deprecate,
1395
+ format: () => format,
1396
+ inherits: () => inherits,
1397
+ inspect: () => inspect,
1398
+ isArray: () => isArray,
1399
+ isBoolean: () => isBoolean,
1400
+ isBuffer: () => isBuffer,
1401
+ isDate: () => isDate,
1402
+ isError: () => isError,
1403
+ isFunction: () => isFunction,
1404
+ isNull: () => isNull,
1405
+ isNullOrUndefined: () => isNullOrUndefined,
1406
+ isNumber: () => isNumber,
1407
+ isObject: () => isObject,
1408
+ isPrimitive: () => isPrimitive,
1409
+ isRegExp: () => isRegExp,
1410
+ isString: () => isString,
1411
+ isSymbol: () => isSymbol,
1412
+ isUndefined: () => isUndefined,
1413
+ log: () => log,
1414
+ promisify: () => promisify,
1415
+ types: () => types
1416
1416
  });
1417
1417
  function format(f, ...args) {
1418
1418
  if (!isString(f)) {
@@ -1794,16 +1794,16 @@ var init_util = __esm(() => {
1794
1794
  // node:events
1795
1795
  var exports_events = {};
1796
1796
  __export(exports_events, {
1797
- setMaxListeners: () => setMaxListeners2,
1798
- once: () => once2,
1799
- listenerCount: () => listenerCount2,
1800
- init: () => EventEmitter,
1801
- getMaxListeners: () => getMaxListeners2,
1802
- getEventListeners: () => getEventListeners,
1803
- default: () => events_default,
1804
- captureRejectionSymbol: () => captureRejectionSymbol,
1797
+ EventEmitter: () => EventEmitter,
1805
1798
  addAbortListener: () => addAbortListener,
1806
- EventEmitter: () => EventEmitter
1799
+ captureRejectionSymbol: () => captureRejectionSymbol,
1800
+ default: () => events_default,
1801
+ getEventListeners: () => getEventListeners,
1802
+ getMaxListeners: () => getMaxListeners2,
1803
+ init: () => EventEmitter,
1804
+ listenerCount: () => listenerCount2,
1805
+ once: () => once2,
1806
+ setMaxListeners: () => setMaxListeners2
1807
1807
  });
1808
1808
  function emitError(emitter, args) {
1809
1809
  var { _events: events } = emitter;
@@ -2165,7 +2165,7 @@ var init_events = __esm(() => {
2165
2165
  });
2166
2166
 
2167
2167
  // node:stream
2168
- var require_stream = __commonJS((exports, module) => {
2168
+ var require_stream = __commonJS(function(exports, module) {
2169
2169
  var __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
2170
2170
  var require_primordials = __commonJS2((exports2, module2) => {
2171
2171
 
@@ -6089,51 +6089,51 @@ var require_stream = __commonJS((exports, module) => {
6089
6089
  // node:crypto
6090
6090
  var exports_crypto = {};
6091
6091
  __export(exports_crypto, {
6092
- webcrypto: () => webcrypto,
6093
- rng: () => rng2,
6094
- randomUUID: () => randomUUID,
6095
- randomFillSync: () => randomFillSync2,
6096
- randomFill: () => randomFill2,
6097
- randomBytes: () => randomBytes2,
6098
- publicEncrypt: () => publicEncrypt2,
6099
- publicDecrypt: () => publicDecrypt2,
6100
- pseudoRandomBytes: () => pseudoRandomBytes2,
6101
- prng: () => prng2,
6102
- privateEncrypt: () => privateEncrypt2,
6103
- privateDecrypt: () => privateDecrypt2,
6104
- pbkdf2Sync: () => pbkdf2Sync2,
6105
- pbkdf2: () => pbkdf22,
6106
- listCiphers: () => listCiphers2,
6107
- getRandomValues: () => getRandomValues,
6108
- getHashes: () => getHashes2,
6109
- getDiffieHellman: () => getDiffieHellman2,
6110
- getCurves: () => getCurves,
6111
- getCiphers: () => getCiphers2,
6112
- default: () => crypto_default,
6113
- createVerify: () => createVerify2,
6114
- createSign: () => createSign2,
6115
- createHmac: () => createHmac2,
6116
- createHash: () => createHash2,
6117
- createECDH: () => createECDH2,
6118
- createDiffieHellmanGroup: () => createDiffieHellmanGroup2,
6119
- createDiffieHellman: () => createDiffieHellman2,
6120
- createDecipheriv: () => createDecipheriv2,
6121
- createDecipher: () => createDecipher2,
6122
- createCredentials: () => createCredentials2,
6123
- createCipheriv: () => createCipheriv2,
6124
- createCipher: () => createCipher2,
6125
- constants: () => constants2,
6126
- Verify: () => Verify2,
6127
- Sign: () => Sign2,
6128
- Hmac: () => Hmac2,
6129
- Hash: () => Hash2,
6130
- DiffieHellmanGroup: () => DiffieHellmanGroup2,
6131
- DiffieHellman: () => DiffieHellman2,
6132
- Decipheriv: () => Decipheriv2,
6133
- Decipher: () => Decipher2,
6134
- DEFAULT_ENCODING: () => DEFAULT_ENCODING,
6092
+ Cipher: () => Cipher2,
6135
6093
  Cipheriv: () => Cipheriv2,
6136
- Cipher: () => Cipher2
6094
+ DEFAULT_ENCODING: () => DEFAULT_ENCODING,
6095
+ Decipher: () => Decipher2,
6096
+ Decipheriv: () => Decipheriv2,
6097
+ DiffieHellman: () => DiffieHellman2,
6098
+ DiffieHellmanGroup: () => DiffieHellmanGroup2,
6099
+ Hash: () => Hash2,
6100
+ Hmac: () => Hmac2,
6101
+ Sign: () => Sign2,
6102
+ Verify: () => Verify2,
6103
+ constants: () => constants2,
6104
+ createCipher: () => createCipher2,
6105
+ createCipheriv: () => createCipheriv2,
6106
+ createCredentials: () => createCredentials2,
6107
+ createDecipher: () => createDecipher2,
6108
+ createDecipheriv: () => createDecipheriv2,
6109
+ createDiffieHellman: () => createDiffieHellman2,
6110
+ createDiffieHellmanGroup: () => createDiffieHellmanGroup2,
6111
+ createECDH: () => createECDH2,
6112
+ createHash: () => createHash2,
6113
+ createHmac: () => createHmac2,
6114
+ createSign: () => createSign2,
6115
+ createVerify: () => createVerify2,
6116
+ default: () => crypto_default,
6117
+ getCiphers: () => getCiphers2,
6118
+ getCurves: () => getCurves,
6119
+ getDiffieHellman: () => getDiffieHellman2,
6120
+ getHashes: () => getHashes2,
6121
+ getRandomValues: () => getRandomValues,
6122
+ listCiphers: () => listCiphers2,
6123
+ pbkdf2: () => pbkdf22,
6124
+ pbkdf2Sync: () => pbkdf2Sync2,
6125
+ privateDecrypt: () => privateDecrypt2,
6126
+ privateEncrypt: () => privateEncrypt2,
6127
+ prng: () => prng2,
6128
+ pseudoRandomBytes: () => pseudoRandomBytes2,
6129
+ publicDecrypt: () => publicDecrypt2,
6130
+ publicEncrypt: () => publicEncrypt2,
6131
+ randomBytes: () => randomBytes2,
6132
+ randomFill: () => randomFill2,
6133
+ randomFillSync: () => randomFillSync2,
6134
+ randomUUID: () => randomUUID,
6135
+ rng: () => rng2,
6136
+ webcrypto: () => webcrypto
6137
6137
  });
6138
6138
  function __accessProp2(key) {
6139
6139
  return this[key];
@@ -18294,7 +18294,7 @@ class InvalidBaseUrlError extends Error {
18294
18294
  super(`Invalid base URL: "${url}"
18295
18295
  ` + `Reason: ${reason}
18296
18296
 
18297
- ` + `Expected format: an https:// URL, e.g. https://cloud.uipath.com (commercial), https://govcloud.uipath.us (Public Sector), or your Automation Suite host (https://<your-host>).
18297
+ ` + `Expected format: an https:// URL (or bare host — https:// is assumed), e.g. https://cloud.uipath.com (commercial), https://govcloud.uipath.us (Public Sector), or your Automation Suite host (https://<your-host>).
18298
18298
  ` + `You can specify the URL via:
18299
18299
  ` + ` • --authority flag
18300
18300
  ` + ` • UIPATH_URL environment variable
@@ -18315,10 +18315,16 @@ var normalizeAndValidateBaseUrl = (rawUrl) => {
18315
18315
  while (baseUrl.endsWith("/")) {
18316
18316
  baseUrl = baseUrl.slice(0, -1);
18317
18317
  }
18318
+ const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseUrl);
18319
+ const [hostCandidate] = baseUrl.split(/[/?#]/, 1);
18320
+ if (!hasScheme && hostCandidate.includes(".")) {
18321
+ baseUrl = `https://${baseUrl}`;
18322
+ }
18318
18323
  const resolvedBaseUrl = baseUrl;
18319
18324
  const [urlError, url] = catchError(() => new URL(resolvedBaseUrl));
18320
18325
  if (urlError) {
18321
- throw new InvalidBaseUrlError(baseUrl, `Malformed URL. ${urlError instanceof Error ? urlError.message : "Unknown error"}`);
18326
+ const shapeHint = !hasScheme && !hostCandidate.includes(".") ? ` "${rawUrl.trim()}" is not a URL or a host name. Pass the full authority URL — https://<host>, or just <host> (https:// is assumed).` : ` ${urlError instanceof Error ? urlError.message : "Unknown error"}`;
18327
+ throw new InvalidBaseUrlError(baseUrl, `Malformed URL.${shapeHint}`);
18322
18328
  }
18323
18329
  if (url.protocol !== "https:") {
18324
18330
  throw new InvalidBaseUrlError(baseUrl, `Authority must use https:// scheme, got ${url.protocol}//. OIDC token exchange requires TLS end-to-end.`);
@@ -18539,6 +18545,17 @@ var requireEnv = (name) => {
18539
18545
  }
18540
18546
  return value;
18541
18547
  };
18548
+ var OPAQUE_TOKEN_BASE_URL_VAR = "UIPATH_URL";
18549
+ var resolveBaseUrl = (rawUrl, failureContext) => {
18550
+ const [baseUrlError, baseUrl] = catchError(() => normalizeAndValidateBaseUrl(rawUrl));
18551
+ if (baseUrlError) {
18552
+ if (baseUrlError instanceof InvalidBaseUrlError) {
18553
+ throw baseUrlError;
18554
+ }
18555
+ throw new EnvAuthConfigError(`${failureContext}: ` + `${baseUrlError instanceof Error ? baseUrlError.message : String(baseUrlError)}`);
18556
+ }
18557
+ return baseUrl;
18558
+ };
18542
18559
  var readAuthFromEnv = () => {
18543
18560
  const accessToken = requireEnv(ENV_AUTH_VARS.token);
18544
18561
  const organizationName = requireEnv(ENV_AUTH_VARS.organizationName);
@@ -18547,19 +18564,29 @@ var readAuthFromEnv = () => {
18547
18564
  const tenantId = requireEnv(ENV_AUTH_VARS.tenantId);
18548
18565
  const [parseError, payload] = catchError(() => parseJWT(accessToken));
18549
18566
  if (parseError) {
18550
- throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} is not a valid JWT: ` + `${parseError instanceof Error ? parseError.message : String(parseError)}`);
18567
+ const parseErrorMessage = parseError instanceof Error ? parseError.message : String(parseError);
18568
+ const rawUrl = process.env[OPAQUE_TOKEN_BASE_URL_VAR];
18569
+ if (!rawUrl) {
18570
+ throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} is not a JWT (treating it as an opaque token, ` + `e.g. a Personal Access Token). Set ${OPAQUE_TOKEN_BASE_URL_VAR} to the ` + `UiPath base URL if this is a PAT, or fix the token if it was meant to ` + `be a JWT (parse error: ${parseErrorMessage}).`);
18571
+ }
18572
+ const baseUrl2 = resolveBaseUrl(rawUrl, `Failed to validate ${OPAQUE_TOKEN_BASE_URL_VAR}`);
18573
+ return {
18574
+ loginStatus: "Logged in",
18575
+ accessToken,
18576
+ baseUrl: baseUrl2,
18577
+ organizationName,
18578
+ organizationId,
18579
+ tenantName,
18580
+ tenantId,
18581
+ source: "env-vars" /* EnvironmentVariables */,
18582
+ hint: "Token is opaque (not a JWT) - expiration and identity cannot be " + "determined locally. Commands will fail with 401 once it is revoked or " + `expired. If this was meant to be a JWT instead of a PAT, note: ${parseErrorMessage}`
18583
+ };
18551
18584
  }
18552
18585
  const iss = payload.iss;
18553
18586
  if (typeof iss !== "string" || iss.length === 0) {
18554
18587
  throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} has no 'iss' claim; cannot determine ` + `the UiPath server. Ensure the token was issued by a UiPath identity server.`);
18555
18588
  }
18556
- const [baseUrlError, baseUrl] = catchError(() => normalizeAndValidateBaseUrl(iss));
18557
- if (baseUrlError) {
18558
- if (baseUrlError instanceof InvalidBaseUrlError) {
18559
- throw baseUrlError;
18560
- }
18561
- throw new EnvAuthConfigError(`Failed to derive server URL from token 'iss' claim: ` + `${baseUrlError instanceof Error ? baseUrlError.message : String(baseUrlError)}`);
18562
- }
18589
+ const baseUrl = resolveBaseUrl(iss, "Failed to derive server URL from token 'iss' claim");
18563
18590
  const expiration = getTokenExpiration(accessToken);
18564
18591
  const loginStatus = expiration && expiration <= new Date ? "Expired" : "Logged in";
18565
18592
  const identity = resolveSessionIdentity(accessToken);
@@ -19711,42 +19738,42 @@ var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId)
19711
19738
  return data;
19712
19739
  };
19713
19740
  export {
19714
- setAuthFileConfig,
19715
- setActiveAuthProfile,
19716
- saveEnvFileAsync,
19717
- runWithAuthProfile,
19718
- resolveSessionIdentity,
19719
- resolveEnvFilePathAsync,
19720
- resolveEnvFileLocationAsync,
19721
- resolveAuthProfileFilePath,
19722
- refreshAccessToken,
19723
- parseJWT,
19724
- parseAuthFlow,
19725
- normalizeAuthProfileName,
19726
- logoutWithDeps,
19727
- logout,
19728
- loadEnvFileAsync,
19729
- isTokenRefreshOAuthFailure,
19730
- isNode,
19731
- isBrowser,
19732
- getLoginStatusWithDeps,
19733
- getLoginStatusAsync,
19734
- getAuthEnv,
19735
- getAuthContext,
19736
- getActiveAuthProfileFilePath,
19737
- getActiveAuthProfile,
19738
- fetchTenantsAndOrganizations,
19739
- clientCredentialsLogin,
19740
- clearActiveAuthProfile,
19741
- TokenRefreshOAuthError,
19742
- InvalidBaseUrlError,
19743
- DEFAULT_ENV_FILENAME,
19744
- DEFAULT_AUTH_PROFILE,
19745
- DEFAULT_AUTH_FILENAME,
19746
- CredentialSource,
19747
- ClientCredentialsAuthenticationError,
19741
+ AUTH_FLOW_ENV_VAR,
19748
19742
  AuthProfileValidationError,
19749
- AUTH_FLOW_ENV_VAR
19743
+ ClientCredentialsAuthenticationError,
19744
+ CredentialSource,
19745
+ DEFAULT_AUTH_FILENAME,
19746
+ DEFAULT_AUTH_PROFILE,
19747
+ DEFAULT_ENV_FILENAME,
19748
+ InvalidBaseUrlError,
19749
+ TokenRefreshOAuthError,
19750
+ clearActiveAuthProfile,
19751
+ clientCredentialsLogin,
19752
+ fetchTenantsAndOrganizations,
19753
+ getActiveAuthProfile,
19754
+ getActiveAuthProfileFilePath,
19755
+ getAuthContext,
19756
+ getAuthEnv,
19757
+ getLoginStatusAsync,
19758
+ getLoginStatusWithDeps,
19759
+ isBrowser,
19760
+ isNode,
19761
+ isTokenRefreshOAuthFailure,
19762
+ loadEnvFileAsync,
19763
+ logout,
19764
+ logoutWithDeps,
19765
+ normalizeAuthProfileName,
19766
+ parseAuthFlow,
19767
+ parseJWT,
19768
+ refreshAccessToken,
19769
+ resolveAuthProfileFilePath,
19770
+ resolveEnvFileLocationAsync,
19771
+ resolveEnvFilePathAsync,
19772
+ resolveSessionIdentity,
19773
+ runWithAuthProfile,
19774
+ saveEnvFileAsync,
19775
+ setActiveAuthProfile,
19776
+ setAuthFileConfig
19750
19777
  };
19751
19778
 
19752
- //# debugId=7467DDE1FA7C205564756E2164756E21
19779
+ //# debugId=2C66658C2F43E99364756E2164756E21
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from "./authContext";
2
2
  export * from "./authProfile";
3
3
  export * from "./clientCredentials";
4
4
  export { type AuthFileConfig, InvalidBaseUrlError, setAuthFileConfig, } from "./config";
5
- export { AUTH_CANCELLED_ERROR_CODE, AUTH_TIMEOUT_ERROR_CODE, DEFAULT_AUTH_TIMEOUT_MS, } from "./constants";
5
+ export { AUTH_CANCELLED_ERROR_CODE, AUTH_RECLAIMED_ERROR_CODE, AUTH_TIMEOUT_ERROR_CODE, DEFAULT_AUTH_TIMEOUT_MS, } from "./constants";
6
6
  export { ENFORCE_ROBOT_AUTH_VAR, ENV_AUTH_ENABLE_VAR, ENV_AUTH_VARS, EnvAuthConfigError, isEnvAuthEnabled, isRobotAuthEnforced, readAuthFromEnv, } from "./envAuth";
7
7
  export { FederatedCredentialsAuthenticationError, federatedCredentialsLogin, JWT_BEARER_ASSERTION_TYPE, } from "./federatedCredentials";
8
8
  export * from "./interactive";
@@ -38,8 +38,15 @@ interface AuthenticateOptions {
38
38
  /** Cancels a stuck browser login: closes the local callback server and
39
39
  * rejects with `AUTH_CANCELLED_ERROR_CODE`. Node (local-server) path only. */
40
40
  signal?: AbortSignal;
41
+ /** Invoked once when a stale `uip login` callback listener was holding
42
+ * the local port and was released so this login could bind it
43
+ * (EADDRINUSE reclaim; Node path only). `pid` names the evicted
44
+ * listener when known. */
45
+ onPortReclaimed?: (details: {
46
+ pid?: number;
47
+ }) => void;
41
48
  }
42
- export declare const authenticate: ({ baseUrl, clientId, clientSecret, redirectUri, scope, organization, timeoutMs, noBrowser, onAuthUrl, signal, }: AuthenticateOptions) => Promise<{
49
+ export declare const authenticate: ({ baseUrl, clientId, clientSecret, redirectUri, scope, organization, timeoutMs, noBrowser, onAuthUrl, signal, onPortReclaimed, }: AuthenticateOptions) => Promise<{
43
50
  accessToken: string;
44
51
  refreshToken: string;
45
52
  }>;
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ function settlePromiseLike(thenable) {
45
45
  }
46
46
 
47
47
  // src/constants.ts
48
- var UIPATH_HOME_DIR = ".uipath", AUTH_FILENAME = ".auth", DEFAULT_BASE_URL = "https://cloud.uipath.com", DEFAULT_AUTH_TIMEOUT_MS, AUTH_TIMEOUT_ERROR_CODE = "EAUTHTIMEOUT", AUTH_CANCELLED_ERROR_CODE = "EAUTHCANCELLED", DEFAULT_REDIRECT_URI = "http://localhost:8104/oidc/login";
48
+ var UIPATH_HOME_DIR = ".uipath", AUTH_FILENAME = ".auth", DEFAULT_BASE_URL = "https://cloud.uipath.com", DEFAULT_AUTH_TIMEOUT_MS, AUTH_TIMEOUT_ERROR_CODE = "EAUTHTIMEOUT", AUTH_CANCELLED_ERROR_CODE = "EAUTHCANCELLED", AUTH_RECLAIMED_ERROR_CODE = "EAUTHRECLAIMED", DEFAULT_REDIRECT_URI = "http://localhost:8104/oidc/login";
49
49
  var init_constants = __esm(() => {
50
50
  DEFAULT_AUTH_TIMEOUT_MS = 5 * 60 * 1000;
51
51
  });
@@ -411,9 +411,9 @@ var init_is_in_ssh = __esm(() => {
411
411
  // ../../node_modules/open/index.js
412
412
  var exports_open = {};
413
413
  __export(exports_open, {
414
- openApp: () => openApp,
414
+ apps: () => apps,
415
415
  default: () => open_default,
416
- apps: () => apps
416
+ openApp: () => openApp
417
417
  });
418
418
  import process8 from "node:process";
419
419
  import path from "node:path";
@@ -1043,7 +1043,7 @@ var init_browser_strategy = __esm(() => {
1043
1043
  });
1044
1044
 
1045
1045
  // src/getBaseHtml.ts
1046
- var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
1046
+ var CALLBACK_PAGE_TITLE_SUFFIX = " - UiPath CLI", escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
1047
1047
  switch (char) {
1048
1048
  case "&":
1049
1049
  return "&amp;";
@@ -1069,7 +1069,7 @@ var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
1069
1069
  <head>
1070
1070
  <meta charset="UTF-8">
1071
1071
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1072
- <title>${safeTitle} - UiPath CLI</title>
1072
+ <title>${safeTitle}${CALLBACK_PAGE_TITLE_SUFFIX}</title>
1073
1073
  <link rel="preconnect" href="https://fonts.googleapis.com">
1074
1074
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1075
1075
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400&family=Poppins:wght@600&display=swap" rel="stylesheet">
@@ -1205,11 +1205,52 @@ var escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => {
1205
1205
  };
1206
1206
 
1207
1207
  // src/server.ts
1208
- var startServer = async ({
1208
+ var RECLAIM_PROBE_PATH = "/uip-reclaim-probe", RECLAIM_LISTENER_PID_HEADER = "x-uipath-cli-login-pid", RECLAIM_PROBER_PID_HEADER = "x-uipath-cli-prober-pid", RECLAIM_MARKER, RECLAIM_PROBE_TIMEOUT_MS = 1000, RECLAIM_REBIND_DELAY_MS = 150, RECLAIM_PROBE_READ_LIMIT, probeStaleCallbackListener = (http, host, port) => new Promise((resolve2) => {
1209
+ let sawResponse = false;
1210
+ let listenerPid;
1211
+ let deadline;
1212
+ const settle = (verdict) => {
1213
+ clearTimeout(deadline);
1214
+ resolve2({ verdict, pid: listenerPid });
1215
+ };
1216
+ const req = http.get({
1217
+ host,
1218
+ port,
1219
+ path: RECLAIM_PROBE_PATH,
1220
+ timeout: RECLAIM_PROBE_TIMEOUT_MS,
1221
+ headers: { [RECLAIM_PROBER_PID_HEADER]: String(process.pid) }
1222
+ }, (res) => {
1223
+ sawResponse = true;
1224
+ const rawPid = res.headers[RECLAIM_LISTENER_PID_HEADER];
1225
+ if (typeof rawPid === "string" && /^\d+$/.test(rawPid)) {
1226
+ listenerPid = Number.parseInt(rawPid, 10);
1227
+ }
1228
+ res.on("error", () => {});
1229
+ let body = "";
1230
+ res.setEncoding("utf8");
1231
+ const classify = () => settle(body.includes(RECLAIM_MARKER) ? "reclaimed" : "foreign");
1232
+ res.on("data", (chunk) => {
1233
+ body += chunk;
1234
+ if (body.length >= RECLAIM_PROBE_READ_LIMIT) {
1235
+ classify();
1236
+ req.destroy();
1237
+ }
1238
+ });
1239
+ res.on("end", classify);
1240
+ });
1241
+ const giveUp = () => {
1242
+ settle("foreign");
1243
+ req.destroy();
1244
+ };
1245
+ req.on("timeout", giveUp);
1246
+ deadline = setTimeout(giveUp, RECLAIM_PROBE_TIMEOUT_MS);
1247
+ req.on("error", () => settle(sawResponse ? "foreign" : "gone"));
1248
+ }), startServer = async ({
1209
1249
  redirectUri,
1210
1250
  timeoutMs = DEFAULT_AUTH_TIMEOUT_MS,
1211
1251
  onListening,
1212
- signal
1252
+ signal,
1253
+ onPortReclaimed
1213
1254
  }) => {
1214
1255
  let http;
1215
1256
  try {
@@ -1234,6 +1275,25 @@ var startServer = async ({
1234
1275
  return;
1235
1276
  }
1236
1277
  const url = new URL(req.url, redirectUri);
1278
+ if (url.pathname === RECLAIM_PROBE_PATH) {
1279
+ const proberPidRaw = req.headers[RECLAIM_PROBER_PID_HEADER];
1280
+ const proberPid = typeof proberPidRaw === "string" && /^\d+$/.test(proberPidRaw) ? ` (PID ${proberPidRaw})` : "";
1281
+ res.writeHead(200, {
1282
+ "Content-Type": "text/html; charset=utf-8",
1283
+ Connection: "close",
1284
+ [RECLAIM_LISTENER_PID_HEADER]: String(process.pid)
1285
+ });
1286
+ res.end(getBaseHtml({
1287
+ title: "Login superseded",
1288
+ message: "This sign-in was taken over by a newer 'uip login' on this machine.",
1289
+ type: "error"
1290
+ }));
1291
+ server.close();
1292
+ const err = new Error(`Another 'uip login'${proberPid} on this machine took over the callback port; this login was cancelled.`);
1293
+ err.code = AUTH_RECLAIMED_ERROR_CODE;
1294
+ reject(err);
1295
+ return;
1296
+ }
1237
1297
  const error = url.searchParams.get("error");
1238
1298
  if (error) {
1239
1299
  res.writeHead(400, {
@@ -1278,7 +1338,9 @@ var startServer = async ({
1278
1338
  return;
1279
1339
  });
1280
1340
  let timeoutHandle;
1341
+ let settled = false;
1281
1342
  const onAbort = () => {
1343
+ settled = true;
1282
1344
  clearTimeout(timeoutHandle);
1283
1345
  server.close();
1284
1346
  const err = new Error("Authentication cancelled");
@@ -1293,6 +1355,7 @@ var startServer = async ({
1293
1355
  signal.addEventListener("abort", onAbort, { once: true });
1294
1356
  }
1295
1357
  timeoutHandle = setTimeout(() => {
1358
+ settled = true;
1296
1359
  server.close();
1297
1360
  signal?.removeEventListener("abort", onAbort);
1298
1361
  const err = new Error("Authentication timeout");
@@ -1300,20 +1363,62 @@ var startServer = async ({
1300
1363
  reject(err);
1301
1364
  }, timeoutMs);
1302
1365
  const bindHost = redirectUri.hostname === "localhost" ? "127.0.0.1" : redirectUri.hostname;
1303
- server.on("error", (err) => {
1366
+ const fail = (err) => {
1367
+ settled = true;
1304
1368
  clearTimeout(timeoutHandle);
1305
1369
  signal?.removeEventListener("abort", onAbort);
1306
1370
  reject(err);
1371
+ };
1372
+ let reclaimAttempted = false;
1373
+ let reclaimed = false;
1374
+ let reclaimedPid;
1375
+ let rebindAttemptsLeft = 0;
1376
+ server.on("listening", () => {
1377
+ Promise.resolve().then(() => {
1378
+ if (reclaimed)
1379
+ onPortReclaimed?.({ pid: reclaimedPid });
1380
+ }).then(() => onListening?.()).catch((err) => {
1381
+ settled = true;
1382
+ server.close();
1383
+ clearTimeout(timeoutHandle);
1384
+ reject(err);
1385
+ });
1307
1386
  });
1308
- server.listen(Number(redirectUri.port), bindHost, () => {
1309
- if (onListening) {
1310
- Promise.resolve(onListening()).catch((err) => {
1311
- server.close();
1312
- clearTimeout(timeoutHandle);
1313
- reject(err);
1314
- });
1387
+ const listen = () => {
1388
+ if (settled)
1389
+ return;
1390
+ server.listen(Number(redirectUri.port), bindHost);
1391
+ };
1392
+ server.on("error", (err) => {
1393
+ if (settled)
1394
+ return;
1395
+ const code = err.code;
1396
+ if (code === "EADDRINUSE") {
1397
+ if (!reclaimAttempted) {
1398
+ reclaimAttempted = true;
1399
+ probeStaleCallbackListener(http, bindHost, Number(redirectUri.port)).then(({ verdict, pid }) => {
1400
+ if (settled)
1401
+ return;
1402
+ if (verdict === "foreign") {
1403
+ fail(err);
1404
+ return;
1405
+ }
1406
+ reclaimed = verdict === "reclaimed";
1407
+ reclaimedPid = pid;
1408
+ rebindAttemptsLeft = 3;
1409
+ setTimeout(listen, RECLAIM_REBIND_DELAY_MS);
1410
+ });
1411
+ return;
1412
+ }
1413
+ if (rebindAttemptsLeft > 0) {
1414
+ rebindAttemptsLeft -= 1;
1415
+ setTimeout(listen, RECLAIM_REBIND_DELAY_MS);
1416
+ return;
1417
+ }
1315
1418
  }
1419
+ fail(err);
1316
1420
  });
1421
+ listen();
1317
1422
  server.on("close", () => {
1318
1423
  clearTimeout(timeoutHandle);
1319
1424
  signal?.removeEventListener("abort", onAbort);
@@ -1322,6 +1427,8 @@ var startServer = async ({
1322
1427
  };
1323
1428
  var init_server = __esm(() => {
1324
1429
  init_constants();
1430
+ RECLAIM_MARKER = `${CALLBACK_PAGE_TITLE_SUFFIX}</title>`;
1431
+ RECLAIM_PROBE_READ_LIMIT = 16 * 1024;
1325
1432
  });
1326
1433
 
1327
1434
  // src/strategies/node-strategy.ts
@@ -1337,6 +1444,7 @@ class NodeAuthStrategy {
1337
1444
  redirectUri,
1338
1445
  timeoutMs: opts?.timeoutMs,
1339
1446
  signal: opts?.signal,
1447
+ onPortReclaimed: opts?.onPortReclaimed,
1340
1448
  onListening: async () => {
1341
1449
  let safeUrl = "";
1342
1450
  for (const ch of url) {
@@ -1401,7 +1509,7 @@ class InvalidBaseUrlError extends Error {
1401
1509
  super(`Invalid base URL: "${url}"
1402
1510
  ` + `Reason: ${reason}
1403
1511
 
1404
- ` + `Expected format: an https:// URL, e.g. https://cloud.uipath.com (commercial), https://govcloud.uipath.us (Public Sector), or your Automation Suite host (https://<your-host>).
1512
+ ` + `Expected format: an https:// URL (or bare host — https:// is assumed), e.g. https://cloud.uipath.com (commercial), https://govcloud.uipath.us (Public Sector), or your Automation Suite host (https://<your-host>).
1405
1513
  ` + `You can specify the URL via:
1406
1514
  ` + ` • --authority flag
1407
1515
  ` + ` • UIPATH_URL environment variable
@@ -1422,10 +1530,16 @@ var normalizeAndValidateBaseUrl = (rawUrl) => {
1422
1530
  while (baseUrl.endsWith("/")) {
1423
1531
  baseUrl = baseUrl.slice(0, -1);
1424
1532
  }
1533
+ const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseUrl);
1534
+ const [hostCandidate] = baseUrl.split(/[/?#]/, 1);
1535
+ if (!hasScheme && hostCandidate.includes(".")) {
1536
+ baseUrl = `https://${baseUrl}`;
1537
+ }
1425
1538
  const resolvedBaseUrl = baseUrl;
1426
1539
  const [urlError, url] = catchError(() => new URL(resolvedBaseUrl));
1427
1540
  if (urlError) {
1428
- throw new InvalidBaseUrlError(baseUrl, `Malformed URL. ${urlError instanceof Error ? urlError.message : "Unknown error"}`);
1541
+ const shapeHint = !hasScheme && !hostCandidate.includes(".") ? ` "${rawUrl.trim()}" is not a URL or a host name. Pass the full authority URL — https://<host>, or just <host> (https:// is assumed).` : ` ${urlError instanceof Error ? urlError.message : "Unknown error"}`;
1542
+ throw new InvalidBaseUrlError(baseUrl, `Malformed URL.${shapeHint}`);
1429
1543
  }
1430
1544
  if (url.protocol !== "https:") {
1431
1545
  throw new InvalidBaseUrlError(baseUrl, `Authority must use https:// scheme, got ${url.protocol}//. OIDC token exchange requires TLS end-to-end.`);
@@ -1562,17 +1676,6 @@ function b64u(input) {
1562
1676
  }
1563
1677
  return encodeBase64Url(input);
1564
1678
  }
1565
-
1566
- class UnsupportedOperationError extends Error {
1567
- code;
1568
- constructor(message, options) {
1569
- super(message, options);
1570
- this.name = this.constructor.name;
1571
- this.code = UNSUPPORTED_OPERATION;
1572
- Error.captureStackTrace?.(this, this.constructor);
1573
- }
1574
- }
1575
-
1576
1679
  class OperationProcessingError extends Error {
1577
1680
  code;
1578
1681
  constructor(message, options) {
@@ -1587,60 +1690,6 @@ class OperationProcessingError extends Error {
1587
1690
  function OPE(message, code, cause) {
1588
1691
  return new OperationProcessingError(message, { code, cause });
1589
1692
  }
1590
- async function calculateJwkThumbprint(jwk) {
1591
- let components;
1592
- switch (jwk.kty) {
1593
- case "EC":
1594
- components = {
1595
- crv: jwk.crv,
1596
- kty: jwk.kty,
1597
- x: jwk.x,
1598
- y: jwk.y
1599
- };
1600
- break;
1601
- case "OKP":
1602
- components = {
1603
- crv: jwk.crv,
1604
- kty: jwk.kty,
1605
- x: jwk.x
1606
- };
1607
- break;
1608
- case "AKP":
1609
- components = {
1610
- alg: jwk.alg,
1611
- kty: jwk.kty,
1612
- pub: jwk.pub
1613
- };
1614
- break;
1615
- case "RSA":
1616
- components = {
1617
- e: jwk.e,
1618
- kty: jwk.kty,
1619
- n: jwk.n
1620
- };
1621
- break;
1622
- default:
1623
- throw new UnsupportedOperationError("unsupported JWK key type", { cause: jwk });
1624
- }
1625
- return b64u(await crypto.subtle.digest("SHA-256", buf(JSON.stringify(components))));
1626
- }
1627
- function assertCryptoKey(key, it) {
1628
- if (!(key instanceof CryptoKey)) {
1629
- throw CodedTypeError(`${it} must be a CryptoKey`, ERR_INVALID_ARG_TYPE);
1630
- }
1631
- }
1632
- function assertPrivateKey(key, it) {
1633
- assertCryptoKey(key, it);
1634
- if (key.type !== "private") {
1635
- throw CodedTypeError(`${it} must be a private CryptoKey`, ERR_INVALID_ARG_VALUE);
1636
- }
1637
- }
1638
- function assertPublicKey(key, it) {
1639
- assertCryptoKey(key, it);
1640
- if (key.type !== "public") {
1641
- throw CodedTypeError(`${it} must be a public CryptoKey`, ERR_INVALID_ARG_VALUE);
1642
- }
1643
- }
1644
1693
  function assertString(input, it, code, cause) {
1645
1694
  try {
1646
1695
  if (typeof input !== "string") {
@@ -1669,72 +1718,6 @@ async function calculatePKCECodeChallenge(codeVerifier) {
1669
1718
  assertString(codeVerifier, "codeVerifier");
1670
1719
  return b64u(await crypto.subtle.digest("SHA-256", buf(codeVerifier)));
1671
1720
  }
1672
- function psAlg(key) {
1673
- switch (key.algorithm.hash.name) {
1674
- case "SHA-256":
1675
- return "PS256";
1676
- case "SHA-384":
1677
- return "PS384";
1678
- case "SHA-512":
1679
- return "PS512";
1680
- default:
1681
- throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
1682
- cause: key
1683
- });
1684
- }
1685
- }
1686
- function rsAlg(key) {
1687
- switch (key.algorithm.hash.name) {
1688
- case "SHA-256":
1689
- return "RS256";
1690
- case "SHA-384":
1691
- return "RS384";
1692
- case "SHA-512":
1693
- return "RS512";
1694
- default:
1695
- throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
1696
- cause: key
1697
- });
1698
- }
1699
- }
1700
- function esAlg(key) {
1701
- switch (key.algorithm.namedCurve) {
1702
- case "P-256":
1703
- return "ES256";
1704
- case "P-384":
1705
- return "ES384";
1706
- case "P-521":
1707
- return "ES512";
1708
- default:
1709
- throw new UnsupportedOperationError("unsupported EcKeyAlgorithm namedCurve", { cause: key });
1710
- }
1711
- }
1712
- function keyToJws(key) {
1713
- switch (key.algorithm.name) {
1714
- case "RSA-PSS":
1715
- return psAlg(key);
1716
- case "RSASSA-PKCS1-v1_5":
1717
- return rsAlg(key);
1718
- case "ECDSA":
1719
- return esAlg(key);
1720
- case "Ed25519":
1721
- case "ML-DSA-44":
1722
- case "ML-DSA-65":
1723
- case "ML-DSA-87":
1724
- return key.algorithm.name;
1725
- case "EdDSA":
1726
- return "Ed25519";
1727
- default:
1728
- throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
1729
- }
1730
- }
1731
- function getClockSkew(client) {
1732
- const skew = client?.[clockSkew];
1733
- return typeof skew === "number" && Number.isFinite(skew) ? skew : 0;
1734
- }
1735
- function epochTime() {
1736
- return Math.floor(Date.now() / 1000);
1737
- }
1738
1721
  function ClientSecretPost(clientSecret) {
1739
1722
  assertString(clientSecret, '"clientSecret"');
1740
1723
  return (_as, client, body, _headers) => {
@@ -1747,27 +1730,6 @@ function None() {
1747
1730
  body.set("client_id", client.client_id);
1748
1731
  };
1749
1732
  }
1750
- async function signJwt(header, payload, key) {
1751
- if (!key.usages.includes("sign")) {
1752
- throw CodedTypeError('CryptoKey instances used for signing assertions must include "sign" in their "usages"', ERR_INVALID_ARG_VALUE);
1753
- }
1754
- const input = `${b64u(buf(JSON.stringify(header)))}.${b64u(buf(JSON.stringify(payload)))}`;
1755
- const signature = b64u(await crypto.subtle.sign(keyToSubtle(key), key, buf(input)));
1756
- return `${input}.${signature}`;
1757
- }
1758
- var jwkCache;
1759
- async function getSetPublicJwkCache(key, alg) {
1760
- const { kty, e, n, x, y, crv, pub } = await crypto.subtle.exportKey("jwk", key);
1761
- const jwk = { kty, e, n, x, y, crv, pub };
1762
- if (kty === "AKP")
1763
- jwk.alg = alg;
1764
- jwkCache.set(key, jwk);
1765
- return jwk;
1766
- }
1767
- async function publicJwk(key, alg) {
1768
- jwkCache ||= new WeakMap;
1769
- return jwkCache.get(key) || getSetPublicJwkCache(key, alg);
1770
- }
1771
1733
  var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
1772
1734
  try {
1773
1735
  return new URL(url, base);
@@ -1775,79 +1737,6 @@ var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) =>
1775
1737
  return null;
1776
1738
  }
1777
1739
  };
1778
- class DPoPHandler {
1779
- #header;
1780
- #privateKey;
1781
- #publicKey;
1782
- #clockSkew;
1783
- #modifyAssertion;
1784
- #map;
1785
- #jkt;
1786
- constructor(client, keyPair, options) {
1787
- assertPrivateKey(keyPair?.privateKey, '"DPoP.privateKey"');
1788
- assertPublicKey(keyPair?.publicKey, '"DPoP.publicKey"');
1789
- if (!keyPair.publicKey.extractable) {
1790
- throw CodedTypeError('"DPoP.publicKey.extractable" must be true', ERR_INVALID_ARG_VALUE);
1791
- }
1792
- this.#modifyAssertion = options?.[modifyAssertion];
1793
- this.#clockSkew = getClockSkew(client);
1794
- this.#privateKey = keyPair.privateKey;
1795
- this.#publicKey = keyPair.publicKey;
1796
- branded.add(this);
1797
- }
1798
- #get(key) {
1799
- this.#map ||= new Map;
1800
- let item = this.#map.get(key);
1801
- if (item) {
1802
- this.#map.delete(key);
1803
- this.#map.set(key, item);
1804
- }
1805
- return item;
1806
- }
1807
- #set(key, val) {
1808
- this.#map ||= new Map;
1809
- this.#map.delete(key);
1810
- if (this.#map.size === 100) {
1811
- this.#map.delete(this.#map.keys().next().value);
1812
- }
1813
- this.#map.set(key, val);
1814
- }
1815
- async calculateThumbprint() {
1816
- if (!this.#jkt) {
1817
- const jwk = await crypto.subtle.exportKey("jwk", this.#publicKey);
1818
- this.#jkt ||= await calculateJwkThumbprint(jwk);
1819
- }
1820
- return this.#jkt;
1821
- }
1822
- async addProof(url, headers, htm, accessToken) {
1823
- const alg = keyToJws(this.#privateKey);
1824
- this.#header ||= {
1825
- alg,
1826
- typ: "dpop+jwt",
1827
- jwk: await publicJwk(this.#publicKey, alg)
1828
- };
1829
- const nonce = this.#get(url.origin);
1830
- const now = epochTime() + this.#clockSkew;
1831
- const payload = {
1832
- iat: now,
1833
- jti: randomBytes(),
1834
- htm,
1835
- nonce,
1836
- htu: `${url.origin}${url.pathname}`,
1837
- ath: accessToken ? b64u(await crypto.subtle.digest("SHA-256", buf(accessToken))) : undefined
1838
- };
1839
- this.#modifyAssertion?.(this.#header, payload);
1840
- headers.set("dpop", await signJwt(this.#header, payload, this.#privateKey));
1841
- }
1842
- cacheNonce(response, url) {
1843
- try {
1844
- const nonce = response.headers.get("dpop-nonce");
1845
- if (nonce) {
1846
- this.#set(url.origin, nonce);
1847
- }
1848
- } catch {}
1849
- }
1850
- }
1851
1740
  var tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
1852
1741
  var token68Match = "[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}";
1853
1742
  var quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
@@ -1864,60 +1753,6 @@ var branded = new WeakSet;
1864
1753
  var nopkce = Symbol();
1865
1754
  var expectNoNonce = Symbol();
1866
1755
  var skipAuthTimeCheck = Symbol();
1867
- var UNSUPPORTED_OPERATION = "OAUTH_UNSUPPORTED_OPERATION";
1868
- function checkRsaKeyAlgorithm(key) {
1869
- const { algorithm } = key;
1870
- if (typeof algorithm.modulusLength !== "number" || algorithm.modulusLength < 2048) {
1871
- throw new UnsupportedOperationError(`unsupported ${algorithm.name} modulusLength`, {
1872
- cause: key
1873
- });
1874
- }
1875
- }
1876
- function ecdsaHashName(key) {
1877
- const { algorithm } = key;
1878
- switch (algorithm.namedCurve) {
1879
- case "P-256":
1880
- return "SHA-256";
1881
- case "P-384":
1882
- return "SHA-384";
1883
- case "P-521":
1884
- return "SHA-512";
1885
- default:
1886
- throw new UnsupportedOperationError("unsupported ECDSA namedCurve", { cause: key });
1887
- }
1888
- }
1889
- function keyToSubtle(key) {
1890
- switch (key.algorithm.name) {
1891
- case "ECDSA":
1892
- return {
1893
- name: key.algorithm.name,
1894
- hash: ecdsaHashName(key)
1895
- };
1896
- case "RSA-PSS": {
1897
- checkRsaKeyAlgorithm(key);
1898
- switch (key.algorithm.hash.name) {
1899
- case "SHA-256":
1900
- case "SHA-384":
1901
- case "SHA-512":
1902
- return {
1903
- name: key.algorithm.name,
1904
- saltLength: parseInt(key.algorithm.hash.name.slice(-3), 10) >> 3
1905
- };
1906
- default:
1907
- throw new UnsupportedOperationError("unsupported RSA-PSS hash name", { cause: key });
1908
- }
1909
- }
1910
- case "RSASSA-PKCS1-v1_5":
1911
- checkRsaKeyAlgorithm(key);
1912
- return key.algorithm.name;
1913
- case "ML-DSA-44":
1914
- case "ML-DSA-65":
1915
- case "ML-DSA-87":
1916
- case "Ed25519":
1917
- return key.algorithm.name;
1918
- }
1919
- throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
1920
- }
1921
1756
  var skipStateCheck = Symbol();
1922
1757
  var expectNoState = Symbol();
1923
1758
  var _nodiscoverycheck = Symbol();
@@ -2373,6 +2208,17 @@ var requireEnv = (name) => {
2373
2208
  }
2374
2209
  return value;
2375
2210
  };
2211
+ var OPAQUE_TOKEN_BASE_URL_VAR = "UIPATH_URL";
2212
+ var resolveBaseUrl = (rawUrl, failureContext) => {
2213
+ const [baseUrlError, baseUrl] = catchError(() => normalizeAndValidateBaseUrl(rawUrl));
2214
+ if (baseUrlError) {
2215
+ if (baseUrlError instanceof InvalidBaseUrlError) {
2216
+ throw baseUrlError;
2217
+ }
2218
+ throw new EnvAuthConfigError(`${failureContext}: ` + `${baseUrlError instanceof Error ? baseUrlError.message : String(baseUrlError)}`);
2219
+ }
2220
+ return baseUrl;
2221
+ };
2376
2222
  var readAuthFromEnv = () => {
2377
2223
  const accessToken = requireEnv(ENV_AUTH_VARS.token);
2378
2224
  const organizationName = requireEnv(ENV_AUTH_VARS.organizationName);
@@ -2381,19 +2227,29 @@ var readAuthFromEnv = () => {
2381
2227
  const tenantId = requireEnv(ENV_AUTH_VARS.tenantId);
2382
2228
  const [parseError, payload] = catchError(() => parseJWT(accessToken));
2383
2229
  if (parseError) {
2384
- throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} is not a valid JWT: ` + `${parseError instanceof Error ? parseError.message : String(parseError)}`);
2230
+ const parseErrorMessage = parseError instanceof Error ? parseError.message : String(parseError);
2231
+ const rawUrl = process.env[OPAQUE_TOKEN_BASE_URL_VAR];
2232
+ if (!rawUrl) {
2233
+ throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} is not a JWT (treating it as an opaque token, ` + `e.g. a Personal Access Token). Set ${OPAQUE_TOKEN_BASE_URL_VAR} to the ` + `UiPath base URL if this is a PAT, or fix the token if it was meant to ` + `be a JWT (parse error: ${parseErrorMessage}).`);
2234
+ }
2235
+ const baseUrl2 = resolveBaseUrl(rawUrl, `Failed to validate ${OPAQUE_TOKEN_BASE_URL_VAR}`);
2236
+ return {
2237
+ loginStatus: "Logged in",
2238
+ accessToken,
2239
+ baseUrl: baseUrl2,
2240
+ organizationName,
2241
+ organizationId,
2242
+ tenantName,
2243
+ tenantId,
2244
+ source: "env-vars" /* EnvironmentVariables */,
2245
+ hint: "Token is opaque (not a JWT) - expiration and identity cannot be " + "determined locally. Commands will fail with 401 once it is revoked or " + `expired. If this was meant to be a JWT instead of a PAT, note: ${parseErrorMessage}`
2246
+ };
2385
2247
  }
2386
2248
  const iss = payload.iss;
2387
2249
  if (typeof iss !== "string" || iss.length === 0) {
2388
2250
  throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} has no 'iss' claim; cannot determine ` + `the UiPath server. Ensure the token was issued by a UiPath identity server.`);
2389
2251
  }
2390
- const [baseUrlError, baseUrl] = catchError(() => normalizeAndValidateBaseUrl(iss));
2391
- if (baseUrlError) {
2392
- if (baseUrlError instanceof InvalidBaseUrlError) {
2393
- throw baseUrlError;
2394
- }
2395
- throw new EnvAuthConfigError(`Failed to derive server URL from token 'iss' claim: ` + `${baseUrlError instanceof Error ? baseUrlError.message : String(baseUrlError)}`);
2396
- }
2252
+ const baseUrl = resolveBaseUrl(iss, "Failed to derive server URL from token 'iss' claim");
2397
2253
  const expiration = getTokenExpiration(accessToken);
2398
2254
  const loginStatus = expiration && expiration <= new Date ? "Expired" : "Logged in";
2399
2255
  const identity = resolveSessionIdentity(accessToken);
@@ -3545,6 +3401,7 @@ var federatedCredentialsLogin = async ({
3545
3401
  };
3546
3402
  // src/interactive.ts
3547
3403
  init_src();
3404
+ init_constants();
3548
3405
 
3549
3406
  // src/tenantSelection.ts
3550
3407
  var LOGIN_STATUS_HINT = "Run 'uip login status' to see the organization the CLI is signed in to.";
@@ -3732,7 +3589,11 @@ var interactiveLoginWithDeps = async (options, deps) => {
3732
3589
  timeoutMs,
3733
3590
  noBrowser,
3734
3591
  signal,
3735
- onAuthUrl: noBrowser ? (url) => deliverAuthUrl(url) : undefined
3592
+ onAuthUrl: noBrowser ? (url) => deliverAuthUrl(url) : undefined,
3593
+ onPortReclaimed: ({ pid }) => emit({
3594
+ type: "port-reclaimed",
3595
+ message: `A previous 'uip login'${pid === undefined ? "" : ` (PID ${pid})`} was still holding the callback port ` + `(${new URL(DEFAULT_REDIRECT_URI).port}) and was released; continuing with this login.`
3596
+ })
3736
3597
  });
3737
3598
  return {
3738
3599
  UIPATH_ACCESS_TOKEN: authTokens.accessToken,
@@ -3887,7 +3748,8 @@ var authenticate = async ({
3887
3748
  timeoutMs,
3888
3749
  noBrowser,
3889
3750
  onAuthUrl,
3890
- signal
3751
+ signal,
3752
+ onPortReclaimed
3891
3753
  }) => {
3892
3754
  const config = await resolveConfigAsync({
3893
3755
  customAuthority: baseUrl,
@@ -3941,7 +3803,8 @@ var authenticate = async ({
3941
3803
  timeoutMs,
3942
3804
  noBrowser,
3943
3805
  onAuthUrl,
3944
- signal
3806
+ signal,
3807
+ onPortReclaimed
3945
3808
  });
3946
3809
  return await exchangeCodeForTokens({
3947
3810
  code,
@@ -3953,70 +3816,71 @@ var authenticate = async ({
3953
3816
  });
3954
3817
  };
3955
3818
  export {
3956
- setAuthFileConfig,
3957
- setActiveAuthProfile,
3958
- selectTenantWithDeps,
3959
- saveRefreshBreaker,
3960
- saveEnvFileAsync,
3961
- runWithAuthProfile,
3962
- resolveSessionIdentity,
3963
- resolveEnvFilePathAsync,
3964
- resolveEnvFileLocationAsync,
3965
- resolveAuthProfileFilePath,
3966
- registerRobotClientLoader,
3967
- refreshTokenFingerprint,
3968
- refreshAccessToken,
3969
- readAuthFromEnv,
3970
- parseJWT,
3971
- parseAuthFlow,
3972
- normalizeAuthProfileName,
3973
- logoutWithDeps,
3974
- logout,
3975
- loadRefreshBreaker,
3976
- loadEnvFileAsync,
3977
- isTokenRefreshOAuthFailure,
3978
- isTenantSelectionError,
3979
- isRobotAuthEnforced,
3980
- isNode,
3981
- isEnvAuthEnabled,
3982
- isBrowser,
3983
- interactiveLoginWithDeps,
3984
- interactiveLogin,
3985
- getLoginStatusWithDeps,
3986
- getLoginStatusAsync,
3987
- getAuthEnv,
3988
- getAuthContext,
3989
- getActiveAuthProfileFilePath,
3990
- getActiveAuthProfile,
3991
- fetchTenantsAndOrganizations,
3992
- federatedCredentialsLogin,
3993
- clientCredentialsLogin,
3994
- clearRefreshBreaker,
3995
- clearActiveAuthProfile,
3996
- authenticate,
3997
- TokenRefreshOAuthError,
3998
- TenantSelectionRequiredError,
3999
- TenantSelectionError,
4000
- TENANT_SELECTION_REQUIRED_CODE,
4001
- JWT_BEARER_ASSERTION_TYPE,
4002
- InvalidTenantError,
4003
- InvalidBaseUrlError,
4004
- INVALID_TENANT_CODE,
4005
- FederatedCredentialsAuthenticationError,
4006
- EnvAuthConfigError,
4007
- ENV_AUTH_VARS,
4008
- ENV_AUTH_ENABLE_VAR,
4009
- ENFORCE_ROBOT_AUTH_VAR,
4010
- DEFAULT_ENV_FILENAME,
4011
- DEFAULT_AUTH_TIMEOUT_MS,
4012
- DEFAULT_AUTH_PROFILE,
4013
- DEFAULT_AUTH_FILENAME,
4014
- CredentialSource,
4015
- ClientCredentialsAuthenticationError,
4016
- AuthProfileValidationError,
4017
- AUTH_TIMEOUT_ERROR_CODE,
3819
+ AUTH_CANCELLED_ERROR_CODE,
4018
3820
  AUTH_FLOW_ENV_VAR,
4019
- AUTH_CANCELLED_ERROR_CODE
3821
+ AUTH_RECLAIMED_ERROR_CODE,
3822
+ AUTH_TIMEOUT_ERROR_CODE,
3823
+ AuthProfileValidationError,
3824
+ ClientCredentialsAuthenticationError,
3825
+ CredentialSource,
3826
+ DEFAULT_AUTH_FILENAME,
3827
+ DEFAULT_AUTH_PROFILE,
3828
+ DEFAULT_AUTH_TIMEOUT_MS,
3829
+ DEFAULT_ENV_FILENAME,
3830
+ ENFORCE_ROBOT_AUTH_VAR,
3831
+ ENV_AUTH_ENABLE_VAR,
3832
+ ENV_AUTH_VARS,
3833
+ EnvAuthConfigError,
3834
+ FederatedCredentialsAuthenticationError,
3835
+ INVALID_TENANT_CODE,
3836
+ InvalidBaseUrlError,
3837
+ InvalidTenantError,
3838
+ JWT_BEARER_ASSERTION_TYPE,
3839
+ TENANT_SELECTION_REQUIRED_CODE,
3840
+ TenantSelectionError,
3841
+ TenantSelectionRequiredError,
3842
+ TokenRefreshOAuthError,
3843
+ authenticate,
3844
+ clearActiveAuthProfile,
3845
+ clearRefreshBreaker,
3846
+ clientCredentialsLogin,
3847
+ federatedCredentialsLogin,
3848
+ fetchTenantsAndOrganizations,
3849
+ getActiveAuthProfile,
3850
+ getActiveAuthProfileFilePath,
3851
+ getAuthContext,
3852
+ getAuthEnv,
3853
+ getLoginStatusAsync,
3854
+ getLoginStatusWithDeps,
3855
+ interactiveLogin,
3856
+ interactiveLoginWithDeps,
3857
+ isBrowser,
3858
+ isEnvAuthEnabled,
3859
+ isNode,
3860
+ isRobotAuthEnforced,
3861
+ isTenantSelectionError,
3862
+ isTokenRefreshOAuthFailure,
3863
+ loadEnvFileAsync,
3864
+ loadRefreshBreaker,
3865
+ logout,
3866
+ logoutWithDeps,
3867
+ normalizeAuthProfileName,
3868
+ parseAuthFlow,
3869
+ parseJWT,
3870
+ readAuthFromEnv,
3871
+ refreshAccessToken,
3872
+ refreshTokenFingerprint,
3873
+ registerRobotClientLoader,
3874
+ resolveAuthProfileFilePath,
3875
+ resolveEnvFileLocationAsync,
3876
+ resolveEnvFilePathAsync,
3877
+ resolveSessionIdentity,
3878
+ runWithAuthProfile,
3879
+ saveEnvFileAsync,
3880
+ saveRefreshBreaker,
3881
+ selectTenantWithDeps,
3882
+ setActiveAuthProfile,
3883
+ setAuthFileConfig
4020
3884
  };
4021
3885
 
4022
- //# debugId=C655D07F97343A8C64756E2164756E21
3886
+ //# debugId=8ECD0F0A4A5E19F264756E2164756E21
@@ -28,6 +28,9 @@ export type InteractiveLoginEvent = {
28
28
  } | {
29
29
  type: "auth-url";
30
30
  url: string;
31
+ } | {
32
+ type: "port-reclaimed";
33
+ message: string;
31
34
  };
32
35
  interface InteractiveLoginOptions {
33
36
  envFilePath?: string;
package/dist/server.d.ts CHANGED
@@ -11,5 +11,15 @@ interface StartCallbackServerProps {
11
11
  * extension) cancel a stuck login without waiting out `timeoutMs`.
12
12
  */
13
13
  signal?: AbortSignal;
14
+ /**
15
+ * Invoked once when a failed bind was recovered by releasing a stale
16
+ * `uip login` callback listener that was still holding the port (see the
17
+ * EADDRINUSE reclaim in this module). Fires right before `onListening`,
18
+ * so hosts can tell the user what happened alongside the auth URL.
19
+ * `pid` names the evicted listener when it was new enough to report one.
20
+ */
21
+ onPortReclaimed?: (details: {
22
+ pid?: number;
23
+ }) => void;
14
24
  }
15
- export declare const startServer: ({ redirectUri, timeoutMs, onListening, signal, }: StartCallbackServerProps) => Promise<URL>;
25
+ export declare const startServer: ({ redirectUri, timeoutMs, onListening, signal, onPortReclaimed, }: StartCallbackServerProps) => Promise<URL>;
@@ -19,6 +19,15 @@ export interface AuthExecuteOptions {
19
19
  * rejects. No effect once the flow has already completed.
20
20
  */
21
21
  signal?: AbortSignal;
22
+ /**
23
+ * Invoked once when a stale `uip login` callback listener was holding the
24
+ * local port and was released so this login could bind it (Node strategy
25
+ * only — the browser strategy has no local server). Fires before
26
+ * `onAuthUrl`. `pid` names the evicted listener when known.
27
+ */
28
+ onPortReclaimed?: (details: {
29
+ pid?: number;
30
+ }) => void;
22
31
  }
23
32
  export interface IAuthStrategy {
24
33
  execute(url: string, redirectUri: URL, expectedState: string, opts?: AuthExecuteOptions): Promise<string>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@uipath/auth",
3
3
  "license": "MIT",
4
- "version": "1.201.0-preview.132",
4
+ "version": "1.202.0-preview.134",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/UiPath/cli.git",
@@ -37,5 +37,5 @@
37
37
  "mihaigirleanu",
38
38
  "vlad-uipath"
39
39
  ],
40
- "gitHead": "37388a48f899d8f3e7d590f050c5f00485594638"
40
+ "gitHead": "a335728adbdb02f28308e4f55d8936d0b150444b"
41
41
  }