@uipath/codedapp-tool 1.199.0 → 1.200.0-preview.117
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/browser-strategy-v4n9zxyf.js +99 -0
- package/dist/index-z6sptz1r.js +137 -0
- package/dist/index.js +15 -2148
- package/dist/multipart-parser-kme45c99.js +353 -0
- package/dist/node-strategy-0s9nhcmn.js +63 -0
- package/dist/tool-0gctz400.js +17 -0
- package/dist/tool-1snc64g4.js +11 -0
- package/dist/tool-7ktmqc73.js +1161 -0
- package/dist/tool-k7c9wf29.js +2485 -0
- package/dist/tool-v6rpdr25.js +72383 -0
- package/dist/tool-wckvcay0.js +50 -0
- package/dist/tool-y0g9grx6.js +4452 -0
- package/dist/tool.js +11 -99032
- package/package.json +2 -2
|
@@ -0,0 +1,2485 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getGlobalThis,
|
|
3
|
+
isBrowser
|
|
4
|
+
} from "./tool-0gctz400.js";
|
|
5
|
+
import {
|
|
6
|
+
catchError,
|
|
7
|
+
getFileSystem
|
|
8
|
+
} from "./tool-7ktmqc73.js";
|
|
9
|
+
import {
|
|
10
|
+
AUTH_FILENAME,
|
|
11
|
+
DEFAULT_BASE_URL,
|
|
12
|
+
DEFAULT_REDIRECT_URI,
|
|
13
|
+
UIPATH_HOME_DIR
|
|
14
|
+
} from "./tool-1snc64g4.js";
|
|
15
|
+
import {
|
|
16
|
+
__require
|
|
17
|
+
} from "./tool-wckvcay0.js";
|
|
18
|
+
|
|
19
|
+
// ../auth/src/config.ts
|
|
20
|
+
var DEFAULT_CLIENT_ID = "36dea5b8-e8bb-423d-8e7b-c808df8f1c00";
|
|
21
|
+
var AUTH_FILE_CONFIG_KEY = Symbol.for("@uipath/auth/AuthFileConfig");
|
|
22
|
+
var globalSlot = globalThis;
|
|
23
|
+
var setAuthFileConfig = (cfg) => {
|
|
24
|
+
globalSlot[AUTH_FILE_CONFIG_KEY] = cfg ?? {};
|
|
25
|
+
};
|
|
26
|
+
var getAuthFileConfig = () => globalSlot[AUTH_FILE_CONFIG_KEY] ?? {};
|
|
27
|
+
|
|
28
|
+
class InvalidBaseUrlError extends Error {
|
|
29
|
+
url;
|
|
30
|
+
reason;
|
|
31
|
+
constructor(url, reason) {
|
|
32
|
+
super(`Invalid base URL: "${url}"
|
|
33
|
+
` + `Reason: ${reason}
|
|
34
|
+
|
|
35
|
+
` + `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>).
|
|
36
|
+
` + `You can specify the URL via:
|
|
37
|
+
` + ` • --authority flag
|
|
38
|
+
` + ` • UIPATH_URL environment variable
|
|
39
|
+
` + ` • auth.authority in config file`);
|
|
40
|
+
this.url = url;
|
|
41
|
+
this.reason = reason;
|
|
42
|
+
this.name = "InvalidBaseUrlError";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var DEFAULT_SCOPES = ["openid", "profile", "offline_access"];
|
|
46
|
+
var normalizeAndValidateBaseUrl = (rawUrl) => {
|
|
47
|
+
let baseUrl = rawUrl;
|
|
48
|
+
if (baseUrl.endsWith("/identity_/")) {
|
|
49
|
+
baseUrl = baseUrl.slice(0, -11);
|
|
50
|
+
} else if (baseUrl.endsWith("/identity_")) {
|
|
51
|
+
baseUrl = baseUrl.slice(0, -10);
|
|
52
|
+
}
|
|
53
|
+
while (baseUrl.endsWith("/")) {
|
|
54
|
+
baseUrl = baseUrl.slice(0, -1);
|
|
55
|
+
}
|
|
56
|
+
const resolvedBaseUrl = baseUrl;
|
|
57
|
+
const [urlError, url] = catchError(() => new URL(resolvedBaseUrl));
|
|
58
|
+
if (urlError) {
|
|
59
|
+
throw new InvalidBaseUrlError(baseUrl, `Malformed URL. ${urlError instanceof Error ? urlError.message : "Unknown error"}`);
|
|
60
|
+
}
|
|
61
|
+
if (url.protocol !== "https:") {
|
|
62
|
+
throw new InvalidBaseUrlError(baseUrl, `Authority must use https:// scheme, got ${url.protocol}//. OIDC token exchange requires TLS end-to-end.`);
|
|
63
|
+
}
|
|
64
|
+
return url.pathname.length > 1 ? url.origin : baseUrl;
|
|
65
|
+
};
|
|
66
|
+
var resolveScopes = (isExternalAppAuth, customScopes, fileScopes) => {
|
|
67
|
+
const requestedScopes = customScopes?.length ? customScopes : fileScopes ?? [];
|
|
68
|
+
if (isExternalAppAuth)
|
|
69
|
+
return requestedScopes;
|
|
70
|
+
return [...new Set([...DEFAULT_SCOPES, ...requestedScopes])];
|
|
71
|
+
};
|
|
72
|
+
var resolveConfigAsync = async ({
|
|
73
|
+
customAuthority,
|
|
74
|
+
customClientId,
|
|
75
|
+
customClientSecret,
|
|
76
|
+
customClientAssertion,
|
|
77
|
+
customScopes
|
|
78
|
+
} = {}) => {
|
|
79
|
+
const fileAuth = getAuthFileConfig();
|
|
80
|
+
let baseUrl = customAuthority;
|
|
81
|
+
if (!baseUrl) {
|
|
82
|
+
baseUrl = process.env.UIPATH_URL;
|
|
83
|
+
}
|
|
84
|
+
if (!baseUrl && fileAuth.authority) {
|
|
85
|
+
baseUrl = fileAuth.authority;
|
|
86
|
+
}
|
|
87
|
+
if (!baseUrl) {
|
|
88
|
+
baseUrl = DEFAULT_BASE_URL;
|
|
89
|
+
}
|
|
90
|
+
baseUrl = normalizeAndValidateBaseUrl(baseUrl);
|
|
91
|
+
let clientId = customClientId;
|
|
92
|
+
if (!clientId && fileAuth.clientId) {
|
|
93
|
+
clientId = fileAuth.clientId;
|
|
94
|
+
}
|
|
95
|
+
if (!clientId) {
|
|
96
|
+
clientId = DEFAULT_CLIENT_ID;
|
|
97
|
+
}
|
|
98
|
+
let clientSecret = customClientSecret;
|
|
99
|
+
if (!clientSecret && fileAuth.clientSecret) {
|
|
100
|
+
clientSecret = fileAuth.clientSecret;
|
|
101
|
+
}
|
|
102
|
+
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && (Boolean(clientSecret) || Boolean(customClientAssertion));
|
|
103
|
+
const scopes = resolveScopes(isExternalAppAuth, customScopes, fileAuth.scopes);
|
|
104
|
+
return {
|
|
105
|
+
clientId,
|
|
106
|
+
clientSecret,
|
|
107
|
+
scopes,
|
|
108
|
+
baseUrl,
|
|
109
|
+
authorizationEndpoint: `${baseUrl}/identity_/connect/authorize`,
|
|
110
|
+
tokenEndpoint: `${baseUrl}/identity_/connect/token`
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// ../../node_modules/oauth4webapi/build/index.js
|
|
115
|
+
var USER_AGENT;
|
|
116
|
+
if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
|
|
117
|
+
const NAME = "oauth4webapi";
|
|
118
|
+
const VERSION = "v3.8.6";
|
|
119
|
+
USER_AGENT = `${NAME}/${VERSION}`;
|
|
120
|
+
}
|
|
121
|
+
var ERR_INVALID_ARG_VALUE = "ERR_INVALID_ARG_VALUE";
|
|
122
|
+
var ERR_INVALID_ARG_TYPE = "ERR_INVALID_ARG_TYPE";
|
|
123
|
+
function CodedTypeError(message, code, cause) {
|
|
124
|
+
const err = new TypeError(message, { cause });
|
|
125
|
+
Object.assign(err, { code });
|
|
126
|
+
return err;
|
|
127
|
+
}
|
|
128
|
+
var allowInsecureRequests = Symbol();
|
|
129
|
+
var clockSkew = Symbol();
|
|
130
|
+
var clockTolerance = Symbol();
|
|
131
|
+
var customFetch = Symbol();
|
|
132
|
+
var modifyAssertion = Symbol();
|
|
133
|
+
var jweDecrypt = Symbol();
|
|
134
|
+
var jwksCache = Symbol();
|
|
135
|
+
var encoder = new TextEncoder;
|
|
136
|
+
var decoder = new TextDecoder;
|
|
137
|
+
function buf(input) {
|
|
138
|
+
if (typeof input === "string") {
|
|
139
|
+
return encoder.encode(input);
|
|
140
|
+
}
|
|
141
|
+
return decoder.decode(input);
|
|
142
|
+
}
|
|
143
|
+
var encodeBase64Url;
|
|
144
|
+
if (Uint8Array.prototype.toBase64) {
|
|
145
|
+
encodeBase64Url = (input) => {
|
|
146
|
+
if (input instanceof ArrayBuffer) {
|
|
147
|
+
input = new Uint8Array(input);
|
|
148
|
+
}
|
|
149
|
+
return input.toBase64({ alphabet: "base64url", omitPadding: true });
|
|
150
|
+
};
|
|
151
|
+
} else {
|
|
152
|
+
const CHUNK_SIZE = 32768;
|
|
153
|
+
encodeBase64Url = (input) => {
|
|
154
|
+
if (input instanceof ArrayBuffer) {
|
|
155
|
+
input = new Uint8Array(input);
|
|
156
|
+
}
|
|
157
|
+
const arr = [];
|
|
158
|
+
for (let i = 0;i < input.byteLength; i += CHUNK_SIZE) {
|
|
159
|
+
arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
|
|
160
|
+
}
|
|
161
|
+
return btoa(arr.join("")).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
var decodeBase64Url;
|
|
165
|
+
if (Uint8Array.fromBase64) {
|
|
166
|
+
decodeBase64Url = (input) => {
|
|
167
|
+
try {
|
|
168
|
+
return Uint8Array.fromBase64(input, { alphabet: "base64url" });
|
|
169
|
+
} catch (cause) {
|
|
170
|
+
throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
} else {
|
|
174
|
+
decodeBase64Url = (input) => {
|
|
175
|
+
try {
|
|
176
|
+
const binary = atob(input.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""));
|
|
177
|
+
const bytes = new Uint8Array(binary.length);
|
|
178
|
+
for (let i = 0;i < binary.length; i++) {
|
|
179
|
+
bytes[i] = binary.charCodeAt(i);
|
|
180
|
+
}
|
|
181
|
+
return bytes;
|
|
182
|
+
} catch (cause) {
|
|
183
|
+
throw CodedTypeError("The input to be decoded is not correctly encoded.", ERR_INVALID_ARG_VALUE, cause);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function b64u(input) {
|
|
188
|
+
if (typeof input === "string") {
|
|
189
|
+
return decodeBase64Url(input);
|
|
190
|
+
}
|
|
191
|
+
return encodeBase64Url(input);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class UnsupportedOperationError extends Error {
|
|
195
|
+
code;
|
|
196
|
+
constructor(message, options) {
|
|
197
|
+
super(message, options);
|
|
198
|
+
this.name = this.constructor.name;
|
|
199
|
+
this.code = UNSUPPORTED_OPERATION;
|
|
200
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
class OperationProcessingError extends Error {
|
|
205
|
+
code;
|
|
206
|
+
constructor(message, options) {
|
|
207
|
+
super(message, options);
|
|
208
|
+
this.name = this.constructor.name;
|
|
209
|
+
if (options?.code) {
|
|
210
|
+
this.code = options?.code;
|
|
211
|
+
}
|
|
212
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function OPE(message, code, cause) {
|
|
216
|
+
return new OperationProcessingError(message, { code, cause });
|
|
217
|
+
}
|
|
218
|
+
async function calculateJwkThumbprint(jwk) {
|
|
219
|
+
let components;
|
|
220
|
+
switch (jwk.kty) {
|
|
221
|
+
case "EC":
|
|
222
|
+
components = {
|
|
223
|
+
crv: jwk.crv,
|
|
224
|
+
kty: jwk.kty,
|
|
225
|
+
x: jwk.x,
|
|
226
|
+
y: jwk.y
|
|
227
|
+
};
|
|
228
|
+
break;
|
|
229
|
+
case "OKP":
|
|
230
|
+
components = {
|
|
231
|
+
crv: jwk.crv,
|
|
232
|
+
kty: jwk.kty,
|
|
233
|
+
x: jwk.x
|
|
234
|
+
};
|
|
235
|
+
break;
|
|
236
|
+
case "AKP":
|
|
237
|
+
components = {
|
|
238
|
+
alg: jwk.alg,
|
|
239
|
+
kty: jwk.kty,
|
|
240
|
+
pub: jwk.pub
|
|
241
|
+
};
|
|
242
|
+
break;
|
|
243
|
+
case "RSA":
|
|
244
|
+
components = {
|
|
245
|
+
e: jwk.e,
|
|
246
|
+
kty: jwk.kty,
|
|
247
|
+
n: jwk.n
|
|
248
|
+
};
|
|
249
|
+
break;
|
|
250
|
+
default:
|
|
251
|
+
throw new UnsupportedOperationError("unsupported JWK key type", { cause: jwk });
|
|
252
|
+
}
|
|
253
|
+
return b64u(await crypto.subtle.digest("SHA-256", buf(JSON.stringify(components))));
|
|
254
|
+
}
|
|
255
|
+
function assertCryptoKey(key, it) {
|
|
256
|
+
if (!(key instanceof CryptoKey)) {
|
|
257
|
+
throw CodedTypeError(`${it} must be a CryptoKey`, ERR_INVALID_ARG_TYPE);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function assertPrivateKey(key, it) {
|
|
261
|
+
assertCryptoKey(key, it);
|
|
262
|
+
if (key.type !== "private") {
|
|
263
|
+
throw CodedTypeError(`${it} must be a private CryptoKey`, ERR_INVALID_ARG_VALUE);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function assertPublicKey(key, it) {
|
|
267
|
+
assertCryptoKey(key, it);
|
|
268
|
+
if (key.type !== "public") {
|
|
269
|
+
throw CodedTypeError(`${it} must be a public CryptoKey`, ERR_INVALID_ARG_VALUE);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function assertString(input, it, code, cause) {
|
|
273
|
+
try {
|
|
274
|
+
if (typeof input !== "string") {
|
|
275
|
+
throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE, cause);
|
|
276
|
+
}
|
|
277
|
+
if (input.length === 0) {
|
|
278
|
+
throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE, cause);
|
|
279
|
+
}
|
|
280
|
+
} catch (err) {
|
|
281
|
+
if (code) {
|
|
282
|
+
throw OPE(err.message, code, cause);
|
|
283
|
+
}
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function randomBytes() {
|
|
288
|
+
return b64u(crypto.getRandomValues(new Uint8Array(32)));
|
|
289
|
+
}
|
|
290
|
+
function generateRandomCodeVerifier() {
|
|
291
|
+
return randomBytes();
|
|
292
|
+
}
|
|
293
|
+
function generateRandomState() {
|
|
294
|
+
return randomBytes();
|
|
295
|
+
}
|
|
296
|
+
async function calculatePKCECodeChallenge(codeVerifier) {
|
|
297
|
+
assertString(codeVerifier, "codeVerifier");
|
|
298
|
+
return b64u(await crypto.subtle.digest("SHA-256", buf(codeVerifier)));
|
|
299
|
+
}
|
|
300
|
+
function psAlg(key) {
|
|
301
|
+
switch (key.algorithm.hash.name) {
|
|
302
|
+
case "SHA-256":
|
|
303
|
+
return "PS256";
|
|
304
|
+
case "SHA-384":
|
|
305
|
+
return "PS384";
|
|
306
|
+
case "SHA-512":
|
|
307
|
+
return "PS512";
|
|
308
|
+
default:
|
|
309
|
+
throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
|
|
310
|
+
cause: key
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function rsAlg(key) {
|
|
315
|
+
switch (key.algorithm.hash.name) {
|
|
316
|
+
case "SHA-256":
|
|
317
|
+
return "RS256";
|
|
318
|
+
case "SHA-384":
|
|
319
|
+
return "RS384";
|
|
320
|
+
case "SHA-512":
|
|
321
|
+
return "RS512";
|
|
322
|
+
default:
|
|
323
|
+
throw new UnsupportedOperationError("unsupported RsaHashedKeyAlgorithm hash name", {
|
|
324
|
+
cause: key
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function esAlg(key) {
|
|
329
|
+
switch (key.algorithm.namedCurve) {
|
|
330
|
+
case "P-256":
|
|
331
|
+
return "ES256";
|
|
332
|
+
case "P-384":
|
|
333
|
+
return "ES384";
|
|
334
|
+
case "P-521":
|
|
335
|
+
return "ES512";
|
|
336
|
+
default:
|
|
337
|
+
throw new UnsupportedOperationError("unsupported EcKeyAlgorithm namedCurve", { cause: key });
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function keyToJws(key) {
|
|
341
|
+
switch (key.algorithm.name) {
|
|
342
|
+
case "RSA-PSS":
|
|
343
|
+
return psAlg(key);
|
|
344
|
+
case "RSASSA-PKCS1-v1_5":
|
|
345
|
+
return rsAlg(key);
|
|
346
|
+
case "ECDSA":
|
|
347
|
+
return esAlg(key);
|
|
348
|
+
case "Ed25519":
|
|
349
|
+
case "ML-DSA-44":
|
|
350
|
+
case "ML-DSA-65":
|
|
351
|
+
case "ML-DSA-87":
|
|
352
|
+
return key.algorithm.name;
|
|
353
|
+
case "EdDSA":
|
|
354
|
+
return "Ed25519";
|
|
355
|
+
default:
|
|
356
|
+
throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function getClockSkew(client) {
|
|
360
|
+
const skew = client?.[clockSkew];
|
|
361
|
+
return typeof skew === "number" && Number.isFinite(skew) ? skew : 0;
|
|
362
|
+
}
|
|
363
|
+
function epochTime() {
|
|
364
|
+
return Math.floor(Date.now() / 1000);
|
|
365
|
+
}
|
|
366
|
+
function ClientSecretPost(clientSecret) {
|
|
367
|
+
assertString(clientSecret, '"clientSecret"');
|
|
368
|
+
return (_as, client, body, _headers) => {
|
|
369
|
+
body.set("client_id", client.client_id);
|
|
370
|
+
body.set("client_secret", clientSecret);
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function None() {
|
|
374
|
+
return (_as, client, body, _headers) => {
|
|
375
|
+
body.set("client_id", client.client_id);
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async function signJwt(header, payload, key) {
|
|
379
|
+
if (!key.usages.includes("sign")) {
|
|
380
|
+
throw CodedTypeError('CryptoKey instances used for signing assertions must include "sign" in their "usages"', ERR_INVALID_ARG_VALUE);
|
|
381
|
+
}
|
|
382
|
+
const input = `${b64u(buf(JSON.stringify(header)))}.${b64u(buf(JSON.stringify(payload)))}`;
|
|
383
|
+
const signature = b64u(await crypto.subtle.sign(keyToSubtle(key), key, buf(input)));
|
|
384
|
+
return `${input}.${signature}`;
|
|
385
|
+
}
|
|
386
|
+
var jwkCache;
|
|
387
|
+
async function getSetPublicJwkCache(key, alg) {
|
|
388
|
+
const { kty, e, n, x, y, crv, pub } = await crypto.subtle.exportKey("jwk", key);
|
|
389
|
+
const jwk = { kty, e, n, x, y, crv, pub };
|
|
390
|
+
if (kty === "AKP")
|
|
391
|
+
jwk.alg = alg;
|
|
392
|
+
jwkCache.set(key, jwk);
|
|
393
|
+
return jwk;
|
|
394
|
+
}
|
|
395
|
+
async function publicJwk(key, alg) {
|
|
396
|
+
jwkCache ||= new WeakMap;
|
|
397
|
+
return jwkCache.get(key) || getSetPublicJwkCache(key, alg);
|
|
398
|
+
}
|
|
399
|
+
var URLParse = URL.parse ? (url, base) => URL.parse(url, base) : (url, base) => {
|
|
400
|
+
try {
|
|
401
|
+
return new URL(url, base);
|
|
402
|
+
} catch {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
class DPoPHandler {
|
|
407
|
+
#header;
|
|
408
|
+
#privateKey;
|
|
409
|
+
#publicKey;
|
|
410
|
+
#clockSkew;
|
|
411
|
+
#modifyAssertion;
|
|
412
|
+
#map;
|
|
413
|
+
#jkt;
|
|
414
|
+
constructor(client, keyPair, options) {
|
|
415
|
+
assertPrivateKey(keyPair?.privateKey, '"DPoP.privateKey"');
|
|
416
|
+
assertPublicKey(keyPair?.publicKey, '"DPoP.publicKey"');
|
|
417
|
+
if (!keyPair.publicKey.extractable) {
|
|
418
|
+
throw CodedTypeError('"DPoP.publicKey.extractable" must be true', ERR_INVALID_ARG_VALUE);
|
|
419
|
+
}
|
|
420
|
+
this.#modifyAssertion = options?.[modifyAssertion];
|
|
421
|
+
this.#clockSkew = getClockSkew(client);
|
|
422
|
+
this.#privateKey = keyPair.privateKey;
|
|
423
|
+
this.#publicKey = keyPair.publicKey;
|
|
424
|
+
branded.add(this);
|
|
425
|
+
}
|
|
426
|
+
#get(key) {
|
|
427
|
+
this.#map ||= new Map;
|
|
428
|
+
let item = this.#map.get(key);
|
|
429
|
+
if (item) {
|
|
430
|
+
this.#map.delete(key);
|
|
431
|
+
this.#map.set(key, item);
|
|
432
|
+
}
|
|
433
|
+
return item;
|
|
434
|
+
}
|
|
435
|
+
#set(key, val) {
|
|
436
|
+
this.#map ||= new Map;
|
|
437
|
+
this.#map.delete(key);
|
|
438
|
+
if (this.#map.size === 100) {
|
|
439
|
+
this.#map.delete(this.#map.keys().next().value);
|
|
440
|
+
}
|
|
441
|
+
this.#map.set(key, val);
|
|
442
|
+
}
|
|
443
|
+
async calculateThumbprint() {
|
|
444
|
+
if (!this.#jkt) {
|
|
445
|
+
const jwk = await crypto.subtle.exportKey("jwk", this.#publicKey);
|
|
446
|
+
this.#jkt ||= await calculateJwkThumbprint(jwk);
|
|
447
|
+
}
|
|
448
|
+
return this.#jkt;
|
|
449
|
+
}
|
|
450
|
+
async addProof(url, headers, htm, accessToken) {
|
|
451
|
+
const alg = keyToJws(this.#privateKey);
|
|
452
|
+
this.#header ||= {
|
|
453
|
+
alg,
|
|
454
|
+
typ: "dpop+jwt",
|
|
455
|
+
jwk: await publicJwk(this.#publicKey, alg)
|
|
456
|
+
};
|
|
457
|
+
const nonce = this.#get(url.origin);
|
|
458
|
+
const now = epochTime() + this.#clockSkew;
|
|
459
|
+
const payload = {
|
|
460
|
+
iat: now,
|
|
461
|
+
jti: randomBytes(),
|
|
462
|
+
htm,
|
|
463
|
+
nonce,
|
|
464
|
+
htu: `${url.origin}${url.pathname}`,
|
|
465
|
+
ath: accessToken ? b64u(await crypto.subtle.digest("SHA-256", buf(accessToken))) : undefined
|
|
466
|
+
};
|
|
467
|
+
this.#modifyAssertion?.(this.#header, payload);
|
|
468
|
+
headers.set("dpop", await signJwt(this.#header, payload, this.#privateKey));
|
|
469
|
+
}
|
|
470
|
+
cacheNonce(response, url) {
|
|
471
|
+
try {
|
|
472
|
+
const nonce = response.headers.get("dpop-nonce");
|
|
473
|
+
if (nonce) {
|
|
474
|
+
this.#set(url.origin, nonce);
|
|
475
|
+
}
|
|
476
|
+
} catch {}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
var tokenMatch = "[a-zA-Z0-9!#$%&\\'\\*\\+\\-\\.\\^_`\\|~]+";
|
|
480
|
+
var token68Match = "[a-zA-Z0-9\\-\\._\\~\\+\\/]+={0,2}";
|
|
481
|
+
var quotedMatch = '"((?:[^"\\\\]|\\\\[\\s\\S])*)"';
|
|
482
|
+
var quotedParamMatcher = "(" + tokenMatch + ")\\s*=\\s*" + quotedMatch;
|
|
483
|
+
var paramMatcher = "(" + tokenMatch + ")\\s*=\\s*(" + tokenMatch + ")";
|
|
484
|
+
var schemeRE = new RegExp("^[,\\s]*(" + tokenMatch + ")");
|
|
485
|
+
var quotedParamRE = new RegExp("^[,\\s]*" + quotedParamMatcher + "[,\\s]*(.*)");
|
|
486
|
+
var unquotedParamRE = new RegExp("^[,\\s]*" + paramMatcher + "[,\\s]*(.*)");
|
|
487
|
+
var token68ParamRE = new RegExp("^(" + token68Match + ")(?:$|[,\\s])(.*)");
|
|
488
|
+
var skipSubjectCheck = Symbol();
|
|
489
|
+
var idTokenClaims = new WeakMap;
|
|
490
|
+
var jwtRefs = new WeakMap;
|
|
491
|
+
var branded = new WeakSet;
|
|
492
|
+
var nopkce = Symbol();
|
|
493
|
+
var expectNoNonce = Symbol();
|
|
494
|
+
var skipAuthTimeCheck = Symbol();
|
|
495
|
+
var UNSUPPORTED_OPERATION = "OAUTH_UNSUPPORTED_OPERATION";
|
|
496
|
+
function checkRsaKeyAlgorithm(key) {
|
|
497
|
+
const { algorithm } = key;
|
|
498
|
+
if (typeof algorithm.modulusLength !== "number" || algorithm.modulusLength < 2048) {
|
|
499
|
+
throw new UnsupportedOperationError(`unsupported ${algorithm.name} modulusLength`, {
|
|
500
|
+
cause: key
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function ecdsaHashName(key) {
|
|
505
|
+
const { algorithm } = key;
|
|
506
|
+
switch (algorithm.namedCurve) {
|
|
507
|
+
case "P-256":
|
|
508
|
+
return "SHA-256";
|
|
509
|
+
case "P-384":
|
|
510
|
+
return "SHA-384";
|
|
511
|
+
case "P-521":
|
|
512
|
+
return "SHA-512";
|
|
513
|
+
default:
|
|
514
|
+
throw new UnsupportedOperationError("unsupported ECDSA namedCurve", { cause: key });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function keyToSubtle(key) {
|
|
518
|
+
switch (key.algorithm.name) {
|
|
519
|
+
case "ECDSA":
|
|
520
|
+
return {
|
|
521
|
+
name: key.algorithm.name,
|
|
522
|
+
hash: ecdsaHashName(key)
|
|
523
|
+
};
|
|
524
|
+
case "RSA-PSS": {
|
|
525
|
+
checkRsaKeyAlgorithm(key);
|
|
526
|
+
switch (key.algorithm.hash.name) {
|
|
527
|
+
case "SHA-256":
|
|
528
|
+
case "SHA-384":
|
|
529
|
+
case "SHA-512":
|
|
530
|
+
return {
|
|
531
|
+
name: key.algorithm.name,
|
|
532
|
+
saltLength: parseInt(key.algorithm.hash.name.slice(-3), 10) >> 3
|
|
533
|
+
};
|
|
534
|
+
default:
|
|
535
|
+
throw new UnsupportedOperationError("unsupported RSA-PSS hash name", { cause: key });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
case "RSASSA-PKCS1-v1_5":
|
|
539
|
+
checkRsaKeyAlgorithm(key);
|
|
540
|
+
return key.algorithm.name;
|
|
541
|
+
case "ML-DSA-44":
|
|
542
|
+
case "ML-DSA-65":
|
|
543
|
+
case "ML-DSA-87":
|
|
544
|
+
case "Ed25519":
|
|
545
|
+
return key.algorithm.name;
|
|
546
|
+
}
|
|
547
|
+
throw new UnsupportedOperationError("unsupported CryptoKey algorithm name", { cause: key });
|
|
548
|
+
}
|
|
549
|
+
var skipStateCheck = Symbol();
|
|
550
|
+
var expectNoState = Symbol();
|
|
551
|
+
var _nodiscoverycheck = Symbol();
|
|
552
|
+
var _expectedIssuer = Symbol();
|
|
553
|
+
|
|
554
|
+
// ../../node_modules/openid-client/build/index.js
|
|
555
|
+
var headers;
|
|
556
|
+
var USER_AGENT2;
|
|
557
|
+
if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
|
|
558
|
+
const NAME = "openid-client";
|
|
559
|
+
const VERSION = "v6.8.4";
|
|
560
|
+
USER_AGENT2 = `${NAME}/${VERSION}`;
|
|
561
|
+
headers = { "user-agent": USER_AGENT2 };
|
|
562
|
+
}
|
|
563
|
+
var int = (config) => {
|
|
564
|
+
return props.get(config);
|
|
565
|
+
};
|
|
566
|
+
var props;
|
|
567
|
+
var tbi;
|
|
568
|
+
function ClientSecretPost2(clientSecret) {
|
|
569
|
+
if (clientSecret !== undefined) {
|
|
570
|
+
return ClientSecretPost(clientSecret);
|
|
571
|
+
}
|
|
572
|
+
tbi ||= new WeakMap;
|
|
573
|
+
return (as, client, body, headers2) => {
|
|
574
|
+
let auth;
|
|
575
|
+
if (!(auth = tbi.get(client))) {
|
|
576
|
+
assertString2(client.client_secret, '"metadata.client_secret"');
|
|
577
|
+
auth = ClientSecretPost(client.client_secret);
|
|
578
|
+
tbi.set(client, auth);
|
|
579
|
+
}
|
|
580
|
+
return auth(as, client, body, headers2);
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function assertString2(input, it) {
|
|
584
|
+
if (typeof input !== "string") {
|
|
585
|
+
throw CodedTypeError2(`${it} must be a string`, ERR_INVALID_ARG_TYPE2);
|
|
586
|
+
}
|
|
587
|
+
if (input.length === 0) {
|
|
588
|
+
throw CodedTypeError2(`${it} must not be empty`, ERR_INVALID_ARG_VALUE2);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function None2() {
|
|
592
|
+
return None();
|
|
593
|
+
}
|
|
594
|
+
var customFetch2 = customFetch;
|
|
595
|
+
var ERR_INVALID_ARG_VALUE2 = "ERR_INVALID_ARG_VALUE";
|
|
596
|
+
var ERR_INVALID_ARG_TYPE2 = "ERR_INVALID_ARG_TYPE";
|
|
597
|
+
function CodedTypeError2(message, code, cause) {
|
|
598
|
+
const err = new TypeError(message, { cause });
|
|
599
|
+
Object.assign(err, { code });
|
|
600
|
+
return err;
|
|
601
|
+
}
|
|
602
|
+
function calculatePKCECodeChallenge2(codeVerifier) {
|
|
603
|
+
return calculatePKCECodeChallenge(codeVerifier);
|
|
604
|
+
}
|
|
605
|
+
function randomPKCECodeVerifier() {
|
|
606
|
+
return generateRandomCodeVerifier();
|
|
607
|
+
}
|
|
608
|
+
function randomState() {
|
|
609
|
+
return generateRandomState();
|
|
610
|
+
}
|
|
611
|
+
var decoder2 = new TextDecoder;
|
|
612
|
+
function getServerHelpers(metadata) {
|
|
613
|
+
return {
|
|
614
|
+
supportsPKCE: {
|
|
615
|
+
__proto__: null,
|
|
616
|
+
value(method = "S256") {
|
|
617
|
+
return metadata.code_challenge_methods_supported?.includes(method) === true;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function addServerHelpers(metadata) {
|
|
623
|
+
Object.defineProperties(metadata, getServerHelpers(metadata));
|
|
624
|
+
}
|
|
625
|
+
var kEntraId = Symbol();
|
|
626
|
+
|
|
627
|
+
class Configuration {
|
|
628
|
+
constructor(server, clientId, metadata, clientAuthentication) {
|
|
629
|
+
if (typeof clientId !== "string" || !clientId.length) {
|
|
630
|
+
throw CodedTypeError2('"clientId" must be a non-empty string', ERR_INVALID_ARG_TYPE2);
|
|
631
|
+
}
|
|
632
|
+
if (typeof metadata === "string") {
|
|
633
|
+
metadata = { client_secret: metadata };
|
|
634
|
+
}
|
|
635
|
+
if (metadata?.client_id !== undefined && clientId !== metadata.client_id) {
|
|
636
|
+
throw CodedTypeError2('"clientId" and "metadata.client_id" must be the same', ERR_INVALID_ARG_VALUE2);
|
|
637
|
+
}
|
|
638
|
+
const client = {
|
|
639
|
+
...structuredClone(metadata),
|
|
640
|
+
client_id: clientId
|
|
641
|
+
};
|
|
642
|
+
client[clockSkew] = metadata?.[clockSkew] ?? 0;
|
|
643
|
+
client[clockTolerance] = metadata?.[clockTolerance] ?? 30;
|
|
644
|
+
let auth;
|
|
645
|
+
if (clientAuthentication) {
|
|
646
|
+
auth = clientAuthentication;
|
|
647
|
+
} else {
|
|
648
|
+
if (typeof client.client_secret === "string" && client.client_secret.length) {
|
|
649
|
+
auth = ClientSecretPost2(client.client_secret);
|
|
650
|
+
} else {
|
|
651
|
+
auth = None2();
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
let c = Object.freeze(client);
|
|
655
|
+
const clone = structuredClone(server);
|
|
656
|
+
if (kEntraId in server) {
|
|
657
|
+
clone[_expectedIssuer] = ({ claims: { tid } }) => server.issuer.replace("{tenantid}", tid);
|
|
658
|
+
}
|
|
659
|
+
let as = Object.freeze(clone);
|
|
660
|
+
props ||= new WeakMap;
|
|
661
|
+
props.set(this, {
|
|
662
|
+
__proto__: null,
|
|
663
|
+
as,
|
|
664
|
+
c,
|
|
665
|
+
auth,
|
|
666
|
+
tlsOnly: true,
|
|
667
|
+
jwksCache: {}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
serverMetadata() {
|
|
671
|
+
const metadata = structuredClone(int(this).as);
|
|
672
|
+
addServerHelpers(metadata);
|
|
673
|
+
return metadata;
|
|
674
|
+
}
|
|
675
|
+
clientMetadata() {
|
|
676
|
+
const metadata = structuredClone(int(this).c);
|
|
677
|
+
return metadata;
|
|
678
|
+
}
|
|
679
|
+
get timeout() {
|
|
680
|
+
return int(this).timeout;
|
|
681
|
+
}
|
|
682
|
+
set timeout(value) {
|
|
683
|
+
int(this).timeout = value;
|
|
684
|
+
}
|
|
685
|
+
get [customFetch2]() {
|
|
686
|
+
return int(this).fetch;
|
|
687
|
+
}
|
|
688
|
+
set [customFetch2](value) {
|
|
689
|
+
int(this).fetch = value;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
Object.freeze(Configuration.prototype);
|
|
693
|
+
var retry = Symbol();
|
|
694
|
+
|
|
695
|
+
// ../auth/src/oidc.ts
|
|
696
|
+
var getOidcParams = async () => {
|
|
697
|
+
const code_verifier = randomPKCECodeVerifier();
|
|
698
|
+
const code_challenge = await calculatePKCECodeChallenge2(code_verifier);
|
|
699
|
+
const state = randomState();
|
|
700
|
+
return {
|
|
701
|
+
code_verifier,
|
|
702
|
+
code_challenge,
|
|
703
|
+
state
|
|
704
|
+
};
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// ../auth/src/tokenExchange.ts
|
|
708
|
+
var exchangeCodeForTokens = async ({
|
|
709
|
+
code,
|
|
710
|
+
codeVerifier,
|
|
711
|
+
redirectUri,
|
|
712
|
+
clientId,
|
|
713
|
+
clientSecret,
|
|
714
|
+
tokenEndpoint
|
|
715
|
+
}) => {
|
|
716
|
+
const params = {
|
|
717
|
+
grant_type: "authorization_code",
|
|
718
|
+
code,
|
|
719
|
+
redirect_uri: redirectUri.toString(),
|
|
720
|
+
client_id: clientId,
|
|
721
|
+
code_verifier: codeVerifier
|
|
722
|
+
};
|
|
723
|
+
if (clientSecret) {
|
|
724
|
+
params.client_secret = clientSecret;
|
|
725
|
+
}
|
|
726
|
+
const tokenParams = new URLSearchParams(params);
|
|
727
|
+
const tokenResponse = await fetch(tokenEndpoint, {
|
|
728
|
+
method: "POST",
|
|
729
|
+
headers: {
|
|
730
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
731
|
+
},
|
|
732
|
+
body: tokenParams
|
|
733
|
+
});
|
|
734
|
+
const tokenData = await tokenResponse.json();
|
|
735
|
+
if (!tokenResponse.ok) {
|
|
736
|
+
throw new Error(`Token exchange failed: ${String(tokenData.error ?? "unknown_error")} — ${String(tokenData.error_description ?? "no description")}`);
|
|
737
|
+
}
|
|
738
|
+
const accessToken = tokenData.access_token;
|
|
739
|
+
const refreshToken = tokenData.refresh_token;
|
|
740
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string") {
|
|
741
|
+
throw new Error("Token exchange response is missing access_token or refresh_token");
|
|
742
|
+
}
|
|
743
|
+
return { accessToken, refreshToken };
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
// ../auth/src/authProfile.ts
|
|
747
|
+
var DEFAULT_AUTH_PROFILE = "default";
|
|
748
|
+
var PROFILE_DIR = "profiles";
|
|
749
|
+
var PROFILE_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
750
|
+
var ACTIVE_AUTH_PROFILE_KEY = Symbol.for("@uipath/auth/ActiveAuthProfile");
|
|
751
|
+
var AUTH_PROFILE_STORAGE_KEY = Symbol.for("@uipath/auth/ProfileStorage");
|
|
752
|
+
var globalSlot2 = globalThis;
|
|
753
|
+
function isAuthProfileStorage(value) {
|
|
754
|
+
return value !== null && typeof value === "object" && "getStore" in value && "run" in value;
|
|
755
|
+
}
|
|
756
|
+
function createProfileStorage() {
|
|
757
|
+
const [error, mod] = catchError(() => __require("node:async_hooks"));
|
|
758
|
+
if (error || typeof mod?.AsyncLocalStorage !== "function") {
|
|
759
|
+
return {
|
|
760
|
+
getStore: () => {
|
|
761
|
+
return;
|
|
762
|
+
},
|
|
763
|
+
run: (_store, fn) => fn()
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
return new mod.AsyncLocalStorage;
|
|
767
|
+
}
|
|
768
|
+
function getProfileStorage() {
|
|
769
|
+
const existing = globalSlot2[AUTH_PROFILE_STORAGE_KEY];
|
|
770
|
+
if (isAuthProfileStorage(existing)) {
|
|
771
|
+
return existing;
|
|
772
|
+
}
|
|
773
|
+
const storage = createProfileStorage();
|
|
774
|
+
globalSlot2[AUTH_PROFILE_STORAGE_KEY] = storage;
|
|
775
|
+
return storage;
|
|
776
|
+
}
|
|
777
|
+
var profileStorage = getProfileStorage();
|
|
778
|
+
|
|
779
|
+
class AuthProfileValidationError extends Error {
|
|
780
|
+
constructor(message) {
|
|
781
|
+
super(message);
|
|
782
|
+
this.name = "AuthProfileValidationError";
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
function normalizeAuthProfileName(profile) {
|
|
786
|
+
if (profile === undefined || profile === DEFAULT_AUTH_PROFILE) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if (profile.length === 0 || profile === "." || profile === ".." || !PROFILE_NAME_RE.test(profile)) {
|
|
790
|
+
throw new AuthProfileValidationError(`Invalid profile name "${profile}". Profile names may contain only letters, numbers, '.', '_', and '-'.`);
|
|
791
|
+
}
|
|
792
|
+
return profile;
|
|
793
|
+
}
|
|
794
|
+
function setActiveAuthProfile(profile) {
|
|
795
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
796
|
+
const scopedState = profileStorage.getStore();
|
|
797
|
+
if (scopedState !== undefined) {
|
|
798
|
+
if (normalized === undefined) {
|
|
799
|
+
delete scopedState.profile;
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
scopedState.profile = normalized;
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (normalized === undefined) {
|
|
806
|
+
delete globalSlot2[ACTIVE_AUTH_PROFILE_KEY];
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
globalSlot2[ACTIVE_AUTH_PROFILE_KEY] = { profile: normalized };
|
|
810
|
+
}
|
|
811
|
+
function clearActiveAuthProfile() {
|
|
812
|
+
const scopedState = profileStorage.getStore();
|
|
813
|
+
if (scopedState !== undefined) {
|
|
814
|
+
delete scopedState.profile;
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
delete globalSlot2[ACTIVE_AUTH_PROFILE_KEY];
|
|
818
|
+
}
|
|
819
|
+
function getActiveAuthProfile() {
|
|
820
|
+
const scopedState = profileStorage.getStore();
|
|
821
|
+
if (scopedState !== undefined) {
|
|
822
|
+
return scopedState.profile;
|
|
823
|
+
}
|
|
824
|
+
return globalSlot2[ACTIVE_AUTH_PROFILE_KEY]?.profile;
|
|
825
|
+
}
|
|
826
|
+
function runWithAuthProfile(profile, fn) {
|
|
827
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
828
|
+
return profileStorage.run(normalized === undefined ? {} : { profile: normalized }, fn);
|
|
829
|
+
}
|
|
830
|
+
function resolveAuthProfileFilePath(profile) {
|
|
831
|
+
const normalized = normalizeAuthProfileName(profile);
|
|
832
|
+
if (normalized === undefined) {
|
|
833
|
+
throw new AuthProfileValidationError(`"${DEFAULT_AUTH_PROFILE}" is the built-in profile and does not have a profile file path.`);
|
|
834
|
+
}
|
|
835
|
+
const fs = getFileSystem();
|
|
836
|
+
return fs.path.join(fs.env.homedir(), UIPATH_HOME_DIR, PROFILE_DIR, normalized, AUTH_FILENAME);
|
|
837
|
+
}
|
|
838
|
+
function getActiveAuthProfileFilePath() {
|
|
839
|
+
const profile = getActiveAuthProfile();
|
|
840
|
+
return profile ? resolveAuthProfileFilePath(profile) : undefined;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// ../auth/src/utils/jwt.ts
|
|
844
|
+
class InvalidIssuerError extends Error {
|
|
845
|
+
expected;
|
|
846
|
+
actual;
|
|
847
|
+
constructor(expected, actual) {
|
|
848
|
+
const actualText = actual ?? "<missing>";
|
|
849
|
+
super(`Token issuer does not match the authority used to log in.
|
|
850
|
+
` + `Expected: ${expected}
|
|
851
|
+
` + `Actual: ${actualText}
|
|
852
|
+
|
|
853
|
+
` + `The identity server that issued this token is not the one ` + `you pointed --authority at. Refusing to save credentials.`);
|
|
854
|
+
this.expected = expected;
|
|
855
|
+
this.actual = actual;
|
|
856
|
+
this.name = "InvalidIssuerError";
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
var parseJWT = (token) => {
|
|
860
|
+
try {
|
|
861
|
+
const parts = token.split(".");
|
|
862
|
+
const base64Url = parts[1];
|
|
863
|
+
if (!base64Url) {
|
|
864
|
+
throw new Error("Invalid JWT token format - missing payload section");
|
|
865
|
+
}
|
|
866
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
867
|
+
let decodedString;
|
|
868
|
+
if (isBrowser() && typeof atob !== "undefined") {
|
|
869
|
+
decodedString = atob(base64);
|
|
870
|
+
} else {
|
|
871
|
+
decodedString = Buffer.from(base64, "base64").toString();
|
|
872
|
+
}
|
|
873
|
+
const jsonPayload = decodeURIComponent(decodedString.split("").map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`).join(""));
|
|
874
|
+
const parsed = JSON.parse(jsonPayload);
|
|
875
|
+
return parsed;
|
|
876
|
+
} catch (error) {
|
|
877
|
+
throw new Error(`Failed to parse JWT: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
var assertIssuerMatchesAuthority = (token, authority) => {
|
|
881
|
+
let payload;
|
|
882
|
+
try {
|
|
883
|
+
payload = parseJWT(token);
|
|
884
|
+
} catch (error) {
|
|
885
|
+
throw new InvalidIssuerError(`${authority.replace(/\/+$/, "")}/identity_`, `<unparseable: ${error instanceof Error ? error.message : "unknown error"}>`);
|
|
886
|
+
}
|
|
887
|
+
const stripTrailingSlash = (s) => s.replace(/\/+$/, "");
|
|
888
|
+
const expected = `${stripTrailingSlash(authority)}/identity_`;
|
|
889
|
+
const actual = typeof payload.iss === "string" ? stripTrailingSlash(payload.iss) : undefined;
|
|
890
|
+
if (actual !== expected) {
|
|
891
|
+
throw new InvalidIssuerError(expected, payload.iss);
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
var getTokenExpiration = (accessToken) => {
|
|
895
|
+
try {
|
|
896
|
+
const parts = accessToken.split(".");
|
|
897
|
+
if (parts.length !== 3) {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const payload = parts[1];
|
|
901
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
902
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
903
|
+
const decoded = atob(padded);
|
|
904
|
+
const claims = JSON.parse(decoded);
|
|
905
|
+
if (typeof claims.exp !== "number") {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
return new Date(claims.exp * 1000);
|
|
909
|
+
} catch {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
// ../auth/src/envAuth.ts
|
|
915
|
+
var ENV_AUTH_ENABLE_VAR = "UIPATH_CLI_ENABLE_ENV_AUTH";
|
|
916
|
+
var ENFORCE_ROBOT_AUTH_VAR = "UIPATH_CLI_ENFORCE_ROBOT_AUTH";
|
|
917
|
+
var ENV_AUTH_VARS = {
|
|
918
|
+
token: "UIPATH_CLI_AUTH_TOKEN",
|
|
919
|
+
organizationName: "UIPATH_CLI_ORGANIZATION_NAME",
|
|
920
|
+
organizationId: "UIPATH_CLI_ORGANIZATION_ID",
|
|
921
|
+
tenantName: "UIPATH_CLI_TENANT_NAME",
|
|
922
|
+
tenantId: "UIPATH_CLI_TENANT_ID"
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
class EnvAuthConfigError extends Error {
|
|
926
|
+
constructor(message) {
|
|
927
|
+
super(message);
|
|
928
|
+
this.name = "EnvAuthConfigError";
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
var isEnvAuthEnabled = () => process.env[ENV_AUTH_ENABLE_VAR] === "true";
|
|
932
|
+
var isRobotAuthEnforced = () => process.env[ENFORCE_ROBOT_AUTH_VAR] === "true";
|
|
933
|
+
var requireEnv = (name) => {
|
|
934
|
+
const value = process.env[name];
|
|
935
|
+
if (!value) {
|
|
936
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true but ${name} is not set. ` + `Set ${name} to enable env-var authentication.`);
|
|
937
|
+
}
|
|
938
|
+
return value;
|
|
939
|
+
};
|
|
940
|
+
var readAuthFromEnv = () => {
|
|
941
|
+
const accessToken = requireEnv(ENV_AUTH_VARS.token);
|
|
942
|
+
const organizationName = requireEnv(ENV_AUTH_VARS.organizationName);
|
|
943
|
+
const organizationId = requireEnv(ENV_AUTH_VARS.organizationId);
|
|
944
|
+
const tenantName = requireEnv(ENV_AUTH_VARS.tenantName);
|
|
945
|
+
const tenantId = requireEnv(ENV_AUTH_VARS.tenantId);
|
|
946
|
+
const [parseError, payload] = catchError(() => parseJWT(accessToken));
|
|
947
|
+
if (parseError) {
|
|
948
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_VARS.token} is not a valid JWT: ` + `${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
949
|
+
}
|
|
950
|
+
const iss = payload.iss;
|
|
951
|
+
if (typeof iss !== "string" || iss.length === 0) {
|
|
952
|
+
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.`);
|
|
953
|
+
}
|
|
954
|
+
const [baseUrlError, baseUrl] = catchError(() => normalizeAndValidateBaseUrl(iss));
|
|
955
|
+
if (baseUrlError) {
|
|
956
|
+
if (baseUrlError instanceof InvalidBaseUrlError) {
|
|
957
|
+
throw baseUrlError;
|
|
958
|
+
}
|
|
959
|
+
throw new EnvAuthConfigError(`Failed to derive server URL from token 'iss' claim: ` + `${baseUrlError instanceof Error ? baseUrlError.message : String(baseUrlError)}`);
|
|
960
|
+
}
|
|
961
|
+
const expiration = getTokenExpiration(accessToken);
|
|
962
|
+
const loginStatus = expiration && expiration <= new Date ? "Expired" : "Logged in";
|
|
963
|
+
return {
|
|
964
|
+
loginStatus,
|
|
965
|
+
accessToken,
|
|
966
|
+
baseUrl,
|
|
967
|
+
organizationName,
|
|
968
|
+
organizationId,
|
|
969
|
+
tenantName,
|
|
970
|
+
tenantId,
|
|
971
|
+
expiration,
|
|
972
|
+
source: "env" /* Env */
|
|
973
|
+
};
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// ../auth/src/refreshCircuitBreaker.ts
|
|
977
|
+
var BREAKER_SUFFIX = ".refresh-state";
|
|
978
|
+
var BACKOFF_BASE_MS = 60000;
|
|
979
|
+
var BACKOFF_CAP_MS = 60 * 60 * 1000;
|
|
980
|
+
var SURFACE_WINDOW_MS = 60 * 60 * 1000;
|
|
981
|
+
async function refreshTokenFingerprint(refreshToken) {
|
|
982
|
+
const bytes = new TextEncoder().encode(refreshToken);
|
|
983
|
+
if (globalThis.crypto?.subtle) {
|
|
984
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
985
|
+
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
986
|
+
}
|
|
987
|
+
const { createHash } = await import("node:crypto");
|
|
988
|
+
return createHash("sha256").update(refreshToken).digest("hex").slice(0, 16);
|
|
989
|
+
}
|
|
990
|
+
function breakerPathFor(authPath) {
|
|
991
|
+
return `${authPath}${BREAKER_SUFFIX}`;
|
|
992
|
+
}
|
|
993
|
+
async function loadRefreshBreaker(authPath) {
|
|
994
|
+
const fs = getFileSystem();
|
|
995
|
+
try {
|
|
996
|
+
const content = await fs.readFile(breakerPathFor(authPath), "utf-8");
|
|
997
|
+
if (!content)
|
|
998
|
+
return {};
|
|
999
|
+
const parsed = JSON.parse(content);
|
|
1000
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1001
|
+
} catch {
|
|
1002
|
+
return {};
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
async function saveRefreshBreaker(authPath, state) {
|
|
1006
|
+
try {
|
|
1007
|
+
const fs = getFileSystem();
|
|
1008
|
+
const path = breakerPathFor(authPath);
|
|
1009
|
+
await fs.mkdir(fs.path.dirname(path));
|
|
1010
|
+
const tempPath = `${path}.tmp`;
|
|
1011
|
+
await fs.writeFile(tempPath, JSON.stringify(state));
|
|
1012
|
+
await fs.rename(tempPath, path);
|
|
1013
|
+
} catch {}
|
|
1014
|
+
}
|
|
1015
|
+
async function clearRefreshBreaker(authPath) {
|
|
1016
|
+
const fs = getFileSystem();
|
|
1017
|
+
const path = breakerPathFor(authPath);
|
|
1018
|
+
try {
|
|
1019
|
+
if (await fs.exists(path)) {
|
|
1020
|
+
await fs.rm(path);
|
|
1021
|
+
}
|
|
1022
|
+
} catch {}
|
|
1023
|
+
}
|
|
1024
|
+
function nextBackoffMs(attempts) {
|
|
1025
|
+
const shift = Math.max(0, attempts - 1);
|
|
1026
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** shift, BACKOFF_CAP_MS);
|
|
1027
|
+
}
|
|
1028
|
+
function shouldSurface(state, nowMs) {
|
|
1029
|
+
if (state.lastSurfacedAtMs === undefined)
|
|
1030
|
+
return true;
|
|
1031
|
+
return nowMs - state.lastSurfacedAtMs >= SURFACE_WINDOW_MS;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// ../auth/src/robotClientFallback.ts
|
|
1035
|
+
var DEFAULT_TIMEOUT_MS = 1000;
|
|
1036
|
+
var CLOSE_TIMEOUT_MS = 500;
|
|
1037
|
+
var ROBOT_USER_SERVICES_PIPE = "UiPathUserServices";
|
|
1038
|
+
var ROBOT_USER_SERVICES_ALTERNATE_PIPE = `${ROBOT_USER_SERVICES_PIPE}Alternate`;
|
|
1039
|
+
var PIPE_NAME_MAX_LENGTH = 103;
|
|
1040
|
+
var getRobotIpcPipeNames = async () => {
|
|
1041
|
+
const fs = getFileSystem();
|
|
1042
|
+
const username = fs.env.getenv("USER") ?? fs.env.getenv("USERNAME");
|
|
1043
|
+
if (!username) {
|
|
1044
|
+
throw new Error("Unable to determine current username");
|
|
1045
|
+
}
|
|
1046
|
+
const tempPath = fs.env.getenv("TMPDIR") ?? "/tmp/";
|
|
1047
|
+
return [ROBOT_USER_SERVICES_PIPE, ROBOT_USER_SERVICES_ALTERNATE_PIPE].map((baseName) => fs.path.join(tempPath, `${baseName}_${username}`).substring(0, PIPE_NAME_MAX_LENGTH));
|
|
1048
|
+
};
|
|
1049
|
+
var defaultIsRobotIpcAvailable = async () => {
|
|
1050
|
+
if (process.platform === "win32") {
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
const [pipeNamesError, pipeNames] = await catchError(getRobotIpcPipeNames());
|
|
1054
|
+
if (pipeNamesError || !pipeNames) {
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
const fs = getFileSystem();
|
|
1058
|
+
for (const pipeName of pipeNames) {
|
|
1059
|
+
const [existsError, exists] = await catchError(fs.exists(pipeName));
|
|
1060
|
+
if (!existsError && exists === true) {
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return false;
|
|
1065
|
+
};
|
|
1066
|
+
var withTimeout = (promise, timeoutMs) => new Promise((resolve, reject) => {
|
|
1067
|
+
const timer = setTimeout(() => reject(new Error(`Robot IPC call timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1068
|
+
promise.then((value) => {
|
|
1069
|
+
clearTimeout(timer);
|
|
1070
|
+
resolve(value);
|
|
1071
|
+
}, (error) => {
|
|
1072
|
+
clearTimeout(timer);
|
|
1073
|
+
reject(error);
|
|
1074
|
+
});
|
|
1075
|
+
});
|
|
1076
|
+
var parseResourceUrl = (url) => {
|
|
1077
|
+
const [error, parsed] = catchError(() => new URL(url));
|
|
1078
|
+
if (error || !parsed)
|
|
1079
|
+
return;
|
|
1080
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
1081
|
+
return {
|
|
1082
|
+
baseUrl: parsed.origin,
|
|
1083
|
+
organizationName: segments[0],
|
|
1084
|
+
tenantName: segments[1]
|
|
1085
|
+
};
|
|
1086
|
+
};
|
|
1087
|
+
var ROBOT_CLIENT_LOADER_KEY = Symbol.for("@uipath/auth/RobotClientLoader");
|
|
1088
|
+
var registerRobotClientLoader = (loader) => {
|
|
1089
|
+
globalThis[ROBOT_CLIENT_LOADER_KEY] = loader;
|
|
1090
|
+
};
|
|
1091
|
+
var getRegisteredRobotClientLoader = () => {
|
|
1092
|
+
const loader = globalThis[ROBOT_CLIENT_LOADER_KEY];
|
|
1093
|
+
return typeof loader === "function" ? loader : undefined;
|
|
1094
|
+
};
|
|
1095
|
+
var defaultLoadModule = async () => {
|
|
1096
|
+
const hostLoader = getRegisteredRobotClientLoader();
|
|
1097
|
+
if (!hostLoader) {
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
const [error, mod] = await catchError(() => hostLoader());
|
|
1101
|
+
if (error || !mod) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
return mod;
|
|
1105
|
+
};
|
|
1106
|
+
var tryRobotClientFallback = async (options = {}) => {
|
|
1107
|
+
if (isBrowser())
|
|
1108
|
+
return;
|
|
1109
|
+
if (!options.force) {
|
|
1110
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
if (process.env.UIPATH_URL) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1118
|
+
const isRobotIpcAvailable = options.isRobotIpcAvailable ?? defaultIsRobotIpcAvailable;
|
|
1119
|
+
const loadModule = options.loadModule ?? defaultLoadModule;
|
|
1120
|
+
if (!await isRobotIpcAvailable()) {
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const mod = await loadModule();
|
|
1124
|
+
if (!mod)
|
|
1125
|
+
return;
|
|
1126
|
+
const [ctorError, proxy] = catchError(() => new mod.RobotProxyConstructor);
|
|
1127
|
+
if (ctorError || !proxy) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
try {
|
|
1131
|
+
const enabled = await withTimeout(proxy.interactiveConnectFlow.IsEnabled(), timeoutMs);
|
|
1132
|
+
if (!enabled) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
const [resourceUrl, accessToken] = await Promise.all([
|
|
1136
|
+
withTimeout(proxy.accessProvider.GetResourceUrl("Orchestrator"), timeoutMs),
|
|
1137
|
+
withTimeout(proxy.accessProvider.GetAccessToken("Orchestrator", false), timeoutMs)
|
|
1138
|
+
]);
|
|
1139
|
+
if (!accessToken) {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
const parsedUrl = parseResourceUrl(resourceUrl);
|
|
1143
|
+
if (!parsedUrl) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
let organizationIdFromToken;
|
|
1147
|
+
let tenantIdFromToken;
|
|
1148
|
+
let issuerFromToken;
|
|
1149
|
+
const [jwtError, claims] = catchError(() => parseJWT(accessToken));
|
|
1150
|
+
if (!jwtError && claims) {
|
|
1151
|
+
const rawOrgId = claims.prtId ?? claims.organizationId ?? claims.prt_id;
|
|
1152
|
+
if (typeof rawOrgId === "string" && rawOrgId.length > 0) {
|
|
1153
|
+
organizationIdFromToken = rawOrgId;
|
|
1154
|
+
}
|
|
1155
|
+
const tenantClaim = claims.tenantId ?? claims.tenant_id;
|
|
1156
|
+
if (typeof tenantClaim === "string" && tenantClaim.length > 0) {
|
|
1157
|
+
tenantIdFromToken = tenantClaim;
|
|
1158
|
+
}
|
|
1159
|
+
const issClaim = claims.iss;
|
|
1160
|
+
if (typeof issClaim === "string" && issClaim.length > 0) {
|
|
1161
|
+
issuerFromToken = issClaim;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
return {
|
|
1165
|
+
accessToken,
|
|
1166
|
+
baseUrl: parsedUrl.baseUrl,
|
|
1167
|
+
organizationName: parsedUrl.organizationName,
|
|
1168
|
+
organizationId: organizationIdFromToken ?? parsedUrl.organizationName,
|
|
1169
|
+
tenantName: parsedUrl.tenantName,
|
|
1170
|
+
tenantId: tenantIdFromToken,
|
|
1171
|
+
issuer: issuerFromToken
|
|
1172
|
+
};
|
|
1173
|
+
} catch {
|
|
1174
|
+
return;
|
|
1175
|
+
} finally {
|
|
1176
|
+
await catchError(() => withTimeout(proxy.CloseAsync(), CLOSE_TIMEOUT_MS));
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
// ../auth/src/tokenRefresh.ts
|
|
1181
|
+
var TOKEN_REFRESH_REAUTHENTICATE_MESSAGE = "Token refresh failed. Run 'uip login' to re-authenticate.";
|
|
1182
|
+
|
|
1183
|
+
class TokenRefreshOAuthError extends Error {
|
|
1184
|
+
__brand = "TokenRefreshOAuthError";
|
|
1185
|
+
constructor() {
|
|
1186
|
+
super(TOKEN_REFRESH_REAUTHENTICATE_MESSAGE);
|
|
1187
|
+
this.name = "TokenRefreshOAuthError";
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
function isTokenRefreshOAuthFailure(error) {
|
|
1191
|
+
return error instanceof TokenRefreshOAuthError;
|
|
1192
|
+
}
|
|
1193
|
+
var refreshAccessToken = async ({
|
|
1194
|
+
refreshToken,
|
|
1195
|
+
tokenEndpoint,
|
|
1196
|
+
clientId,
|
|
1197
|
+
expectedAuthority
|
|
1198
|
+
}) => {
|
|
1199
|
+
const tokenParams = new URLSearchParams({
|
|
1200
|
+
grant_type: "refresh_token",
|
|
1201
|
+
refresh_token: refreshToken,
|
|
1202
|
+
client_id: clientId
|
|
1203
|
+
});
|
|
1204
|
+
const tokenResponse = await fetch(tokenEndpoint, {
|
|
1205
|
+
method: "POST",
|
|
1206
|
+
headers: {
|
|
1207
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1208
|
+
},
|
|
1209
|
+
body: tokenParams
|
|
1210
|
+
});
|
|
1211
|
+
const tokenData = await tokenResponse.json();
|
|
1212
|
+
if (!tokenResponse.ok) {
|
|
1213
|
+
throw new TokenRefreshOAuthError;
|
|
1214
|
+
}
|
|
1215
|
+
const newAccessToken = tokenData.access_token;
|
|
1216
|
+
const newRefreshToken = tokenData.refresh_token;
|
|
1217
|
+
if (typeof newAccessToken !== "string" || typeof newRefreshToken !== "string") {
|
|
1218
|
+
throw new Error("Token refresh response is missing access_token or refresh_token");
|
|
1219
|
+
}
|
|
1220
|
+
if (expectedAuthority) {
|
|
1221
|
+
assertIssuerMatchesAuthority(newAccessToken, expectedAuthority);
|
|
1222
|
+
}
|
|
1223
|
+
return { accessToken: newAccessToken, refreshToken: newRefreshToken };
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
// ../auth/src/utils/envFile.ts
|
|
1227
|
+
var DEFAULT_AUTH_FILENAME = AUTH_FILENAME;
|
|
1228
|
+
var DEFAULT_ENV_FILENAME = `${UIPATH_HOME_DIR}/${AUTH_FILENAME}`;
|
|
1229
|
+
var KNOWN_ERROR_CODES = new Set([
|
|
1230
|
+
"EISDIR",
|
|
1231
|
+
"EACCES",
|
|
1232
|
+
"EPERM",
|
|
1233
|
+
"ELOOP",
|
|
1234
|
+
"ENOTDIR",
|
|
1235
|
+
"EUNKNOWN"
|
|
1236
|
+
]);
|
|
1237
|
+
var errorCode = (err) => {
|
|
1238
|
+
if (err !== null && typeof err === "object" && "code" in err && typeof err.code === "string") {
|
|
1239
|
+
const raw = err.code;
|
|
1240
|
+
return KNOWN_ERROR_CODES.has(raw) ? raw : "EUNKNOWN";
|
|
1241
|
+
}
|
|
1242
|
+
return "EUNKNOWN";
|
|
1243
|
+
};
|
|
1244
|
+
var probeAsync = async (fs, candidate) => {
|
|
1245
|
+
try {
|
|
1246
|
+
const stats = await fs.stat(candidate);
|
|
1247
|
+
if (stats === null) {
|
|
1248
|
+
return { exists: false };
|
|
1249
|
+
}
|
|
1250
|
+
if (!stats.isFile()) {
|
|
1251
|
+
return {
|
|
1252
|
+
exists: false,
|
|
1253
|
+
unusable: {
|
|
1254
|
+
reason: "not-a-file",
|
|
1255
|
+
code: "EISDIR",
|
|
1256
|
+
message: `Path is not a regular file: ${candidate}`
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
return { exists: true };
|
|
1261
|
+
} catch (err) {
|
|
1262
|
+
return {
|
|
1263
|
+
exists: false,
|
|
1264
|
+
unusable: {
|
|
1265
|
+
reason: "unreadable",
|
|
1266
|
+
code: errorCode(err),
|
|
1267
|
+
message: err instanceof Error ? err.message : String(err)
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME, opts) => {
|
|
1273
|
+
const fs = getFileSystem();
|
|
1274
|
+
if (fs.path.isAbsolute(envFilePath)) {
|
|
1275
|
+
const probe2 = await probeAsync(fs, envFilePath);
|
|
1276
|
+
return probe2.exists ? { exists: true, absolutePath: envFilePath, source: "absolute" } : {
|
|
1277
|
+
exists: false,
|
|
1278
|
+
absolutePath: envFilePath,
|
|
1279
|
+
source: "absolute",
|
|
1280
|
+
...probe2.unusable ? { unusable: probe2.unusable } : {}
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
const cwd = opts?.cwd ?? fs.env.cwd();
|
|
1284
|
+
let searchDir = cwd;
|
|
1285
|
+
while (true) {
|
|
1286
|
+
const candidate = fs.path.join(searchDir, envFilePath);
|
|
1287
|
+
const probe2 = await probeAsync(fs, candidate);
|
|
1288
|
+
if (probe2.exists) {
|
|
1289
|
+
return {
|
|
1290
|
+
exists: true,
|
|
1291
|
+
absolutePath: candidate,
|
|
1292
|
+
source: searchDir === cwd ? "cwd" : "ancestor"
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
const parentDir = fs.path.dirname(searchDir);
|
|
1296
|
+
if (parentDir === searchDir) {
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
searchDir = parentDir;
|
|
1300
|
+
}
|
|
1301
|
+
const homePath = fs.path.join(fs.env.homedir(), envFilePath);
|
|
1302
|
+
const probe = await probeAsync(fs, homePath);
|
|
1303
|
+
if (probe.exists) {
|
|
1304
|
+
return { exists: true, absolutePath: homePath, source: "home" };
|
|
1305
|
+
}
|
|
1306
|
+
return {
|
|
1307
|
+
exists: false,
|
|
1308
|
+
absolutePath: homePath,
|
|
1309
|
+
source: "default",
|
|
1310
|
+
...probe.unusable ? { unusable: probe.unusable } : {}
|
|
1311
|
+
};
|
|
1312
|
+
};
|
|
1313
|
+
var resolveEnvFilePathAsync = async (envFilePath = DEFAULT_ENV_FILENAME, opts) => {
|
|
1314
|
+
const location = await resolveEnvFileLocationAsync(envFilePath, opts);
|
|
1315
|
+
if (location.exists) {
|
|
1316
|
+
return { absolutePath: location.absolutePath };
|
|
1317
|
+
}
|
|
1318
|
+
return {
|
|
1319
|
+
absolutePath: undefined,
|
|
1320
|
+
errorMessage: location.source === "absolute" ? `Environment file not found: ${envFilePath}` : `Unable to locate environment file: ${envFilePath}. Run 'uip login' to authenticate.`
|
|
1321
|
+
};
|
|
1322
|
+
};
|
|
1323
|
+
var loadEnvFileAsync = async ({ envPath }) => {
|
|
1324
|
+
const fs = getFileSystem();
|
|
1325
|
+
const absolutePath = fs.path.isAbsolute(envPath) ? envPath : fs.path.join(fs.env.cwd(), envPath);
|
|
1326
|
+
if (!await fs.exists(absolutePath)) {
|
|
1327
|
+
throw new Error(`Environment file not found: ${envPath}`);
|
|
1328
|
+
}
|
|
1329
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
1330
|
+
if (content === null) {
|
|
1331
|
+
throw new Error(`Environment file not found: ${envPath}`);
|
|
1332
|
+
}
|
|
1333
|
+
const env = {};
|
|
1334
|
+
for (const line of content.split(`
|
|
1335
|
+
`)) {
|
|
1336
|
+
const trimmed = line.trim();
|
|
1337
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
const equalIndex = trimmed.indexOf("=");
|
|
1341
|
+
if (equalIndex === -1) {
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
const key = trimmed.slice(0, equalIndex).trim();
|
|
1345
|
+
let value = trimmed.slice(equalIndex + 1).trim();
|
|
1346
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1347
|
+
value = value.slice(1, -1);
|
|
1348
|
+
}
|
|
1349
|
+
env[key] = value;
|
|
1350
|
+
}
|
|
1351
|
+
return env;
|
|
1352
|
+
};
|
|
1353
|
+
var saveEnvFileAsync = async ({
|
|
1354
|
+
envPath,
|
|
1355
|
+
data,
|
|
1356
|
+
merge = true
|
|
1357
|
+
}) => {
|
|
1358
|
+
const fs = getFileSystem();
|
|
1359
|
+
const absolutePath = fs.path.isAbsolute(envPath) ? envPath : fs.path.join(fs.env.homedir(), envPath);
|
|
1360
|
+
let existingData = {};
|
|
1361
|
+
if (merge && await fs.exists(absolutePath)) {
|
|
1362
|
+
try {
|
|
1363
|
+
existingData = await loadEnvFileAsync({ envPath: absolutePath });
|
|
1364
|
+
} catch {}
|
|
1365
|
+
}
|
|
1366
|
+
const finalData = { ...existingData, ...data };
|
|
1367
|
+
const lines = [];
|
|
1368
|
+
for (const [key, value] of Object.entries(finalData)) {
|
|
1369
|
+
if (value === undefined) {
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
const needsQuotes = value.includes(" ") || value.includes("#");
|
|
1373
|
+
const finalValue = needsQuotes ? `"${value}"` : value;
|
|
1374
|
+
lines.push(`${key}=${finalValue}`);
|
|
1375
|
+
}
|
|
1376
|
+
const content = `${lines.join(`
|
|
1377
|
+
`)}
|
|
1378
|
+
`;
|
|
1379
|
+
const dir = fs.path.dirname(absolutePath);
|
|
1380
|
+
await fs.mkdir(dir);
|
|
1381
|
+
const tempPath = `${absolutePath}.tmp`;
|
|
1382
|
+
await fs.writeFile(tempPath, content);
|
|
1383
|
+
await fs.rename(tempPath, absolutePath);
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
// ../auth/src/loginStatus.ts
|
|
1387
|
+
var LoginStatusSource;
|
|
1388
|
+
((LoginStatusSource2) => {
|
|
1389
|
+
LoginStatusSource2["File"] = "file";
|
|
1390
|
+
LoginStatusSource2["Robot"] = "robot";
|
|
1391
|
+
LoginStatusSource2["Env"] = "env";
|
|
1392
|
+
})(LoginStatusSource ||= {});
|
|
1393
|
+
var getLoginStatusAsync = async (options = {}) => {
|
|
1394
|
+
return getLoginStatusWithDeps(options);
|
|
1395
|
+
};
|
|
1396
|
+
var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
1397
|
+
const {
|
|
1398
|
+
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
1399
|
+
loadEnvFile = loadEnvFileAsync,
|
|
1400
|
+
saveEnvFile = saveEnvFileAsync,
|
|
1401
|
+
getFs = getFileSystem,
|
|
1402
|
+
refreshToken: refreshTokenFn = refreshAccessToken,
|
|
1403
|
+
resolveConfig = resolveConfigAsync,
|
|
1404
|
+
robotFallback = tryRobotClientFallback,
|
|
1405
|
+
loadBreaker = loadRefreshBreaker,
|
|
1406
|
+
saveBreaker = saveRefreshBreaker,
|
|
1407
|
+
clearBreaker = clearRefreshBreaker
|
|
1408
|
+
} = deps;
|
|
1409
|
+
if (isRobotAuthEnforced()) {
|
|
1410
|
+
return resolveRobotEnforcedStatus(robotFallback);
|
|
1411
|
+
}
|
|
1412
|
+
if (isEnvAuthEnabled()) {
|
|
1413
|
+
return readAuthFromEnv();
|
|
1414
|
+
}
|
|
1415
|
+
const activeProfile = getActiveAuthProfile();
|
|
1416
|
+
const activeProfileFilePath = getActiveAuthProfileFilePath();
|
|
1417
|
+
const usingActiveProfile = activeProfile !== undefined && (options.envFilePath === undefined || options.envFilePath === activeProfileFilePath);
|
|
1418
|
+
const envFilePath = options.envFilePath ?? activeProfileFilePath ?? DEFAULT_ENV_FILENAME;
|
|
1419
|
+
const { ensureTokenValidityMinutes } = options;
|
|
1420
|
+
const { absolutePath } = await resolveEnvFilePath(envFilePath);
|
|
1421
|
+
if (absolutePath === undefined) {
|
|
1422
|
+
if (usingActiveProfile) {
|
|
1423
|
+
return {
|
|
1424
|
+
loginStatus: "Not logged in",
|
|
1425
|
+
hint: `No credentials found for profile "${activeProfile}". Run 'uip login --profile ${activeProfile}' to authenticate this profile.`
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
return resolveBorrowedRobotStatus(robotFallback);
|
|
1429
|
+
}
|
|
1430
|
+
const loaded = await loadFileCredentials(loadEnvFile, absolutePath);
|
|
1431
|
+
if ("status" in loaded) {
|
|
1432
|
+
return loaded.status;
|
|
1433
|
+
}
|
|
1434
|
+
const { credentials } = loaded;
|
|
1435
|
+
const globalHint = () => usingActiveProfile ? Promise.resolve(undefined) : getGlobalCredsHint(getFs, loadEnvFile, absolutePath, envFilePath);
|
|
1436
|
+
const expiration = getTokenExpiration(credentials.UIPATH_ACCESS_TOKEN);
|
|
1437
|
+
const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
1438
|
+
let tokens = {
|
|
1439
|
+
accessToken: credentials.UIPATH_ACCESS_TOKEN,
|
|
1440
|
+
refreshToken: credentials.UIPATH_REFRESH_TOKEN,
|
|
1441
|
+
expiration,
|
|
1442
|
+
lockReleaseFailed: false
|
|
1443
|
+
};
|
|
1444
|
+
const refreshToken = credentials.UIPATH_REFRESH_TOKEN;
|
|
1445
|
+
if (expiration && expiration <= outerThreshold && refreshToken) {
|
|
1446
|
+
const refreshed = await attemptRefresh({
|
|
1447
|
+
absolutePath,
|
|
1448
|
+
credentials,
|
|
1449
|
+
accessToken: credentials.UIPATH_ACCESS_TOKEN,
|
|
1450
|
+
refreshToken,
|
|
1451
|
+
expiration,
|
|
1452
|
+
ensureTokenValidityMinutes,
|
|
1453
|
+
getFs,
|
|
1454
|
+
loadEnvFile,
|
|
1455
|
+
saveEnvFile,
|
|
1456
|
+
refreshFn: refreshTokenFn,
|
|
1457
|
+
resolveConfig,
|
|
1458
|
+
loadBreaker,
|
|
1459
|
+
saveBreaker,
|
|
1460
|
+
clearBreaker,
|
|
1461
|
+
globalHint
|
|
1462
|
+
});
|
|
1463
|
+
if (refreshed.kind === "terminal") {
|
|
1464
|
+
return refreshed.status;
|
|
1465
|
+
}
|
|
1466
|
+
tokens = refreshed.tokens;
|
|
1467
|
+
}
|
|
1468
|
+
return buildFileStatus(tokens, credentials, globalHint);
|
|
1469
|
+
};
|
|
1470
|
+
async function resolveRobotEnforcedStatus(robotFallback) {
|
|
1471
|
+
if (isEnvAuthEnabled()) {
|
|
1472
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
|
|
1473
|
+
}
|
|
1474
|
+
const robotCreds = await robotFallback({ force: true });
|
|
1475
|
+
if (!robotCreds) {
|
|
1476
|
+
return {
|
|
1477
|
+
loginStatus: "Not logged in",
|
|
1478
|
+
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.`
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
return buildRobotStatus(robotCreds);
|
|
1482
|
+
}
|
|
1483
|
+
async function resolveBorrowedRobotStatus(robotFallback) {
|
|
1484
|
+
const robotCreds = await robotFallback();
|
|
1485
|
+
return robotCreds ? buildRobotStatus(robotCreds) : { loginStatus: "Not logged in" };
|
|
1486
|
+
}
|
|
1487
|
+
async function loadFileCredentials(loadEnvFile, absolutePath) {
|
|
1488
|
+
let credentials;
|
|
1489
|
+
try {
|
|
1490
|
+
credentials = await loadEnvFile({ envPath: absolutePath });
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
if (isFileNotFoundError(error)) {
|
|
1493
|
+
return { status: { loginStatus: "Not logged in" } };
|
|
1494
|
+
}
|
|
1495
|
+
throw error;
|
|
1496
|
+
}
|
|
1497
|
+
if (!credentials.UIPATH_ACCESS_TOKEN) {
|
|
1498
|
+
return { status: { loginStatus: "Not logged in" } };
|
|
1499
|
+
}
|
|
1500
|
+
return { credentials };
|
|
1501
|
+
}
|
|
1502
|
+
async function getGlobalCredsHint(getFs, loadEnvFile, absolutePath, envFilePath) {
|
|
1503
|
+
const fs = getFs();
|
|
1504
|
+
const globalPath = fs.path.join(fs.env.homedir(), envFilePath);
|
|
1505
|
+
if (absolutePath === globalPath)
|
|
1506
|
+
return;
|
|
1507
|
+
if (!await fs.exists(globalPath))
|
|
1508
|
+
return;
|
|
1509
|
+
try {
|
|
1510
|
+
const globalCreds = await loadEnvFile({ envPath: globalPath });
|
|
1511
|
+
if (!globalCreds.UIPATH_ACCESS_TOKEN)
|
|
1512
|
+
return;
|
|
1513
|
+
const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
|
|
1514
|
+
if (globalExp && globalExp <= new Date)
|
|
1515
|
+
return;
|
|
1516
|
+
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.`;
|
|
1517
|
+
} catch {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
function computeExpirationThreshold(ensureTokenValidityMinutes) {
|
|
1522
|
+
return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
|
|
1523
|
+
}
|
|
1524
|
+
async function attemptRefresh(ctx) {
|
|
1525
|
+
const shortCircuit = await circuitBreakerShortCircuit(ctx);
|
|
1526
|
+
if (shortCircuit) {
|
|
1527
|
+
return { kind: "terminal", status: shortCircuit };
|
|
1528
|
+
}
|
|
1529
|
+
let release;
|
|
1530
|
+
try {
|
|
1531
|
+
release = await ctx.getFs().acquireLock(ctx.absolutePath);
|
|
1532
|
+
} catch (error) {
|
|
1533
|
+
return {
|
|
1534
|
+
kind: "terminal",
|
|
1535
|
+
status: await lockAcquireFailureStatus(ctx, error)
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
let lockedFailure;
|
|
1539
|
+
let lockReleaseFailed = false;
|
|
1540
|
+
let success;
|
|
1541
|
+
try {
|
|
1542
|
+
const outcome = await runRefreshLocked({
|
|
1543
|
+
absolutePath: ctx.absolutePath,
|
|
1544
|
+
refreshToken: ctx.refreshToken,
|
|
1545
|
+
customAuthority: ctx.credentials.UIPATH_URL,
|
|
1546
|
+
ensureTokenValidityMinutes: ctx.ensureTokenValidityMinutes,
|
|
1547
|
+
loadEnvFile: ctx.loadEnvFile,
|
|
1548
|
+
saveEnvFile: ctx.saveEnvFile,
|
|
1549
|
+
refreshFn: ctx.refreshFn,
|
|
1550
|
+
resolveConfig: ctx.resolveConfig,
|
|
1551
|
+
loadBreaker: ctx.loadBreaker,
|
|
1552
|
+
saveBreaker: ctx.saveBreaker,
|
|
1553
|
+
clearBreaker: ctx.clearBreaker
|
|
1554
|
+
});
|
|
1555
|
+
if (outcome.kind === "fail") {
|
|
1556
|
+
lockedFailure = outcome.status;
|
|
1557
|
+
} else {
|
|
1558
|
+
success = outcome;
|
|
1559
|
+
}
|
|
1560
|
+
} finally {
|
|
1561
|
+
try {
|
|
1562
|
+
await release();
|
|
1563
|
+
} catch {
|
|
1564
|
+
lockReleaseFailed = true;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (lockedFailure) {
|
|
1568
|
+
const globalHint = await ctx.globalHint();
|
|
1569
|
+
const base = globalHint ? { ...lockedFailure, loginStatus: "Expired", hint: globalHint } : lockedFailure;
|
|
1570
|
+
return {
|
|
1571
|
+
kind: "terminal",
|
|
1572
|
+
status: lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
return {
|
|
1576
|
+
kind: "refreshed",
|
|
1577
|
+
tokens: {
|
|
1578
|
+
accessToken: success?.accessToken,
|
|
1579
|
+
refreshToken: success?.refreshToken,
|
|
1580
|
+
expiration: success?.expiration,
|
|
1581
|
+
tokenRefresh: success?.tokenRefresh,
|
|
1582
|
+
persistenceWarning: success?.persistenceWarning,
|
|
1583
|
+
lockReleaseFailed
|
|
1584
|
+
}
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
async function buildFileStatus(tokens, credentials, globalHint) {
|
|
1588
|
+
const result = {
|
|
1589
|
+
loginStatus: tokens.expiration && tokens.expiration <= new Date ? "Expired" : "Logged in",
|
|
1590
|
+
accessToken: tokens.accessToken,
|
|
1591
|
+
refreshToken: tokens.refreshToken,
|
|
1592
|
+
baseUrl: credentials.UIPATH_URL,
|
|
1593
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
1594
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
1595
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
1596
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
1597
|
+
expiration: tokens.expiration,
|
|
1598
|
+
source: "file" /* File */,
|
|
1599
|
+
...tokens.persistenceWarning ? { hint: tokens.persistenceWarning, persistenceFailed: true } : {},
|
|
1600
|
+
...tokens.lockReleaseFailed ? { lockReleaseFailed: true } : {},
|
|
1601
|
+
...tokens.tokenRefresh ? { tokenRefresh: tokens.tokenRefresh } : {}
|
|
1602
|
+
};
|
|
1603
|
+
if (result.loginStatus === "Expired") {
|
|
1604
|
+
const hint = await globalHint();
|
|
1605
|
+
if (hint) {
|
|
1606
|
+
result.hint = hint;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return result;
|
|
1610
|
+
}
|
|
1611
|
+
function buildRobotStatus(robotCreds) {
|
|
1612
|
+
return {
|
|
1613
|
+
loginStatus: "Logged in",
|
|
1614
|
+
accessToken: robotCreds.accessToken,
|
|
1615
|
+
baseUrl: robotCreds.baseUrl,
|
|
1616
|
+
organizationName: robotCreds.organizationName,
|
|
1617
|
+
organizationId: robotCreds.organizationId,
|
|
1618
|
+
tenantName: robotCreds.tenantName,
|
|
1619
|
+
tenantId: robotCreds.tenantId,
|
|
1620
|
+
issuer: robotCreds.issuer,
|
|
1621
|
+
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
1622
|
+
source: "robot" /* Robot */
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
var isFileNotFoundError = (error) => {
|
|
1626
|
+
if (!(error instanceof Object))
|
|
1627
|
+
return false;
|
|
1628
|
+
return error.code === "ENOENT";
|
|
1629
|
+
};
|
|
1630
|
+
async function circuitBreakerShortCircuit(ctx) {
|
|
1631
|
+
const {
|
|
1632
|
+
absolutePath,
|
|
1633
|
+
refreshToken,
|
|
1634
|
+
accessToken,
|
|
1635
|
+
credentials,
|
|
1636
|
+
expiration,
|
|
1637
|
+
loadBreaker,
|
|
1638
|
+
saveBreaker,
|
|
1639
|
+
clearBreaker
|
|
1640
|
+
} = ctx;
|
|
1641
|
+
const fingerprint = await refreshTokenFingerprint(refreshToken);
|
|
1642
|
+
const breaker = await loadBreaker(absolutePath).catch(() => ({}));
|
|
1643
|
+
if (breaker.deadTokenFp && breaker.deadTokenFp !== fingerprint) {
|
|
1644
|
+
await clearBreaker(absolutePath);
|
|
1645
|
+
breaker.deadTokenFp = undefined;
|
|
1646
|
+
}
|
|
1647
|
+
const nowMs = Date.now();
|
|
1648
|
+
const tokenIsDead = breaker.deadTokenFp === fingerprint;
|
|
1649
|
+
const inBackoff = breaker.backoffUntilMs !== undefined && nowMs < breaker.backoffUntilMs;
|
|
1650
|
+
if (!tokenIsDead && !inBackoff)
|
|
1651
|
+
return;
|
|
1652
|
+
const globalHint = await ctx.globalHint();
|
|
1653
|
+
const suppressed = !shouldSurface(breaker, nowMs);
|
|
1654
|
+
if (!suppressed) {
|
|
1655
|
+
await saveBreaker(absolutePath, {
|
|
1656
|
+
...breaker,
|
|
1657
|
+
lastSurfacedAtMs: nowMs
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
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>.";
|
|
1661
|
+
const backoffHint = "Token refresh is temporarily backed off after a recent network error and will retry automatically once the backoff window elapses.";
|
|
1662
|
+
return {
|
|
1663
|
+
loginStatus: globalHint ? "Expired" : "Refresh Failed",
|
|
1664
|
+
...globalHint ? {
|
|
1665
|
+
accessToken,
|
|
1666
|
+
refreshToken,
|
|
1667
|
+
baseUrl: credentials.UIPATH_URL,
|
|
1668
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
1669
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
1670
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
1671
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
1672
|
+
expiration,
|
|
1673
|
+
source: "file" /* File */
|
|
1674
|
+
} : {},
|
|
1675
|
+
hint: globalHint ?? (tokenIsDead ? deadHint : backoffHint),
|
|
1676
|
+
refreshCircuitOpen: true,
|
|
1677
|
+
refreshTelemetrySuppressed: suppressed,
|
|
1678
|
+
tokenRefresh: { attempted: false, success: false }
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
async function lockAcquireFailureStatus(ctx, error) {
|
|
1682
|
+
const msg = errorMessage(error);
|
|
1683
|
+
const globalHint = await ctx.globalHint();
|
|
1684
|
+
if (globalHint) {
|
|
1685
|
+
return {
|
|
1686
|
+
loginStatus: "Expired",
|
|
1687
|
+
accessToken: ctx.accessToken,
|
|
1688
|
+
refreshToken: ctx.refreshToken,
|
|
1689
|
+
baseUrl: ctx.credentials.UIPATH_URL,
|
|
1690
|
+
organizationName: ctx.credentials.UIPATH_ORGANIZATION_NAME,
|
|
1691
|
+
organizationId: ctx.credentials.UIPATH_ORGANIZATION_ID,
|
|
1692
|
+
tenantName: ctx.credentials.UIPATH_TENANT_NAME,
|
|
1693
|
+
tenantId: ctx.credentials.UIPATH_TENANT_ID,
|
|
1694
|
+
expiration: ctx.expiration,
|
|
1695
|
+
source: "file" /* File */,
|
|
1696
|
+
hint: globalHint,
|
|
1697
|
+
tokenRefresh: {
|
|
1698
|
+
attempted: false,
|
|
1699
|
+
success: false,
|
|
1700
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
return {
|
|
1705
|
+
loginStatus: "Refresh Failed",
|
|
1706
|
+
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.",
|
|
1707
|
+
tokenRefresh: {
|
|
1708
|
+
attempted: false,
|
|
1709
|
+
success: false,
|
|
1710
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
async function runRefreshLocked(inputs) {
|
|
1715
|
+
const {
|
|
1716
|
+
absolutePath,
|
|
1717
|
+
refreshToken: callerRefreshToken,
|
|
1718
|
+
customAuthority,
|
|
1719
|
+
ensureTokenValidityMinutes,
|
|
1720
|
+
loadEnvFile,
|
|
1721
|
+
saveEnvFile,
|
|
1722
|
+
refreshFn,
|
|
1723
|
+
resolveConfig,
|
|
1724
|
+
loadBreaker,
|
|
1725
|
+
saveBreaker,
|
|
1726
|
+
clearBreaker
|
|
1727
|
+
} = inputs;
|
|
1728
|
+
const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
1729
|
+
let fresh;
|
|
1730
|
+
try {
|
|
1731
|
+
fresh = await loadEnvFile({ envPath: absolutePath });
|
|
1732
|
+
} catch (error) {
|
|
1733
|
+
return {
|
|
1734
|
+
kind: "fail",
|
|
1735
|
+
status: {
|
|
1736
|
+
loginStatus: "Refresh Failed",
|
|
1737
|
+
hint: "Could not read the auth file while refreshing. Retry, or run 'uip login' to re-authenticate.",
|
|
1738
|
+
tokenRefresh: {
|
|
1739
|
+
attempted: false,
|
|
1740
|
+
success: false,
|
|
1741
|
+
errorMessage: `auth file read failed: ${errorMessage(error)}`
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
|
|
1747
|
+
const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
|
|
1748
|
+
if (freshAccess && freshExp && freshExp > expirationThreshold) {
|
|
1749
|
+
await clearBreaker(absolutePath);
|
|
1750
|
+
return {
|
|
1751
|
+
kind: "ok",
|
|
1752
|
+
accessToken: freshAccess,
|
|
1753
|
+
refreshToken: fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken,
|
|
1754
|
+
expiration: freshExp,
|
|
1755
|
+
tokenRefresh: { attempted: false, success: true }
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
const tokenForIdP = fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken;
|
|
1759
|
+
let refreshedAccess;
|
|
1760
|
+
let refreshedRefresh;
|
|
1761
|
+
try {
|
|
1762
|
+
const config = await resolveConfig({ customAuthority });
|
|
1763
|
+
const refreshed = await refreshFn({
|
|
1764
|
+
refreshToken: tokenForIdP,
|
|
1765
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
1766
|
+
clientId: config.clientId,
|
|
1767
|
+
expectedAuthority: customAuthority
|
|
1768
|
+
});
|
|
1769
|
+
refreshedAccess = refreshed.accessToken;
|
|
1770
|
+
refreshedRefresh = refreshed.refreshToken;
|
|
1771
|
+
} catch (error) {
|
|
1772
|
+
const isOAuthFailure = isTokenRefreshOAuthFailure(error);
|
|
1773
|
+
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.";
|
|
1774
|
+
const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
|
|
1775
|
+
const fp = await refreshTokenFingerprint(tokenForIdP);
|
|
1776
|
+
if (isOAuthFailure) {
|
|
1777
|
+
await saveBreaker(absolutePath, { deadTokenFp: fp });
|
|
1778
|
+
} else {
|
|
1779
|
+
const prior = await loadBreaker(absolutePath).catch(() => ({}));
|
|
1780
|
+
const attempts = (prior.attempts ?? 0) + 1;
|
|
1781
|
+
await saveBreaker(absolutePath, {
|
|
1782
|
+
...prior,
|
|
1783
|
+
deadTokenFp: undefined,
|
|
1784
|
+
attempts,
|
|
1785
|
+
backoffUntilMs: Date.now() + nextBackoffMs(attempts)
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
return {
|
|
1789
|
+
kind: "fail",
|
|
1790
|
+
status: {
|
|
1791
|
+
loginStatus: "Refresh Failed",
|
|
1792
|
+
hint,
|
|
1793
|
+
tokenRefresh: {
|
|
1794
|
+
attempted: true,
|
|
1795
|
+
success: false,
|
|
1796
|
+
errorMessage: message
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
const refreshedExp = getTokenExpiration(refreshedAccess);
|
|
1802
|
+
if (!refreshedExp || refreshedExp <= new Date) {
|
|
1803
|
+
return {
|
|
1804
|
+
kind: "fail",
|
|
1805
|
+
status: {
|
|
1806
|
+
loginStatus: "Refresh Failed",
|
|
1807
|
+
hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
|
|
1808
|
+
tokenRefresh: {
|
|
1809
|
+
attempted: true,
|
|
1810
|
+
success: false,
|
|
1811
|
+
errorMessage: "refreshed token has no valid expiration claim"
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
await clearBreaker(absolutePath);
|
|
1817
|
+
try {
|
|
1818
|
+
await saveEnvFile({
|
|
1819
|
+
envPath: absolutePath,
|
|
1820
|
+
data: {
|
|
1821
|
+
UIPATH_ACCESS_TOKEN: refreshedAccess,
|
|
1822
|
+
UIPATH_REFRESH_TOKEN: refreshedRefresh
|
|
1823
|
+
},
|
|
1824
|
+
merge: true
|
|
1825
|
+
});
|
|
1826
|
+
return {
|
|
1827
|
+
kind: "ok",
|
|
1828
|
+
accessToken: refreshedAccess,
|
|
1829
|
+
refreshToken: refreshedRefresh,
|
|
1830
|
+
expiration: refreshedExp,
|
|
1831
|
+
tokenRefresh: { attempted: true, success: true }
|
|
1832
|
+
};
|
|
1833
|
+
} catch (error) {
|
|
1834
|
+
const msg = errorMessage(error);
|
|
1835
|
+
return {
|
|
1836
|
+
kind: "ok",
|
|
1837
|
+
accessToken: refreshedAccess,
|
|
1838
|
+
refreshToken: refreshedRefresh,
|
|
1839
|
+
expiration: refreshedExp,
|
|
1840
|
+
persistenceWarning: `Access token refreshed in memory but could not be written to ${absolutePath}: ${msg}. The next CLI invocation will fail until the file can be updated — run 'uip login' to re-authenticate.`,
|
|
1841
|
+
tokenRefresh: {
|
|
1842
|
+
attempted: true,
|
|
1843
|
+
success: true,
|
|
1844
|
+
errorMessage: `persistence failed: ${msg}`
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function normalizeTokenRefreshFailure() {
|
|
1850
|
+
return "stored refresh token is invalid or expired";
|
|
1851
|
+
}
|
|
1852
|
+
function normalizeTokenRefreshUnavailableFailure() {
|
|
1853
|
+
return "token refresh failed before authentication completed";
|
|
1854
|
+
}
|
|
1855
|
+
function errorMessage(error) {
|
|
1856
|
+
return error instanceof Error ? error.message : String(error);
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// ../auth/src/authContext.ts
|
|
1860
|
+
var getAuthContext = async (options = {}) => {
|
|
1861
|
+
const status = await getLoginStatusAsync({
|
|
1862
|
+
ensureTokenValidityMinutes: options.ensureTokenValidityMinutes,
|
|
1863
|
+
envFilePath: options.envFilePath
|
|
1864
|
+
});
|
|
1865
|
+
if (status.loginStatus !== "Logged in" || !status.baseUrl || !status.accessToken) {
|
|
1866
|
+
throw new Error(status.hint ? `Not logged in. ${status.hint}` : "Not logged in. Run 'uip login' first.");
|
|
1867
|
+
}
|
|
1868
|
+
const tenantName = options.tenant ?? status.tenantName;
|
|
1869
|
+
if (options.requireOrganizationId && !status.organizationId) {
|
|
1870
|
+
throw new Error("Organization ID not available. Ensure you are logged in with an organization context.");
|
|
1871
|
+
}
|
|
1872
|
+
if (options.requireOrganizationName && !status.organizationName) {
|
|
1873
|
+
throw new Error("Organization name not available. Ensure you are logged in with an organization context.");
|
|
1874
|
+
}
|
|
1875
|
+
if (options.requireTenantId && !status.tenantId) {
|
|
1876
|
+
throw new Error("Tenant ID not available. Ensure UIPATH_TENANT_ID is set.");
|
|
1877
|
+
}
|
|
1878
|
+
if (options.requireTenantName && tenantName === undefined) {
|
|
1879
|
+
throw new Error("Tenant not provided and UIPATH_TENANT_NAME not set. Run 'uip login' to select a tenant, or use 'uip login tenant set <tenant>' to switch tenants.");
|
|
1880
|
+
}
|
|
1881
|
+
return {
|
|
1882
|
+
baseUrl: status.baseUrl,
|
|
1883
|
+
accessToken: status.accessToken,
|
|
1884
|
+
organizationId: status.organizationId,
|
|
1885
|
+
organizationName: status.organizationName,
|
|
1886
|
+
tenantId: status.tenantId,
|
|
1887
|
+
tenantName
|
|
1888
|
+
};
|
|
1889
|
+
};
|
|
1890
|
+
var getAuthEnv = async (options = {}) => {
|
|
1891
|
+
const authEnv = {};
|
|
1892
|
+
let status;
|
|
1893
|
+
try {
|
|
1894
|
+
status = await getLoginStatusAsync(options);
|
|
1895
|
+
} catch {
|
|
1896
|
+
return { authEnv };
|
|
1897
|
+
}
|
|
1898
|
+
if (status.loginStatus === "Logged in" && status.accessToken) {
|
|
1899
|
+
authEnv.UIPATH_ACCESS_TOKEN = status.accessToken;
|
|
1900
|
+
if (status.baseUrl) {
|
|
1901
|
+
const org = status.organizationName || status.organizationId;
|
|
1902
|
+
const tenant = status.tenantName || status.tenantId;
|
|
1903
|
+
if (org && tenant) {
|
|
1904
|
+
authEnv.UIPATH_URL = `${status.baseUrl.replace(/\/+$/, "")}/${org}/${tenant}`;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
if (status.organizationId)
|
|
1908
|
+
authEnv.UIPATH_ORGANIZATION_ID = status.organizationId;
|
|
1909
|
+
if (status.organizationName)
|
|
1910
|
+
authEnv.UIPATH_ORGANIZATION_NAME = status.organizationName;
|
|
1911
|
+
if (status.tenantId)
|
|
1912
|
+
authEnv.UIPATH_TENANT_ID = status.tenantId;
|
|
1913
|
+
if (status.tenantName)
|
|
1914
|
+
authEnv.UIPATH_TENANT_NAME = status.tenantName;
|
|
1915
|
+
}
|
|
1916
|
+
return { authEnv, loginStatus: status };
|
|
1917
|
+
};
|
|
1918
|
+
// ../auth/src/tokenGrant.ts
|
|
1919
|
+
var requestClientCredentialsToken = async ({
|
|
1920
|
+
tokenEndpoint,
|
|
1921
|
+
baseUrl,
|
|
1922
|
+
params,
|
|
1923
|
+
buildError
|
|
1924
|
+
}) => {
|
|
1925
|
+
const tokenResponse = await fetch(tokenEndpoint, {
|
|
1926
|
+
method: "POST",
|
|
1927
|
+
headers: {
|
|
1928
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1929
|
+
},
|
|
1930
|
+
body: new URLSearchParams(params)
|
|
1931
|
+
});
|
|
1932
|
+
const [parseError, tokenData] = await catchError(tokenResponse.json());
|
|
1933
|
+
if (!tokenResponse.ok) {
|
|
1934
|
+
const data = parseError ? {} : tokenData ?? {};
|
|
1935
|
+
throw buildError({
|
|
1936
|
+
status: tokenResponse.status,
|
|
1937
|
+
errorType: data.error || "authentication_failed",
|
|
1938
|
+
errorDesc: data.error_description || "Unknown error"
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
if (parseError || !tokenData) {
|
|
1942
|
+
throw buildError({
|
|
1943
|
+
status: tokenResponse.status,
|
|
1944
|
+
errorType: "invalid_response",
|
|
1945
|
+
errorDesc: "Token endpoint returned a non-JSON response."
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
if (!tokenData.access_token) {
|
|
1949
|
+
throw buildError({
|
|
1950
|
+
status: tokenResponse.status,
|
|
1951
|
+
errorType: "invalid_response",
|
|
1952
|
+
errorDesc: "Token endpoint response did not include an access_token."
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
return {
|
|
1956
|
+
UIPATH_ACCESS_TOKEN: tokenData.access_token,
|
|
1957
|
+
UIPATH_URL: baseUrl
|
|
1958
|
+
};
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// ../auth/src/clientCredentials.ts
|
|
1962
|
+
class ClientCredentialsAuthenticationError extends Error {
|
|
1963
|
+
status;
|
|
1964
|
+
constructor(message, status) {
|
|
1965
|
+
super(message);
|
|
1966
|
+
this.name = "ClientCredentialsAuthenticationError";
|
|
1967
|
+
this.status = status;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
var buildClientCredentialsError = ({
|
|
1971
|
+
status,
|
|
1972
|
+
errorType,
|
|
1973
|
+
errorDesc
|
|
1974
|
+
}) => {
|
|
1975
|
+
let troubleshooting = "";
|
|
1976
|
+
if (errorType === "invalid_client" || status === 401) {
|
|
1977
|
+
troubleshooting = `
|
|
1978
|
+
|
|
1979
|
+
Troubleshooting:` + `
|
|
1980
|
+
• Verify your Client ID is correct` + `
|
|
1981
|
+
• Ensure your Client Secret hasn't expired` + `
|
|
1982
|
+
• Check that the external application is enabled in UiPath` + `
|
|
1983
|
+
• Confirm you're using the correct authority URL`;
|
|
1984
|
+
} else if (errorType === "invalid_scope") {
|
|
1985
|
+
troubleshooting = `
|
|
1986
|
+
|
|
1987
|
+
Troubleshooting:` + `
|
|
1988
|
+
• The requested scopes may not be available for your account` + `
|
|
1989
|
+
• Try using default scopes or contact your UiPath administrator`;
|
|
1990
|
+
}
|
|
1991
|
+
return new ClientCredentialsAuthenticationError(`Client Credentials authentication failed (${status})
|
|
1992
|
+
` + `Error: ${errorType}
|
|
1993
|
+
` + `Details: ${errorDesc}${troubleshooting}`, status);
|
|
1994
|
+
};
|
|
1995
|
+
var clientCredentialsLogin = async ({
|
|
1996
|
+
clientId,
|
|
1997
|
+
clientSecret,
|
|
1998
|
+
scope,
|
|
1999
|
+
authority
|
|
2000
|
+
}) => {
|
|
2001
|
+
const config = await resolveConfigAsync({
|
|
2002
|
+
customAuthority: authority,
|
|
2003
|
+
customClientId: clientId,
|
|
2004
|
+
customClientSecret: clientSecret,
|
|
2005
|
+
customScopes: scope
|
|
2006
|
+
});
|
|
2007
|
+
const params = {
|
|
2008
|
+
grant_type: "client_credentials",
|
|
2009
|
+
client_id: config.clientId,
|
|
2010
|
+
client_secret: config.clientSecret ?? ""
|
|
2011
|
+
};
|
|
2012
|
+
if (config.scopes.length > 0) {
|
|
2013
|
+
params.scope = config.scopes.join(" ");
|
|
2014
|
+
}
|
|
2015
|
+
return await requestClientCredentialsToken({
|
|
2016
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
2017
|
+
baseUrl: config.baseUrl,
|
|
2018
|
+
params,
|
|
2019
|
+
buildError: buildClientCredentialsError
|
|
2020
|
+
});
|
|
2021
|
+
};
|
|
2022
|
+
// ../auth/src/federatedCredentials.ts
|
|
2023
|
+
var JWT_BEARER_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
2024
|
+
|
|
2025
|
+
class FederatedCredentialsAuthenticationError extends Error {
|
|
2026
|
+
status;
|
|
2027
|
+
constructor(message, status) {
|
|
2028
|
+
super(message);
|
|
2029
|
+
this.name = "FederatedCredentialsAuthenticationError";
|
|
2030
|
+
this.status = status;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
var buildFederatedError = ({
|
|
2034
|
+
status,
|
|
2035
|
+
errorType,
|
|
2036
|
+
errorDesc
|
|
2037
|
+
}) => {
|
|
2038
|
+
let troubleshooting = "";
|
|
2039
|
+
if (errorType === "invalid_client" || errorType === "invalid_grant" || errorType === "invalid_request" || status === 401) {
|
|
2040
|
+
troubleshooting = `
|
|
2041
|
+
|
|
2042
|
+
Troubleshooting:` + `
|
|
2043
|
+
• Verify your Client ID matches the external application` + `
|
|
2044
|
+
• Confirm a federated credential is registered on the app whose` + `
|
|
2045
|
+
issuer, audience, and subject exactly match the token's claims` + `
|
|
2046
|
+
(list them: uip admin external-apps federated-credentials list <client-id>)` + `
|
|
2047
|
+
• Check the token was issued by the expected provider and has not expired` + `
|
|
2048
|
+
• Confirm you're using the correct authority URL`;
|
|
2049
|
+
} else if (errorType === "invalid_scope") {
|
|
2050
|
+
troubleshooting = `
|
|
2051
|
+
|
|
2052
|
+
Troubleshooting:` + `
|
|
2053
|
+
• The requested scopes may not be available for your application` + `
|
|
2054
|
+
• Try using default scopes or contact your UiPath administrator`;
|
|
2055
|
+
}
|
|
2056
|
+
return new FederatedCredentialsAuthenticationError(`Federated Credentials authentication failed (${status})
|
|
2057
|
+
` + `Error: ${errorType}
|
|
2058
|
+
` + `Details: ${errorDesc}${troubleshooting}`, status);
|
|
2059
|
+
};
|
|
2060
|
+
var federatedCredentialsLogin = async ({
|
|
2061
|
+
clientId,
|
|
2062
|
+
clientAssertion,
|
|
2063
|
+
scope,
|
|
2064
|
+
authority
|
|
2065
|
+
}) => {
|
|
2066
|
+
const config = await resolveConfigAsync({
|
|
2067
|
+
customAuthority: authority,
|
|
2068
|
+
customClientId: clientId,
|
|
2069
|
+
customClientAssertion: clientAssertion,
|
|
2070
|
+
customScopes: scope
|
|
2071
|
+
});
|
|
2072
|
+
const params = {
|
|
2073
|
+
grant_type: "client_credentials",
|
|
2074
|
+
client_id: config.clientId,
|
|
2075
|
+
client_assertion: clientAssertion,
|
|
2076
|
+
client_assertion_type: JWT_BEARER_ASSERTION_TYPE
|
|
2077
|
+
};
|
|
2078
|
+
if (config.scopes.length > 0) {
|
|
2079
|
+
params.scope = config.scopes.join(" ");
|
|
2080
|
+
}
|
|
2081
|
+
return await requestClientCredentialsToken({
|
|
2082
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
2083
|
+
baseUrl: config.baseUrl,
|
|
2084
|
+
params,
|
|
2085
|
+
buildError: buildFederatedError
|
|
2086
|
+
});
|
|
2087
|
+
};
|
|
2088
|
+
// ../auth/src/tenantSelection.ts
|
|
2089
|
+
var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId) => {
|
|
2090
|
+
const url = `${baseUrl}/${organizationId}/portal_/api/filtering/leftnav/tenantsAndOrganizationInfo`;
|
|
2091
|
+
const response = await fetch(url, {
|
|
2092
|
+
headers: {
|
|
2093
|
+
Authorization: `Bearer ${accessToken}`
|
|
2094
|
+
}
|
|
2095
|
+
});
|
|
2096
|
+
if (!response.ok) {
|
|
2097
|
+
if (response.status === 401) {
|
|
2098
|
+
throw new Error("Unauthorized: Invalid or expired access token");
|
|
2099
|
+
}
|
|
2100
|
+
const errorText = await response.text();
|
|
2101
|
+
throw new Error(`Failed to get tenants and organizations: ${response.status} ${errorText}`);
|
|
2102
|
+
}
|
|
2103
|
+
const data = await response.json();
|
|
2104
|
+
return data;
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
// ../auth/src/selectTenant.ts
|
|
2108
|
+
var TENANT_SELECTION_REQUIRED_CODE = "TENANT_SELECTION_REQUIRED";
|
|
2109
|
+
var INVALID_TENANT_CODE = "INVALID_TENANT";
|
|
2110
|
+
|
|
2111
|
+
class TenantSelectionError extends Error {
|
|
2112
|
+
availableTenants;
|
|
2113
|
+
organizationName;
|
|
2114
|
+
constructor(message, organizationName, availableTenants) {
|
|
2115
|
+
super(message);
|
|
2116
|
+
this.organizationName = organizationName;
|
|
2117
|
+
this.availableTenants = availableTenants;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
class TenantSelectionRequiredError extends TenantSelectionError {
|
|
2122
|
+
code = TENANT_SELECTION_REQUIRED_CODE;
|
|
2123
|
+
constructor(organizationName, availableTenants) {
|
|
2124
|
+
super(`Multiple tenants available in organization "${organizationName}"; none selected.`, organizationName, availableTenants);
|
|
2125
|
+
this.name = "TenantSelectionRequiredError";
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
class InvalidTenantError extends TenantSelectionError {
|
|
2130
|
+
code = INVALID_TENANT_CODE;
|
|
2131
|
+
requestedTenant;
|
|
2132
|
+
constructor(requestedTenant, organizationName, availableTenants) {
|
|
2133
|
+
super(`Invalid tenant requested: "${requestedTenant}"
|
|
2134
|
+
` + `Organization: ${organizationName}
|
|
2135
|
+
` + `Available tenants: ${availableTenants.join(", ")}`, organizationName, availableTenants);
|
|
2136
|
+
this.name = "InvalidTenantError";
|
|
2137
|
+
this.requestedTenant = requestedTenant;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
var TENANT_SELECTION_CODES = new Set([
|
|
2141
|
+
TENANT_SELECTION_REQUIRED_CODE,
|
|
2142
|
+
INVALID_TENANT_CODE
|
|
2143
|
+
]);
|
|
2144
|
+
function isTenantSelectionError(error) {
|
|
2145
|
+
if (!(error instanceof Object) || !("code" in error)) {
|
|
2146
|
+
return false;
|
|
2147
|
+
}
|
|
2148
|
+
const code = error.code;
|
|
2149
|
+
return typeof code === "string" && TENANT_SELECTION_CODES.has(code) && Array.isArray(error.availableTenants);
|
|
2150
|
+
}
|
|
2151
|
+
var selectTenantWithDeps = async (baseUrl, accessToken, organizationId, targetTenantName, interactive, deps = {}) => {
|
|
2152
|
+
const {
|
|
2153
|
+
fetchTenantsAndOrgs = fetchTenantsAndOrganizations,
|
|
2154
|
+
selectFromList
|
|
2155
|
+
} = deps;
|
|
2156
|
+
const data = await fetchTenantsAndOrgs(baseUrl, accessToken, organizationId);
|
|
2157
|
+
const { tenants, organization } = data;
|
|
2158
|
+
if (!tenants || tenants.length === 0) {
|
|
2159
|
+
throw new Error("No tenants found for this organization");
|
|
2160
|
+
}
|
|
2161
|
+
const tenantNames = tenants.map((tenant) => tenant.name);
|
|
2162
|
+
let selectedIndex;
|
|
2163
|
+
if (targetTenantName !== undefined) {
|
|
2164
|
+
selectedIndex = tenants.findIndex((tenant) => tenant.name === targetTenantName);
|
|
2165
|
+
if (selectedIndex === -1) {
|
|
2166
|
+
throw new InvalidTenantError(targetTenantName, organization.name, tenantNames);
|
|
2167
|
+
}
|
|
2168
|
+
} else if (tenants.length === 1) {
|
|
2169
|
+
selectedIndex = 0;
|
|
2170
|
+
} else if (interactive) {
|
|
2171
|
+
if (!selectFromList) {
|
|
2172
|
+
throw new Error("Interactive tenant selection requires a `selectFromList` callback. " + "Pass one via the `deps` argument of `selectTenantWithDeps`.");
|
|
2173
|
+
}
|
|
2174
|
+
selectedIndex = await selectFromList(tenantNames, "Select a tenant:");
|
|
2175
|
+
} else {
|
|
2176
|
+
throw new TenantSelectionRequiredError(organization.name, tenantNames);
|
|
2177
|
+
}
|
|
2178
|
+
const selectedTenant = tenants[selectedIndex];
|
|
2179
|
+
return [selectedTenant.name, selectedTenant.id, organization.name];
|
|
2180
|
+
};
|
|
2181
|
+
|
|
2182
|
+
// ../auth/src/types.ts
|
|
2183
|
+
var AUTH_FLOW_ENV_VAR = "UIPATH_AUTH_FLOW";
|
|
2184
|
+
|
|
2185
|
+
// ../auth/src/interactive.ts
|
|
2186
|
+
var interactiveLoginWithDeps = async (options, deps) => {
|
|
2187
|
+
const {
|
|
2188
|
+
resolveConfig = resolveConfigAsync,
|
|
2189
|
+
clientCredentials = clientCredentialsLogin,
|
|
2190
|
+
federatedCredentials = federatedCredentialsLogin,
|
|
2191
|
+
auth = authenticate,
|
|
2192
|
+
saveEnvFile = saveEnvFileAsync,
|
|
2193
|
+
loadEnvFile = loadEnvFileAsync,
|
|
2194
|
+
getFs = getFileSystem,
|
|
2195
|
+
jwtParser = parseJWT,
|
|
2196
|
+
issuerAsserter = assertIssuerMatchesAuthority,
|
|
2197
|
+
tenantSelector = (baseUrl, accessToken, organizationId, tenantName, interactive2) => selectTenantWithDeps(baseUrl, accessToken, organizationId, tenantName, interactive2, { selectFromList: options.selectFromList })
|
|
2198
|
+
} = deps;
|
|
2199
|
+
const {
|
|
2200
|
+
envFilePath,
|
|
2201
|
+
scope,
|
|
2202
|
+
authority,
|
|
2203
|
+
clientId,
|
|
2204
|
+
clientSecret,
|
|
2205
|
+
clientAssertion,
|
|
2206
|
+
tenant,
|
|
2207
|
+
organization,
|
|
2208
|
+
interactive,
|
|
2209
|
+
onEvent,
|
|
2210
|
+
timeoutMs,
|
|
2211
|
+
noBrowser,
|
|
2212
|
+
signal
|
|
2213
|
+
} = options;
|
|
2214
|
+
const emit = (event) => {
|
|
2215
|
+
if (!onEvent)
|
|
2216
|
+
return;
|
|
2217
|
+
try {
|
|
2218
|
+
onEvent(event);
|
|
2219
|
+
} catch {}
|
|
2220
|
+
};
|
|
2221
|
+
const deliverAuthUrl = (url) => {
|
|
2222
|
+
try {
|
|
2223
|
+
onEvent?.({ type: "auth-url", url });
|
|
2224
|
+
} catch (error) {
|
|
2225
|
+
throw new Error("Failed to deliver the authorize URL to the onEvent subscriber; " + "cannot continue headless login.", { cause: error });
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
const config = await resolveConfig({
|
|
2229
|
+
customAuthority: authority,
|
|
2230
|
+
customClientId: clientId,
|
|
2231
|
+
customClientSecret: clientSecret,
|
|
2232
|
+
customClientAssertion: clientAssertion
|
|
2233
|
+
});
|
|
2234
|
+
const { clientSecret: resolvedSecret } = config;
|
|
2235
|
+
if (noBrowser && !resolvedSecret && !clientAssertion && !onEvent) {
|
|
2236
|
+
throw new Error("noBrowser login requires an onEvent subscriber to receive the " + "auth-url event — the authorize URL is delivered through it.");
|
|
2237
|
+
}
|
|
2238
|
+
const authFlow = clientAssertion ? "federated_credentials" : resolvedSecret ? "client_credentials" : "authorization_code";
|
|
2239
|
+
const authPromise = clientAssertion ? (async () => {
|
|
2240
|
+
return await federatedCredentials({
|
|
2241
|
+
clientId: config.clientId,
|
|
2242
|
+
clientAssertion,
|
|
2243
|
+
authority: config.baseUrl,
|
|
2244
|
+
scope
|
|
2245
|
+
});
|
|
2246
|
+
})() : resolvedSecret ? (async () => {
|
|
2247
|
+
return await clientCredentials({
|
|
2248
|
+
clientId: config.clientId,
|
|
2249
|
+
clientSecret: resolvedSecret,
|
|
2250
|
+
authority: config.baseUrl,
|
|
2251
|
+
scope
|
|
2252
|
+
});
|
|
2253
|
+
})() : (async () => {
|
|
2254
|
+
const authTokens = await auth({
|
|
2255
|
+
baseUrl: authority,
|
|
2256
|
+
clientId,
|
|
2257
|
+
clientSecret,
|
|
2258
|
+
scope,
|
|
2259
|
+
organization,
|
|
2260
|
+
timeoutMs,
|
|
2261
|
+
noBrowser,
|
|
2262
|
+
signal,
|
|
2263
|
+
onAuthUrl: noBrowser ? (url) => deliverAuthUrl(url) : undefined
|
|
2264
|
+
});
|
|
2265
|
+
return {
|
|
2266
|
+
UIPATH_ACCESS_TOKEN: authTokens.accessToken,
|
|
2267
|
+
UIPATH_REFRESH_TOKEN: authTokens.refreshToken
|
|
2268
|
+
};
|
|
2269
|
+
})();
|
|
2270
|
+
const [authError, tokens] = await catchError(authPromise);
|
|
2271
|
+
if (authError) {
|
|
2272
|
+
throw authError;
|
|
2273
|
+
}
|
|
2274
|
+
issuerAsserter(tokens.UIPATH_ACCESS_TOKEN, config.baseUrl);
|
|
2275
|
+
const credentials = {
|
|
2276
|
+
...tokens,
|
|
2277
|
+
UIPATH_URL: config.baseUrl,
|
|
2278
|
+
[AUTH_FLOW_ENV_VAR]: authFlow
|
|
2279
|
+
};
|
|
2280
|
+
if (authFlow !== "authorization_code") {
|
|
2281
|
+
credentials.UIPATH_REFRESH_TOKEN = undefined;
|
|
2282
|
+
}
|
|
2283
|
+
try {
|
|
2284
|
+
const tokenData = jwtParser(tokens.UIPATH_ACCESS_TOKEN);
|
|
2285
|
+
const orgId = tokenData.prtId || tokenData.organizationId || tokenData.prt_id;
|
|
2286
|
+
if (typeof orgId !== "string" || orgId.length === 0) {
|
|
2287
|
+
throw new Error("Organization ID not available from login token. Cannot establish an active tenant.");
|
|
2288
|
+
}
|
|
2289
|
+
credentials.UIPATH_ORGANIZATION_ID = orgId;
|
|
2290
|
+
if (organization) {
|
|
2291
|
+
credentials.UIPATH_ORGANIZATION_NAME = organization;
|
|
2292
|
+
}
|
|
2293
|
+
const [tenantName, tenantId, orgName] = await tenantSelector(config.baseUrl, tokens.UIPATH_ACCESS_TOKEN, orgId, tenant, interactive);
|
|
2294
|
+
credentials.UIPATH_ORGANIZATION_NAME = orgName;
|
|
2295
|
+
credentials.UIPATH_TENANT_NAME = tenantName;
|
|
2296
|
+
credentials.UIPATH_TENANT_ID = tenantId;
|
|
2297
|
+
} catch (error) {
|
|
2298
|
+
if (!isTenantSelectionError(error)) {
|
|
2299
|
+
emit({
|
|
2300
|
+
type: "tenant-fetch-failed",
|
|
2301
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
throw error;
|
|
2305
|
+
}
|
|
2306
|
+
const fs = getFs();
|
|
2307
|
+
const requestedPath = envFilePath ?? DEFAULT_ENV_FILENAME;
|
|
2308
|
+
let savePath = requestedPath;
|
|
2309
|
+
let savedLocally = false;
|
|
2310
|
+
if (!fs.path.isAbsolute(savePath)) {
|
|
2311
|
+
let searchDir = fs.env.cwd();
|
|
2312
|
+
while (true) {
|
|
2313
|
+
const candidate = fs.path.join(searchDir, savePath);
|
|
2314
|
+
if (await fs.exists(candidate)) {
|
|
2315
|
+
try {
|
|
2316
|
+
const existing = await loadEnvFile({
|
|
2317
|
+
envPath: candidate
|
|
2318
|
+
});
|
|
2319
|
+
if (existing.UIPATH_ACCESS_TOKEN) {
|
|
2320
|
+
savePath = candidate;
|
|
2321
|
+
savedLocally = true;
|
|
2322
|
+
break;
|
|
2323
|
+
}
|
|
2324
|
+
} catch {}
|
|
2325
|
+
}
|
|
2326
|
+
const parentDir = fs.path.dirname(searchDir);
|
|
2327
|
+
if (parentDir === searchDir) {
|
|
2328
|
+
break;
|
|
2329
|
+
}
|
|
2330
|
+
searchDir = parentDir;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
let saveRelease;
|
|
2334
|
+
try {
|
|
2335
|
+
if (typeof fs.acquireLock === "function") {
|
|
2336
|
+
saveRelease = await fs.acquireLock(savePath);
|
|
2337
|
+
}
|
|
2338
|
+
} catch {
|
|
2339
|
+
saveRelease = undefined;
|
|
2340
|
+
}
|
|
2341
|
+
try {
|
|
2342
|
+
await saveEnvFile({
|
|
2343
|
+
envPath: savePath,
|
|
2344
|
+
data: credentials,
|
|
2345
|
+
merge: true
|
|
2346
|
+
});
|
|
2347
|
+
} finally {
|
|
2348
|
+
if (saveRelease) {
|
|
2349
|
+
await saveRelease().catch(() => {});
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
const reportedPath = fs.path.isAbsolute(savePath) ? savePath : fs.path.join(fs.env.homedir(), savePath);
|
|
2353
|
+
emit({
|
|
2354
|
+
type: "saved",
|
|
2355
|
+
path: reportedPath,
|
|
2356
|
+
locality: savedLocally ? "local" : "global"
|
|
2357
|
+
});
|
|
2358
|
+
return credentials;
|
|
2359
|
+
};
|
|
2360
|
+
var interactiveLogin = async (options) => {
|
|
2361
|
+
return interactiveLoginWithDeps(options, {});
|
|
2362
|
+
};
|
|
2363
|
+
// ../auth/src/logout.ts
|
|
2364
|
+
async function logoutWithDeps(options, deps = {}) {
|
|
2365
|
+
const {
|
|
2366
|
+
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
2367
|
+
getFileSystem: getFs = getFileSystem,
|
|
2368
|
+
clearBreaker = clearRefreshBreaker,
|
|
2369
|
+
getActiveProfileFilePath = getActiveAuthProfileFilePath
|
|
2370
|
+
} = deps;
|
|
2371
|
+
const fs = getFs();
|
|
2372
|
+
const { absolutePath } = await resolveEnvFilePath(options.file ?? getActiveProfileFilePath());
|
|
2373
|
+
if (absolutePath && await fs.exists(absolutePath)) {
|
|
2374
|
+
let release;
|
|
2375
|
+
try {
|
|
2376
|
+
if (typeof fs.acquireLock === "function") {
|
|
2377
|
+
release = await fs.acquireLock(absolutePath);
|
|
2378
|
+
}
|
|
2379
|
+
} catch {
|
|
2380
|
+
release = undefined;
|
|
2381
|
+
}
|
|
2382
|
+
try {
|
|
2383
|
+
await fs.rm(absolutePath);
|
|
2384
|
+
await clearBreaker(absolutePath);
|
|
2385
|
+
} finally {
|
|
2386
|
+
if (release) {
|
|
2387
|
+
await release().catch(() => {});
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
return {
|
|
2391
|
+
success: true,
|
|
2392
|
+
message: `Logged out successfully. Removed ${absolutePath}`,
|
|
2393
|
+
removedPath: absolutePath
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
return {
|
|
2397
|
+
success: false,
|
|
2398
|
+
message: "No credentials file found. You are already logged out.",
|
|
2399
|
+
reason: "no_credentials_file"
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
async function logout(options) {
|
|
2403
|
+
return logoutWithDeps(options);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
// ../auth/src/index.ts
|
|
2407
|
+
var authenticate = async ({
|
|
2408
|
+
baseUrl,
|
|
2409
|
+
clientId,
|
|
2410
|
+
clientSecret,
|
|
2411
|
+
redirectUri,
|
|
2412
|
+
scope,
|
|
2413
|
+
organization,
|
|
2414
|
+
timeoutMs,
|
|
2415
|
+
noBrowser,
|
|
2416
|
+
onAuthUrl,
|
|
2417
|
+
signal
|
|
2418
|
+
}) => {
|
|
2419
|
+
const config = await resolveConfigAsync({
|
|
2420
|
+
customAuthority: baseUrl,
|
|
2421
|
+
customClientId: clientId,
|
|
2422
|
+
customClientSecret: clientSecret,
|
|
2423
|
+
customScopes: scope
|
|
2424
|
+
});
|
|
2425
|
+
const {
|
|
2426
|
+
authorizationEndpoint,
|
|
2427
|
+
tokenEndpoint,
|
|
2428
|
+
clientId: resolvedClientId,
|
|
2429
|
+
clientSecret: resolvedClientSecret,
|
|
2430
|
+
scopes: resolvedScopes
|
|
2431
|
+
} = config;
|
|
2432
|
+
const { code_challenge, code_verifier, state } = await getOidcParams();
|
|
2433
|
+
let effectiveRedirectUri;
|
|
2434
|
+
if (redirectUri) {
|
|
2435
|
+
effectiveRedirectUri = redirectUri.toString();
|
|
2436
|
+
} else if (isBrowser()) {
|
|
2437
|
+
const origin = getGlobalThis()?.window?.location?.origin ?? "";
|
|
2438
|
+
effectiveRedirectUri = `${origin}/oidc/login`;
|
|
2439
|
+
} else {
|
|
2440
|
+
effectiveRedirectUri = DEFAULT_REDIRECT_URI;
|
|
2441
|
+
}
|
|
2442
|
+
const effectiveRedirectUriUrl = new URL(effectiveRedirectUri);
|
|
2443
|
+
const authParams = new URLSearchParams({
|
|
2444
|
+
client_id: resolvedClientId,
|
|
2445
|
+
redirect_uri: effectiveRedirectUriUrl.toString(),
|
|
2446
|
+
response_type: "code",
|
|
2447
|
+
scope: resolvedScopes.join(" "),
|
|
2448
|
+
code_challenge,
|
|
2449
|
+
code_challenge_method: "S256",
|
|
2450
|
+
state
|
|
2451
|
+
});
|
|
2452
|
+
if (organization) {
|
|
2453
|
+
if (/[\s:]/.test(organization)) {
|
|
2454
|
+
throw new Error(`Invalid organization name: "${organization}". Whitespace and colons are not allowed; use the organization's logical name (e.g. "my-org").`);
|
|
2455
|
+
}
|
|
2456
|
+
authParams.set("acr_values", `tenantName:${organization}`);
|
|
2457
|
+
}
|
|
2458
|
+
const authUrl = `${authorizationEndpoint}?${authParams.toString()}`;
|
|
2459
|
+
let strategy;
|
|
2460
|
+
if (isBrowser()) {
|
|
2461
|
+
const { BrowserAuthStrategy } = await import("./browser-strategy-v4n9zxyf.js");
|
|
2462
|
+
strategy = new BrowserAuthStrategy;
|
|
2463
|
+
} else {
|
|
2464
|
+
const { NodeAuthStrategy } = await import("./node-strategy-0s9nhcmn.js");
|
|
2465
|
+
strategy = new NodeAuthStrategy;
|
|
2466
|
+
}
|
|
2467
|
+
const code = await strategy.execute(authUrl, effectiveRedirectUriUrl, state, {
|
|
2468
|
+
timeoutMs,
|
|
2469
|
+
noBrowser,
|
|
2470
|
+
onAuthUrl,
|
|
2471
|
+
signal
|
|
2472
|
+
});
|
|
2473
|
+
return await exchangeCodeForTokens({
|
|
2474
|
+
code,
|
|
2475
|
+
codeVerifier: code_verifier,
|
|
2476
|
+
redirectUri: effectiveRedirectUriUrl,
|
|
2477
|
+
clientId: resolvedClientId,
|
|
2478
|
+
clientSecret: resolvedClientSecret,
|
|
2479
|
+
tokenEndpoint
|
|
2480
|
+
});
|
|
2481
|
+
};
|
|
2482
|
+
|
|
2483
|
+
export { setAuthFileConfig, InvalidBaseUrlError, DEFAULT_AUTH_PROFILE, AuthProfileValidationError, normalizeAuthProfileName, setActiveAuthProfile, clearActiveAuthProfile, getActiveAuthProfile, runWithAuthProfile, resolveAuthProfileFilePath, getActiveAuthProfileFilePath, parseJWT, ENV_AUTH_ENABLE_VAR, ENFORCE_ROBOT_AUTH_VAR, ENV_AUTH_VARS, EnvAuthConfigError, isEnvAuthEnabled, isRobotAuthEnforced, readAuthFromEnv, refreshTokenFingerprint, loadRefreshBreaker, saveRefreshBreaker, clearRefreshBreaker, registerRobotClientLoader, TokenRefreshOAuthError, isTokenRefreshOAuthFailure, refreshAccessToken, DEFAULT_AUTH_FILENAME, DEFAULT_ENV_FILENAME, resolveEnvFileLocationAsync, resolveEnvFilePathAsync, loadEnvFileAsync, saveEnvFileAsync, LoginStatusSource, getLoginStatusAsync, getLoginStatusWithDeps, getAuthContext, getAuthEnv, ClientCredentialsAuthenticationError, clientCredentialsLogin, JWT_BEARER_ASSERTION_TYPE, FederatedCredentialsAuthenticationError, federatedCredentialsLogin, fetchTenantsAndOrganizations, TENANT_SELECTION_REQUIRED_CODE, INVALID_TENANT_CODE, TenantSelectionError, TenantSelectionRequiredError, InvalidTenantError, isTenantSelectionError, selectTenantWithDeps, AUTH_FLOW_ENV_VAR, interactiveLoginWithDeps, interactiveLogin, logoutWithDeps, logout, authenticate };
|
|
2484
|
+
|
|
2485
|
+
//# debugId=1B99BDDE9F71605864756E2164756E21
|