@uipath/auth 1.0.4 → 1.195.0
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/authContext.d.ts +17 -0
- package/dist/catch-error.d.ts +5 -0
- package/dist/clientCredentials.d.ts +9 -0
- package/dist/config.d.ts +73 -0
- package/dist/constants.d.ts +10 -0
- package/dist/envAuth.d.ts +75 -0
- package/dist/getBaseHtml.d.ts +7 -0
- package/dist/index.browser.d.ts +10 -0
- package/dist/index.browser.js +314 -115
- package/dist/index.d.ts +27 -0
- package/dist/index.js +528 -221
- package/dist/interactive.d.ts +93 -0
- package/dist/loginStatus.d.ts +73 -0
- package/dist/logout.d.ts +44 -0
- package/dist/oidc.d.ts +5 -0
- package/dist/robotClientFallback.d.ts +63 -0
- package/dist/selectTenant.d.ts +8 -0
- package/dist/server.d.ts +8 -0
- package/dist/strategies/auth-strategy.d.ts +3 -0
- package/dist/strategies/browser-strategy.d.ts +4 -0
- package/dist/strategies/node-strategy.d.ts +4 -0
- package/dist/tenantSelection.d.ts +17 -0
- package/dist/tokenExchange.d.ts +13 -0
- package/dist/tokenRefresh.d.ts +27 -0
- package/dist/types.d.ts +5 -0
- package/dist/utils/envFile.d.ts +101 -0
- package/dist/utils/jwt.d.ts +43 -0
- package/dist/utils/platform.d.ts +36 -0
- package/package.json +38 -50
package/dist/index.browser.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/loginStatus.ts
|
|
10
|
-
import { getFileSystem as
|
|
10
|
+
import { getFileSystem as getFileSystem3 } from "@uipath/filesystem";
|
|
11
11
|
|
|
12
12
|
// src/catch-error.ts
|
|
13
13
|
function isPromiseLike(value) {
|
|
@@ -69,30 +69,7 @@ class InvalidBaseUrlError extends Error {
|
|
|
69
69
|
this.name = "InvalidBaseUrlError";
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
var DEFAULT_SCOPES = [
|
|
73
|
-
"offline_access",
|
|
74
|
-
"ProcessMining",
|
|
75
|
-
"OrchestratorApiUserAccess",
|
|
76
|
-
"StudioWebBackend",
|
|
77
|
-
"IdentityServerApi",
|
|
78
|
-
"ConnectionService",
|
|
79
|
-
"DataService",
|
|
80
|
-
"DataServiceApiUserAccess",
|
|
81
|
-
"DocumentUnderstanding",
|
|
82
|
-
"EnterpriseContextService",
|
|
83
|
-
"Directory",
|
|
84
|
-
"JamJamApi",
|
|
85
|
-
"LLMGateway",
|
|
86
|
-
"LLMOps",
|
|
87
|
-
"OMS",
|
|
88
|
-
"RCS.FolderAuthorization",
|
|
89
|
-
"RCS.TagsManagement",
|
|
90
|
-
"TestmanagerApiUserAccess",
|
|
91
|
-
"AutomationSolutions",
|
|
92
|
-
"StudioWebTypeCacheService",
|
|
93
|
-
"Docs.GPT.Search",
|
|
94
|
-
"Insights"
|
|
95
|
-
];
|
|
72
|
+
var DEFAULT_SCOPES = ["openid", "profile", "offline_access"];
|
|
96
73
|
var normalizeAndValidateBaseUrl = (rawUrl) => {
|
|
97
74
|
let baseUrl = rawUrl;
|
|
98
75
|
if (baseUrl.endsWith("/identity_/")) {
|
|
@@ -142,7 +119,8 @@ var resolveConfigAsync = async ({
|
|
|
142
119
|
if (!clientSecret && fileAuth.clientSecret) {
|
|
143
120
|
clientSecret = fileAuth.clientSecret;
|
|
144
121
|
}
|
|
145
|
-
const
|
|
122
|
+
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && Boolean(clientSecret);
|
|
123
|
+
const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : isExternalAppAuth ? [] : DEFAULT_SCOPES;
|
|
146
124
|
return {
|
|
147
125
|
clientId,
|
|
148
126
|
clientSecret,
|
|
@@ -234,6 +212,7 @@ var getTokenExpiration = (accessToken) => {
|
|
|
234
212
|
|
|
235
213
|
// src/envAuth.ts
|
|
236
214
|
var ENV_AUTH_ENABLE_VAR = "UIPATH_CLI_ENABLE_ENV_AUTH";
|
|
215
|
+
var ENFORCE_ROBOT_AUTH_VAR = "UIPATH_CLI_ENFORCE_ROBOT_AUTH";
|
|
237
216
|
var ENV_AUTH_VARS = {
|
|
238
217
|
token: "UIPATH_CLI_AUTH_TOKEN",
|
|
239
218
|
organizationName: "UIPATH_CLI_ORGANIZATION_NAME",
|
|
@@ -249,6 +228,7 @@ class EnvAuthConfigError extends Error {
|
|
|
249
228
|
}
|
|
250
229
|
}
|
|
251
230
|
var isEnvAuthEnabled = () => process.env[ENV_AUTH_ENABLE_VAR] === "true";
|
|
231
|
+
var isRobotAuthEnforced = () => process.env[ENFORCE_ROBOT_AUTH_VAR] === "true";
|
|
252
232
|
var requireEnv = (name) => {
|
|
253
233
|
const value = process.env[name];
|
|
254
234
|
if (!value) {
|
|
@@ -292,6 +272,7 @@ var readAuthFromEnv = () => {
|
|
|
292
272
|
};
|
|
293
273
|
|
|
294
274
|
// src/robotClientFallback.ts
|
|
275
|
+
import { getFileSystem } from "@uipath/filesystem";
|
|
295
276
|
var DEFAULT_TIMEOUT_MS = 1000;
|
|
296
277
|
var CLOSE_TIMEOUT_MS = 500;
|
|
297
278
|
var NOTICE_SENTINEL = Symbol.for("@uipath/auth/robotFallbackNoticePrinted");
|
|
@@ -303,6 +284,35 @@ var printNoticeOnce = () => {
|
|
|
303
284
|
catchError(() => process.stderr.write(`Using UiPath Robot credentials. Run 'uip login' for a dedicated session.
|
|
304
285
|
`));
|
|
305
286
|
};
|
|
287
|
+
var ROBOT_USER_SERVICES_PIPE = "UiPathUserServices";
|
|
288
|
+
var ROBOT_USER_SERVICES_ALTERNATE_PIPE = `${ROBOT_USER_SERVICES_PIPE}Alternate`;
|
|
289
|
+
var PIPE_NAME_MAX_LENGTH = 103;
|
|
290
|
+
var getRobotIpcPipeNames = async () => {
|
|
291
|
+
const fs = getFileSystem();
|
|
292
|
+
const username = fs.env.getenv("USER") ?? fs.env.getenv("USERNAME");
|
|
293
|
+
if (!username) {
|
|
294
|
+
throw new Error("Unable to determine current username");
|
|
295
|
+
}
|
|
296
|
+
const tempPath = fs.env.getenv("TMPDIR") ?? "/tmp/";
|
|
297
|
+
return [ROBOT_USER_SERVICES_PIPE, ROBOT_USER_SERVICES_ALTERNATE_PIPE].map((baseName) => fs.path.join(tempPath, `${baseName}_${username}`).substring(0, PIPE_NAME_MAX_LENGTH));
|
|
298
|
+
};
|
|
299
|
+
var defaultIsRobotIpcAvailable = async () => {
|
|
300
|
+
if (process.platform === "win32") {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
const [pipeNamesError, pipeNames] = await catchError(getRobotIpcPipeNames());
|
|
304
|
+
if (pipeNamesError || !pipeNames) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
const fs = getFileSystem();
|
|
308
|
+
for (const pipeName of pipeNames) {
|
|
309
|
+
const [existsError, exists] = await catchError(fs.exists(pipeName));
|
|
310
|
+
if (!existsError && exists === true) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
};
|
|
306
316
|
var withTimeout = (promise, timeoutMs) => new Promise((resolve, reject) => {
|
|
307
317
|
const timer = setTimeout(() => reject(new Error(`Robot IPC call timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
308
318
|
promise.then((value) => {
|
|
@@ -334,14 +344,20 @@ var defaultLoadModule = async () => {
|
|
|
334
344
|
var tryRobotClientFallback = async (options = {}) => {
|
|
335
345
|
if (isBrowser())
|
|
336
346
|
return;
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
347
|
+
if (!options.force) {
|
|
348
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (process.env.UIPATH_URL) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
342
354
|
}
|
|
343
355
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
356
|
+
const isRobotIpcAvailable = options.isRobotIpcAvailable ?? defaultIsRobotIpcAvailable;
|
|
344
357
|
const loadModule = options.loadModule ?? defaultLoadModule;
|
|
358
|
+
if (!await isRobotIpcAvailable()) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
345
361
|
const mod = await loadModule();
|
|
346
362
|
if (!mod)
|
|
347
363
|
return;
|
|
@@ -441,7 +457,7 @@ var refreshAccessToken = async ({
|
|
|
441
457
|
};
|
|
442
458
|
|
|
443
459
|
// src/utils/envFile.ts
|
|
444
|
-
import { getFileSystem } from "@uipath/filesystem";
|
|
460
|
+
import { getFileSystem as getFileSystem2 } from "@uipath/filesystem";
|
|
445
461
|
var DEFAULT_AUTH_FILENAME = AUTH_FILENAME;
|
|
446
462
|
var DEFAULT_ENV_FILENAME = `${UIPATH_HOME_DIR}/${AUTH_FILENAME}`;
|
|
447
463
|
var KNOWN_ERROR_CODES = new Set([
|
|
@@ -488,7 +504,7 @@ var probeAsync = async (fs, candidate) => {
|
|
|
488
504
|
}
|
|
489
505
|
};
|
|
490
506
|
var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME) => {
|
|
491
|
-
const fs =
|
|
507
|
+
const fs = getFileSystem2();
|
|
492
508
|
if (fs.path.isAbsolute(envFilePath)) {
|
|
493
509
|
const probe2 = await probeAsync(fs, envFilePath);
|
|
494
510
|
return probe2.exists ? { exists: true, absolutePath: envFilePath, source: "absolute" } : {
|
|
@@ -539,7 +555,7 @@ var resolveEnvFilePathAsync = async (envFilePath = DEFAULT_ENV_FILENAME) => {
|
|
|
539
555
|
};
|
|
540
556
|
};
|
|
541
557
|
var loadEnvFileAsync = async ({ envPath }) => {
|
|
542
|
-
const fs =
|
|
558
|
+
const fs = getFileSystem2();
|
|
543
559
|
const absolutePath = fs.path.isAbsolute(envPath) ? envPath : fs.path.join(fs.env.cwd(), envPath);
|
|
544
560
|
if (!await fs.exists(absolutePath)) {
|
|
545
561
|
throw new Error(`Environment file not found: ${envPath}`);
|
|
@@ -573,7 +589,7 @@ var saveEnvFileAsync = async ({
|
|
|
573
589
|
data,
|
|
574
590
|
merge = true
|
|
575
591
|
}) => {
|
|
576
|
-
const fs =
|
|
592
|
+
const fs = getFileSystem2();
|
|
577
593
|
const absolutePath = fs.path.isAbsolute(envPath) ? envPath : fs.path.join(fs.env.homedir(), envPath);
|
|
578
594
|
let existingData = {};
|
|
579
595
|
if (merge && await fs.exists(absolutePath)) {
|
|
@@ -613,20 +629,167 @@ function normalizeTokenRefreshFailure() {
|
|
|
613
629
|
function normalizeTokenRefreshUnavailableFailure() {
|
|
614
630
|
return "token refresh failed before authentication completed";
|
|
615
631
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
632
|
+
function errorMessage(error) {
|
|
633
|
+
return error instanceof Error ? error.message : String(error);
|
|
634
|
+
}
|
|
635
|
+
function computeExpirationThreshold(ensureTokenValidityMinutes) {
|
|
636
|
+
return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
|
|
637
|
+
}
|
|
638
|
+
async function runRefreshLocked(inputs) {
|
|
639
|
+
const {
|
|
640
|
+
absolutePath,
|
|
641
|
+
refreshToken: callerRefreshToken,
|
|
642
|
+
customAuthority,
|
|
643
|
+
ensureTokenValidityMinutes,
|
|
644
|
+
loadEnvFile,
|
|
645
|
+
saveEnvFile,
|
|
646
|
+
refreshFn,
|
|
647
|
+
resolveConfig
|
|
648
|
+
} = inputs;
|
|
649
|
+
const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
650
|
+
let fresh;
|
|
651
|
+
try {
|
|
652
|
+
fresh = await loadEnvFile({ envPath: absolutePath });
|
|
653
|
+
} catch (error) {
|
|
654
|
+
return {
|
|
655
|
+
kind: "fail",
|
|
656
|
+
status: {
|
|
657
|
+
loginStatus: "Refresh Failed",
|
|
658
|
+
hint: "Could not read the auth file while refreshing. Retry, or run 'uip login' to re-authenticate.",
|
|
659
|
+
tokenRefresh: {
|
|
660
|
+
attempted: false,
|
|
661
|
+
success: false,
|
|
662
|
+
errorMessage: `auth file read failed: ${errorMessage(error)}`
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
619
666
|
}
|
|
620
|
-
const
|
|
667
|
+
const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
|
|
668
|
+
const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
|
|
669
|
+
if (freshAccess && freshExp && freshExp > expirationThreshold) {
|
|
670
|
+
return {
|
|
671
|
+
kind: "ok",
|
|
672
|
+
accessToken: freshAccess,
|
|
673
|
+
refreshToken: fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken,
|
|
674
|
+
expiration: freshExp,
|
|
675
|
+
tokenRefresh: { attempted: false, success: true }
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
const tokenForIdP = fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken;
|
|
679
|
+
let refreshedAccess;
|
|
680
|
+
let refreshedRefresh;
|
|
681
|
+
try {
|
|
682
|
+
const config = await resolveConfig({ customAuthority });
|
|
683
|
+
const refreshed = await refreshFn({
|
|
684
|
+
refreshToken: tokenForIdP,
|
|
685
|
+
tokenEndpoint: config.tokenEndpoint,
|
|
686
|
+
clientId: config.clientId,
|
|
687
|
+
expectedAuthority: customAuthority
|
|
688
|
+
});
|
|
689
|
+
refreshedAccess = refreshed.accessToken;
|
|
690
|
+
refreshedRefresh = refreshed.refreshToken;
|
|
691
|
+
} catch (error) {
|
|
692
|
+
const isOAuthFailure = isTokenRefreshOAuthFailure(error);
|
|
693
|
+
const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
|
|
694
|
+
const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
|
|
695
|
+
return {
|
|
696
|
+
kind: "fail",
|
|
697
|
+
status: {
|
|
698
|
+
loginStatus: "Refresh Failed",
|
|
699
|
+
hint,
|
|
700
|
+
tokenRefresh: {
|
|
701
|
+
attempted: true,
|
|
702
|
+
success: false,
|
|
703
|
+
errorMessage: message
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
const refreshedExp = getTokenExpiration(refreshedAccess);
|
|
709
|
+
if (!refreshedExp || refreshedExp <= new Date) {
|
|
710
|
+
return {
|
|
711
|
+
kind: "fail",
|
|
712
|
+
status: {
|
|
713
|
+
loginStatus: "Refresh Failed",
|
|
714
|
+
hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
|
|
715
|
+
tokenRefresh: {
|
|
716
|
+
attempted: true,
|
|
717
|
+
success: false,
|
|
718
|
+
errorMessage: "refreshed token has no valid expiration claim"
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
await saveEnvFile({
|
|
725
|
+
envPath: absolutePath,
|
|
726
|
+
data: {
|
|
727
|
+
UIPATH_ACCESS_TOKEN: refreshedAccess,
|
|
728
|
+
UIPATH_REFRESH_TOKEN: refreshedRefresh
|
|
729
|
+
},
|
|
730
|
+
merge: true
|
|
731
|
+
});
|
|
732
|
+
return {
|
|
733
|
+
kind: "ok",
|
|
734
|
+
accessToken: refreshedAccess,
|
|
735
|
+
refreshToken: refreshedRefresh,
|
|
736
|
+
expiration: refreshedExp,
|
|
737
|
+
tokenRefresh: { attempted: true, success: true }
|
|
738
|
+
};
|
|
739
|
+
} catch (error) {
|
|
740
|
+
const msg = errorMessage(error);
|
|
741
|
+
return {
|
|
742
|
+
kind: "ok",
|
|
743
|
+
accessToken: refreshedAccess,
|
|
744
|
+
refreshToken: refreshedRefresh,
|
|
745
|
+
expiration: refreshedExp,
|
|
746
|
+
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.`,
|
|
747
|
+
tokenRefresh: {
|
|
748
|
+
attempted: true,
|
|
749
|
+
success: true,
|
|
750
|
+
errorMessage: `persistence failed: ${msg}`
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
621
756
|
const {
|
|
622
757
|
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
623
758
|
loadEnvFile = loadEnvFileAsync,
|
|
624
759
|
saveEnvFile = saveEnvFileAsync,
|
|
625
|
-
getFs =
|
|
760
|
+
getFs = getFileSystem3,
|
|
626
761
|
refreshToken: refreshTokenFn = refreshAccessToken,
|
|
627
762
|
resolveConfig = resolveConfigAsync,
|
|
628
763
|
robotFallback = tryRobotClientFallback
|
|
629
764
|
} = deps;
|
|
765
|
+
if (isRobotAuthEnforced()) {
|
|
766
|
+
if (isEnvAuthEnabled()) {
|
|
767
|
+
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
|
|
768
|
+
}
|
|
769
|
+
const robotCreds = await robotFallback({ force: true });
|
|
770
|
+
if (!robotCreds) {
|
|
771
|
+
return {
|
|
772
|
+
loginStatus: "Not logged in",
|
|
773
|
+
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.`
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
const expiration2 = getTokenExpiration(robotCreds.accessToken);
|
|
777
|
+
return {
|
|
778
|
+
loginStatus: "Logged in",
|
|
779
|
+
accessToken: robotCreds.accessToken,
|
|
780
|
+
baseUrl: robotCreds.baseUrl,
|
|
781
|
+
organizationName: robotCreds.organizationName,
|
|
782
|
+
organizationId: robotCreds.organizationId,
|
|
783
|
+
tenantName: robotCreds.tenantName,
|
|
784
|
+
tenantId: robotCreds.tenantId,
|
|
785
|
+
expiration: expiration2,
|
|
786
|
+
source: "robot" /* Robot */
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
if (isEnvAuthEnabled()) {
|
|
790
|
+
return readAuthFromEnv();
|
|
791
|
+
}
|
|
792
|
+
const { envFilePath = DEFAULT_ENV_FILENAME, ensureTokenValidityMinutes } = options;
|
|
630
793
|
const { absolutePath } = await resolveEnvFilePath(envFilePath);
|
|
631
794
|
if (absolutePath === undefined) {
|
|
632
795
|
const robotCreds = await robotFallback();
|
|
@@ -663,73 +826,103 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
663
826
|
let refreshToken = credentials.UIPATH_REFRESH_TOKEN;
|
|
664
827
|
let expiration = getTokenExpiration(accessToken);
|
|
665
828
|
let persistenceWarning;
|
|
829
|
+
let lockReleaseFailed = false;
|
|
666
830
|
let tokenRefresh;
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
831
|
+
const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
|
|
832
|
+
const tryGlobalCredsHint = async () => {
|
|
833
|
+
const fs = getFs();
|
|
834
|
+
const globalPath = fs.path.join(fs.env.homedir(), envFilePath);
|
|
835
|
+
if (absolutePath === globalPath)
|
|
836
|
+
return;
|
|
837
|
+
if (!await fs.exists(globalPath))
|
|
838
|
+
return;
|
|
671
839
|
try {
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
refreshedAccess = refreshed.accessToken;
|
|
682
|
-
refreshedRefresh = refreshed.refreshToken;
|
|
683
|
-
} catch (error) {
|
|
684
|
-
const isOAuthFailure = isTokenRefreshOAuthFailure(error);
|
|
685
|
-
const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
|
|
686
|
-
const errorMessage = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
|
|
687
|
-
return {
|
|
688
|
-
loginStatus: "Refresh Failed",
|
|
689
|
-
hint,
|
|
690
|
-
tokenRefresh: {
|
|
691
|
-
attempted: true,
|
|
692
|
-
success: false,
|
|
693
|
-
errorMessage
|
|
694
|
-
}
|
|
695
|
-
};
|
|
840
|
+
const globalCreds = await loadEnvFile({ envPath: globalPath });
|
|
841
|
+
if (!globalCreds.UIPATH_ACCESS_TOKEN)
|
|
842
|
+
return;
|
|
843
|
+
const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
|
|
844
|
+
if (globalExp && globalExp <= new Date)
|
|
845
|
+
return;
|
|
846
|
+
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.`;
|
|
847
|
+
} catch {
|
|
848
|
+
return;
|
|
696
849
|
}
|
|
697
|
-
|
|
698
|
-
|
|
850
|
+
};
|
|
851
|
+
if (expiration && expiration <= outerThreshold && refreshToken) {
|
|
852
|
+
let release;
|
|
853
|
+
try {
|
|
854
|
+
release = await getFs().acquireLock(absolutePath);
|
|
855
|
+
} catch (error) {
|
|
856
|
+
const msg = errorMessage(error);
|
|
857
|
+
const globalHint = await tryGlobalCredsHint();
|
|
858
|
+
if (globalHint) {
|
|
859
|
+
return {
|
|
860
|
+
loginStatus: "Expired",
|
|
861
|
+
accessToken,
|
|
862
|
+
refreshToken,
|
|
863
|
+
baseUrl: credentials.UIPATH_URL,
|
|
864
|
+
organizationName: credentials.UIPATH_ORGANIZATION_NAME,
|
|
865
|
+
organizationId: credentials.UIPATH_ORGANIZATION_ID,
|
|
866
|
+
tenantName: credentials.UIPATH_TENANT_NAME,
|
|
867
|
+
tenantId: credentials.UIPATH_TENANT_ID,
|
|
868
|
+
expiration,
|
|
869
|
+
source: "file" /* File */,
|
|
870
|
+
hint: globalHint,
|
|
871
|
+
tokenRefresh: {
|
|
872
|
+
attempted: false,
|
|
873
|
+
success: false,
|
|
874
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
}
|
|
699
878
|
return {
|
|
700
879
|
loginStatus: "Refresh Failed",
|
|
701
|
-
hint: "
|
|
880
|
+
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.",
|
|
702
881
|
tokenRefresh: {
|
|
703
|
-
attempted:
|
|
882
|
+
attempted: false,
|
|
704
883
|
success: false,
|
|
705
|
-
errorMessage:
|
|
884
|
+
errorMessage: `lock acquisition failed: ${msg}`
|
|
706
885
|
}
|
|
707
886
|
};
|
|
708
887
|
}
|
|
709
|
-
|
|
710
|
-
refreshToken = refreshedRefresh;
|
|
711
|
-
expiration = refreshedExp;
|
|
888
|
+
let lockedFailure;
|
|
712
889
|
try {
|
|
713
|
-
await
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
890
|
+
const outcome = await runRefreshLocked({
|
|
891
|
+
absolutePath,
|
|
892
|
+
refreshToken,
|
|
893
|
+
customAuthority: credentials.UIPATH_URL,
|
|
894
|
+
ensureTokenValidityMinutes,
|
|
895
|
+
loadEnvFile,
|
|
896
|
+
saveEnvFile,
|
|
897
|
+
refreshFn: refreshTokenFn,
|
|
898
|
+
resolveConfig
|
|
720
899
|
});
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
900
|
+
if (outcome.kind === "fail") {
|
|
901
|
+
lockedFailure = outcome.status;
|
|
902
|
+
} else {
|
|
903
|
+
accessToken = outcome.accessToken;
|
|
904
|
+
refreshToken = outcome.refreshToken;
|
|
905
|
+
expiration = outcome.expiration;
|
|
906
|
+
tokenRefresh = outcome.tokenRefresh;
|
|
907
|
+
if (outcome.persistenceWarning) {
|
|
908
|
+
persistenceWarning = outcome.persistenceWarning;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
} finally {
|
|
912
|
+
try {
|
|
913
|
+
await release();
|
|
914
|
+
} catch {
|
|
915
|
+
lockReleaseFailed = true;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (lockedFailure) {
|
|
919
|
+
const globalHint = await tryGlobalCredsHint();
|
|
920
|
+
const base = globalHint ? {
|
|
921
|
+
...lockedFailure,
|
|
922
|
+
loginStatus: "Expired",
|
|
923
|
+
hint: globalHint
|
|
924
|
+
} : lockedFailure;
|
|
925
|
+
return lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base;
|
|
733
926
|
}
|
|
734
927
|
}
|
|
735
928
|
const result = {
|
|
@@ -744,23 +937,13 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
744
937
|
expiration,
|
|
745
938
|
source: "file" /* File */,
|
|
746
939
|
...persistenceWarning ? { hint: persistenceWarning, persistenceFailed: true } : {},
|
|
940
|
+
...lockReleaseFailed ? { lockReleaseFailed: true } : {},
|
|
747
941
|
...tokenRefresh ? { tokenRefresh } : {}
|
|
748
942
|
};
|
|
749
943
|
if (result.loginStatus === "Expired") {
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
try {
|
|
754
|
-
const globalCreds = await loadEnvFile({
|
|
755
|
-
envPath: globalPath
|
|
756
|
-
});
|
|
757
|
-
if (globalCreds.UIPATH_ACCESS_TOKEN) {
|
|
758
|
-
const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
|
|
759
|
-
if (!globalExp || globalExp > new Date) {
|
|
760
|
-
result.hint = `Local credentials file at ${absolutePath} has expired credentials. Valid credentials exist in ${globalPath}. Remove the local file or run 'uip login' to re-authenticate.`;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
} catch {}
|
|
944
|
+
const globalHint = await tryGlobalCredsHint();
|
|
945
|
+
if (globalHint) {
|
|
946
|
+
result.hint = globalHint;
|
|
764
947
|
}
|
|
765
948
|
}
|
|
766
949
|
return result;
|
|
@@ -793,7 +976,7 @@ var getAuthContext = async (options = {}) => {
|
|
|
793
976
|
throw new Error("Tenant ID not available. Ensure UIPATH_TENANT_ID is set.");
|
|
794
977
|
}
|
|
795
978
|
if (options.requireTenantName && tenantName === undefined) {
|
|
796
|
-
throw new Error("Tenant not provided and UIPATH_TENANT_NAME not set.
|
|
979
|
+
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.");
|
|
797
980
|
}
|
|
798
981
|
return {
|
|
799
982
|
baseUrl: status.baseUrl,
|
|
@@ -820,9 +1003,11 @@ var clientCredentialsLogin = async ({
|
|
|
820
1003
|
const params = {
|
|
821
1004
|
grant_type: "client_credentials",
|
|
822
1005
|
client_id: config.clientId,
|
|
823
|
-
client_secret: config.clientSecret ?? ""
|
|
824
|
-
scope: config.scopes.join(" ")
|
|
1006
|
+
client_secret: config.clientSecret ?? ""
|
|
825
1007
|
};
|
|
1008
|
+
if (config.scopes.length > 0) {
|
|
1009
|
+
params.scope = config.scopes.join(" ");
|
|
1010
|
+
}
|
|
826
1011
|
const tokenResponse = await fetch(config.tokenEndpoint, {
|
|
827
1012
|
method: "POST",
|
|
828
1013
|
headers: {
|
|
@@ -860,16 +1045,30 @@ Troubleshooting:` + `
|
|
|
860
1045
|
};
|
|
861
1046
|
};
|
|
862
1047
|
// src/logout.ts
|
|
863
|
-
import { getFileSystem as
|
|
1048
|
+
import { getFileSystem as getFileSystem4 } from "@uipath/filesystem";
|
|
864
1049
|
async function logoutWithDeps(options, deps = {}) {
|
|
865
1050
|
const {
|
|
866
1051
|
resolveEnvFilePath = resolveEnvFilePathAsync,
|
|
867
|
-
getFileSystem: getFs =
|
|
1052
|
+
getFileSystem: getFs = getFileSystem4
|
|
868
1053
|
} = deps;
|
|
869
1054
|
const fs = getFs();
|
|
870
1055
|
const { absolutePath } = await resolveEnvFilePath(options.file);
|
|
871
1056
|
if (absolutePath && await fs.exists(absolutePath)) {
|
|
872
|
-
|
|
1057
|
+
let release;
|
|
1058
|
+
try {
|
|
1059
|
+
if (typeof fs.acquireLock === "function") {
|
|
1060
|
+
release = await fs.acquireLock(absolutePath);
|
|
1061
|
+
}
|
|
1062
|
+
} catch {
|
|
1063
|
+
release = undefined;
|
|
1064
|
+
}
|
|
1065
|
+
try {
|
|
1066
|
+
await fs.rm(absolutePath);
|
|
1067
|
+
} finally {
|
|
1068
|
+
if (release) {
|
|
1069
|
+
await release().catch(() => {});
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
873
1072
|
return {
|
|
874
1073
|
success: true,
|
|
875
1074
|
message: `Logged out successfully. Removed ${absolutePath}`,
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export * from "./authContext";
|
|
2
|
+
export * from "./clientCredentials";
|
|
3
|
+
export { type AuthFileConfig, InvalidBaseUrlError, setAuthFileConfig, } from "./config";
|
|
4
|
+
export { ENFORCE_ROBOT_AUTH_VAR, ENV_AUTH_ENABLE_VAR, ENV_AUTH_VARS, EnvAuthConfigError, isEnvAuthEnabled, isRobotAuthEnforced, readAuthFromEnv, } from "./envAuth";
|
|
5
|
+
export * from "./interactive";
|
|
6
|
+
export * from "./loginStatus";
|
|
7
|
+
export * from "./logout";
|
|
8
|
+
export { type SelectFromList, selectTenantWithDeps } from "./selectTenant";
|
|
9
|
+
export { AUTH_TIMEOUT_ERROR_CODE } from "./server";
|
|
10
|
+
export { fetchTenantsAndOrganizations, type Tenant, type TenantsAndOrganizations, } from "./tenantSelection";
|
|
11
|
+
export * from "./tokenRefresh";
|
|
12
|
+
export type { BaseCredentials } from "./types";
|
|
13
|
+
export { DEFAULT_AUTH_FILENAME, DEFAULT_ENV_FILENAME, type EnvFileErrorCode, type EnvFileLocation, type EnvFileSource, type EnvFileUnusable, type EnvFileUnusableReason, loadEnvFileAsync, resolveEnvFileLocationAsync, resolveEnvFilePathAsync, saveEnvFileAsync, } from "./utils/envFile";
|
|
14
|
+
export { parseJWT } from "./utils/jwt";
|
|
15
|
+
export { isBrowser, isNode } from "./utils/platform";
|
|
16
|
+
interface AuthenticateOptions {
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
clientId?: string;
|
|
19
|
+
clientSecret?: string;
|
|
20
|
+
redirectUri?: URL;
|
|
21
|
+
scope?: string[];
|
|
22
|
+
organization?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const authenticate: ({ baseUrl, clientId, clientSecret, redirectUri, scope, organization, }: AuthenticateOptions) => Promise<{
|
|
25
|
+
accessToken: string;
|
|
26
|
+
refreshToken: string;
|
|
27
|
+
}>;
|