@uipath/auth 1.204.0-preview.255 → 1.205.0-preview.256
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/README.md +1 -1
- package/dist/config.d.ts +0 -2
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +130 -63
- package/dist/index.d.ts +1 -1
- package/dist/index.js +131 -55
- package/dist/loginStatus.d.ts +17 -0
- package/dist/sessionIdentity.d.ts +13 -0
- package/dist/tenantSelection.d.ts +5 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install @uipath/auth
|
|
|
16
16
|
|
|
17
17
|
### Log in and save credentials
|
|
18
18
|
|
|
19
|
-
`interactiveLogin` runs the whole login and writes the result to a credentials file. The flow is chosen by the options: the browser (authorization code + PKCE) by default, client credentials when a client secret is set, federated login when `clientAssertion` is set.
|
|
19
|
+
`interactiveLogin` runs the whole login and writes the result to a credentials file. The flow is chosen by the options: the browser (authorization code + PKCE) by default, client credentials when a client secret is set, federated login when `clientAssertion` is set.
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
22
|
import { interactiveLogin } from "@uipath/auth";
|
package/dist/config.d.ts
CHANGED
package/dist/index.browser.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export * from "./clientCredentials";
|
|
|
4
4
|
export { type AuthFileConfig, InvalidBaseUrlError, setAuthFileConfig, } from "./config";
|
|
5
5
|
export * from "./loginStatus";
|
|
6
6
|
export * from "./logout";
|
|
7
|
-
export { type IdentityType, parseAuthFlow, readTokenScopes, resolveSessionIdentity, type SessionIdentity, } from "./sessionIdentity";
|
|
7
|
+
export { type ApplicationSubtype, type IdentityType, parseAuthFlow, readTokenScopes, resolveSessionIdentity, type SessionIdentity, } from "./sessionIdentity";
|
|
8
8
|
export { fetchTenantsAndOrganizations, type Tenant, type TenantsAndOrganizations, } from "./tenantSelection";
|
|
9
9
|
export * from "./tokenRefresh";
|
|
10
10
|
export { AUTH_FLOW_ENV_VAR, type AuthFlow, type BaseCredentials, } from "./types";
|
package/dist/index.browser.js
CHANGED
|
@@ -2769,7 +2769,7 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2769
2769
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2770
2770
|
var eventTargetShim = require_event_target_shim();
|
|
2771
2771
|
|
|
2772
|
-
class
|
|
2772
|
+
class AbortSignal2 extends eventTargetShim.EventTarget {
|
|
2773
2773
|
constructor() {
|
|
2774
2774
|
super();
|
|
2775
2775
|
throw TypeError("AbortSignal cannot be constructed directly");
|
|
@@ -2781,9 +2781,9 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2781
2781
|
return aborted;
|
|
2782
2782
|
}
|
|
2783
2783
|
}
|
|
2784
|
-
eventTargetShim.defineEventAttribute(
|
|
2784
|
+
eventTargetShim.defineEventAttribute(AbortSignal2.prototype, "abort");
|
|
2785
2785
|
function createAbortSignal() {
|
|
2786
|
-
let signal = Object.create(
|
|
2786
|
+
let signal = Object.create(AbortSignal2.prototype);
|
|
2787
2787
|
return eventTargetShim.EventTarget.call(signal), abortedFlags.set(signal, false), signal;
|
|
2788
2788
|
}
|
|
2789
2789
|
function abortSignal(signal) {
|
|
@@ -2792,9 +2792,9 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2792
2792
|
abortedFlags.set(signal, true), signal.dispatchEvent({ type: "abort" });
|
|
2793
2793
|
}
|
|
2794
2794
|
var abortedFlags = new WeakMap;
|
|
2795
|
-
Object.defineProperties(
|
|
2795
|
+
Object.defineProperties(AbortSignal2.prototype, { aborted: { enumerable: true } });
|
|
2796
2796
|
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol")
|
|
2797
|
-
Object.defineProperty(
|
|
2797
|
+
Object.defineProperty(AbortSignal2.prototype, Symbol.toStringTag, { configurable: true, value: "AbortSignal" });
|
|
2798
2798
|
|
|
2799
2799
|
class AbortController {
|
|
2800
2800
|
constructor() {
|
|
@@ -2818,14 +2818,14 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2818
2818
|
if (typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol")
|
|
2819
2819
|
Object.defineProperty(AbortController.prototype, Symbol.toStringTag, { configurable: true, value: "AbortController" });
|
|
2820
2820
|
exports2.AbortController = AbortController;
|
|
2821
|
-
exports2.AbortSignal =
|
|
2821
|
+
exports2.AbortSignal = AbortSignal2;
|
|
2822
2822
|
exports2.default = AbortController;
|
|
2823
2823
|
module2.exports = AbortController;
|
|
2824
2824
|
module2.exports.AbortController = module2.exports.default = AbortController;
|
|
2825
|
-
module2.exports.AbortSignal =
|
|
2825
|
+
module2.exports.AbortSignal = AbortSignal2;
|
|
2826
2826
|
});
|
|
2827
2827
|
var require_util = __commonJS2((exports2, module2) => {
|
|
2828
|
-
var bufferModule = (init_buffer(), __toCommonJS(exports_buffer)), { format: format2, inspect: inspect2 } = require_inspect(), { codes: { ERR_INVALID_ARG_TYPE: ERR_INVALID_ARG_TYPE3 } } = require_errors(), { kResistStopPropagation, AggregateError: AggregateError2, SymbolDispose } = require_primordials(),
|
|
2828
|
+
var bufferModule = (init_buffer(), __toCommonJS(exports_buffer)), { format: format2, inspect: inspect2 } = require_inspect(), { codes: { ERR_INVALID_ARG_TYPE: ERR_INVALID_ARG_TYPE3 } } = require_errors(), { kResistStopPropagation, AggregateError: AggregateError2, SymbolDispose } = require_primordials(), AbortSignal2 = globalThis.AbortSignal || require_abort_controller().AbortSignal, AbortController = globalThis.AbortController || require_abort_controller().AbortController, AsyncFunction = Object.getPrototypeOf(async function() {}).constructor, Blob2 = globalThis.Blob || bufferModule.Blob, isBlob = typeof Blob2 < "u" ? function(b) {
|
|
2829
2829
|
return b instanceof Blob2;
|
|
2830
2830
|
} : function(b) {
|
|
2831
2831
|
return false;
|
|
@@ -2879,7 +2879,7 @@ var require_stream = __commonJS(function(exports, module) {
|
|
|
2879
2879
|
var _removeEventListener;
|
|
2880
2880
|
(_removeEventListener = removeEventListener) === null || _removeEventListener === undefined || _removeEventListener();
|
|
2881
2881
|
} };
|
|
2882
|
-
}, AbortSignalAny:
|
|
2882
|
+
}, AbortSignalAny: AbortSignal2.any || function(signals) {
|
|
2883
2883
|
if (signals.length === 1)
|
|
2884
2884
|
return signals[0];
|
|
2885
2885
|
let ac = new AbortController, abort = () => ac.abort();
|
|
@@ -18413,22 +18413,12 @@ var resolveConfigAsync = async ({
|
|
|
18413
18413
|
baseUrl = DEFAULT_BASE_URL;
|
|
18414
18414
|
}
|
|
18415
18415
|
baseUrl = normalizeAndValidateBaseUrl(baseUrl);
|
|
18416
|
-
|
|
18417
|
-
|
|
18418
|
-
clientId = fileAuth.clientId;
|
|
18419
|
-
}
|
|
18420
|
-
if (!clientId) {
|
|
18421
|
-
clientId = DEFAULT_CLIENT_ID;
|
|
18422
|
-
}
|
|
18423
|
-
let clientSecret = customClientSecret;
|
|
18424
|
-
if (!clientSecret && fileAuth.clientSecret) {
|
|
18425
|
-
clientSecret = fileAuth.clientSecret;
|
|
18426
|
-
}
|
|
18427
|
-
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && (Boolean(clientSecret) || Boolean(customClientAssertion));
|
|
18416
|
+
const clientId = customClientId || DEFAULT_CLIENT_ID;
|
|
18417
|
+
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && (Boolean(customClientSecret) || Boolean(customClientAssertion));
|
|
18428
18418
|
const scopes = resolveScopes(isExternalAppAuth, customScopes, fileAuth.scopes);
|
|
18429
18419
|
return {
|
|
18430
18420
|
clientId,
|
|
18431
|
-
clientSecret,
|
|
18421
|
+
clientSecret: customClientSecret,
|
|
18432
18422
|
scopes,
|
|
18433
18423
|
baseUrl,
|
|
18434
18424
|
authorizationEndpoint: `${baseUrl}/identity_/connect/authorize`,
|
|
@@ -18537,6 +18527,16 @@ var resolveIdentityType = (claims, authFlow, email) => {
|
|
|
18537
18527
|
}
|
|
18538
18528
|
return;
|
|
18539
18529
|
};
|
|
18530
|
+
var resolveApplicationSubtype = (claims, authFlow) => {
|
|
18531
|
+
switch (asString(claims?.sub_type)) {
|
|
18532
|
+
case "service.external":
|
|
18533
|
+
return "External";
|
|
18534
|
+
case "service.internal":
|
|
18535
|
+
return "Internal";
|
|
18536
|
+
default:
|
|
18537
|
+
return authFlow === "client_credentials" || authFlow === "federated_credentials" ? "External" : undefined;
|
|
18538
|
+
}
|
|
18539
|
+
};
|
|
18540
18540
|
var looksLikeEmail = (value) => value?.includes("@") ?? false;
|
|
18541
18541
|
var pickEmail = (claims) => {
|
|
18542
18542
|
for (const candidate of [claims?.email, claims?.preferred_username]) {
|
|
@@ -18578,6 +18578,12 @@ var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
|
18578
18578
|
const clientId = asString(claims?.client_id);
|
|
18579
18579
|
if (clientId)
|
|
18580
18580
|
identity.clientId = clientId;
|
|
18581
|
+
const subType = asString(claims?.sub_type);
|
|
18582
|
+
if (subType)
|
|
18583
|
+
identity.subType = subType;
|
|
18584
|
+
const applicationSubtype = resolveApplicationSubtype(claims, authFlow);
|
|
18585
|
+
if (applicationSubtype)
|
|
18586
|
+
identity.applicationSubtype = applicationSubtype;
|
|
18581
18587
|
return identity;
|
|
18582
18588
|
};
|
|
18583
18589
|
var readTokenScopes = (accessToken) => accessToken ? pickScopes(decodeClaims(accessToken)) : undefined;
|
|
@@ -18872,6 +18878,40 @@ var tryRobotClientFallback = async (options = {}) => {
|
|
|
18872
18878
|
}
|
|
18873
18879
|
};
|
|
18874
18880
|
|
|
18881
|
+
// src/tenantSelection.ts
|
|
18882
|
+
var LOGIN_STATUS_HINT = "Run 'uip login status' to see the organization the CLI is signed in to.";
|
|
18883
|
+
var organizationHint = (organizationId) => `The request used '${organizationId}' as the organization. ${LOGIN_STATUS_HINT}`;
|
|
18884
|
+
var IDENTIFIER_STATUSES = new Set([400, 403, 404]);
|
|
18885
|
+
var describeNonJsonResponse = (organizationId, response, parseError) => {
|
|
18886
|
+
if (response.redirected) {
|
|
18887
|
+
return `Could not find organization '${organizationId}': the request was ` + `redirected to a sign-in page instead of the organization's tenant ` + `list. ${LOGIN_STATUS_HINT}`;
|
|
18888
|
+
}
|
|
18889
|
+
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
18890
|
+
return `The organization service returned a '${contentType}' response instead ` + `of JSON: ${parseError.message}. ${organizationHint(organizationId)}`;
|
|
18891
|
+
};
|
|
18892
|
+
var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId, signal) => {
|
|
18893
|
+
const url = `${baseUrl}/${organizationId}/portal_/api/filtering/leftnav/tenantsAndOrganizationInfo`;
|
|
18894
|
+
const response = await fetch(url, {
|
|
18895
|
+
headers: {
|
|
18896
|
+
Authorization: `Bearer ${accessToken}`
|
|
18897
|
+
},
|
|
18898
|
+
...signal ? { signal } : {}
|
|
18899
|
+
});
|
|
18900
|
+
if (!response.ok) {
|
|
18901
|
+
if (response.status === 401) {
|
|
18902
|
+
throw new Error(`Unauthorized: the access token was rejected, it was not ` + `issued for organization '${organizationId}', or ` + `'${organizationId}' was not accepted as an organization ` + `URL segment in this environment. ` + LOGIN_STATUS_HINT);
|
|
18903
|
+
}
|
|
18904
|
+
const errorText = await response.text();
|
|
18905
|
+
const message = `Failed to get tenants and organizations: ${response.status} ${errorText}`;
|
|
18906
|
+
throw new Error(IDENTIFIER_STATUSES.has(response.status) ? `${message}. ${organizationHint(organizationId)}` : message);
|
|
18907
|
+
}
|
|
18908
|
+
const [parseError, data] = await catchError(response.json());
|
|
18909
|
+
if (parseError) {
|
|
18910
|
+
throw new Error(describeNonJsonResponse(organizationId, response, parseError));
|
|
18911
|
+
}
|
|
18912
|
+
return data;
|
|
18913
|
+
};
|
|
18914
|
+
|
|
18875
18915
|
// src/tokenRefresh.ts
|
|
18876
18916
|
var TOKEN_REFRESH_REAUTHENTICATE_MESSAGE = "Token refresh failed. Run 'uip login' to re-authenticate.";
|
|
18877
18917
|
|
|
@@ -19092,6 +19132,8 @@ var CredentialSource;
|
|
|
19092
19132
|
CredentialSource2["Robot"] = "robot";
|
|
19093
19133
|
CredentialSource2["EnvironmentVariables"] = "env-vars";
|
|
19094
19134
|
})(CredentialSource ||= {});
|
|
19135
|
+
var defaultRobotTenantIdCache = new Map;
|
|
19136
|
+
var ROBOT_TENANT_LOOKUP_TIMEOUT_MS = 5000;
|
|
19095
19137
|
var getLoginStatusAsync = async (options = {}) => {
|
|
19096
19138
|
return getLoginStatusWithDeps(options);
|
|
19097
19139
|
};
|
|
@@ -19104,12 +19146,20 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
19104
19146
|
refreshToken: refreshTokenFn = refreshAccessToken,
|
|
19105
19147
|
resolveConfig = resolveConfigAsync,
|
|
19106
19148
|
robotFallback = tryRobotClientFallback,
|
|
19149
|
+
fetchTenantsAndOrgs = fetchTenantsAndOrganizations,
|
|
19150
|
+
robotTenantIdCache = defaultRobotTenantIdCache,
|
|
19151
|
+
robotTenantLookupTimeoutMs = ROBOT_TENANT_LOOKUP_TIMEOUT_MS,
|
|
19107
19152
|
loadBreaker = loadRefreshBreaker,
|
|
19108
19153
|
saveBreaker = saveRefreshBreaker,
|
|
19109
19154
|
clearBreaker = clearRefreshBreaker
|
|
19110
19155
|
} = deps;
|
|
19156
|
+
const robotDeps = {
|
|
19157
|
+
fetchTenantsAndOrgs,
|
|
19158
|
+
cache: robotTenantIdCache,
|
|
19159
|
+
lookupTimeoutMs: robotTenantLookupTimeoutMs
|
|
19160
|
+
};
|
|
19111
19161
|
if (isRobotAuthEnforced()) {
|
|
19112
|
-
return resolveRobotEnforcedStatus(robotFallback);
|
|
19162
|
+
return resolveRobotEnforcedStatus(robotFallback, robotDeps);
|
|
19113
19163
|
}
|
|
19114
19164
|
if (isEnvAuthEnabled()) {
|
|
19115
19165
|
return readAuthFromEnv();
|
|
@@ -19127,7 +19177,7 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
19127
19177
|
hint: `No credentials found for profile "${activeProfile}". Run 'uip login --profile ${activeProfile}' to authenticate this profile.`
|
|
19128
19178
|
};
|
|
19129
19179
|
}
|
|
19130
|
-
return resolveBorrowedRobotStatus(robotFallback);
|
|
19180
|
+
return resolveBorrowedRobotStatus(robotFallback, robotDeps);
|
|
19131
19181
|
}
|
|
19132
19182
|
const loaded = await loadFileCredentials(loadEnvFile, absolutePath);
|
|
19133
19183
|
if ("status" in loaded) {
|
|
@@ -19169,7 +19219,7 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
19169
19219
|
}
|
|
19170
19220
|
return buildFileStatus(tokens, credentials, globalHint);
|
|
19171
19221
|
};
|
|
19172
|
-
async function resolveRobotEnforcedStatus(robotFallback) {
|
|
19222
|
+
async function resolveRobotEnforcedStatus(robotFallback, deps) {
|
|
19173
19223
|
if (isEnvAuthEnabled()) {
|
|
19174
19224
|
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
|
|
19175
19225
|
}
|
|
@@ -19180,11 +19230,11 @@ async function resolveRobotEnforcedStatus(robotFallback) {
|
|
|
19180
19230
|
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.`
|
|
19181
19231
|
};
|
|
19182
19232
|
}
|
|
19183
|
-
return buildRobotStatus(robotCreds);
|
|
19233
|
+
return buildRobotStatus(robotCreds, deps);
|
|
19184
19234
|
}
|
|
19185
|
-
async function resolveBorrowedRobotStatus(robotFallback) {
|
|
19235
|
+
async function resolveBorrowedRobotStatus(robotFallback, deps) {
|
|
19186
19236
|
const robotCreds = await robotFallback();
|
|
19187
|
-
return robotCreds ? buildRobotStatus(robotCreds) : { loginStatus: "Not logged in" };
|
|
19237
|
+
return robotCreds ? buildRobotStatus(robotCreds, deps) : { loginStatus: "Not logged in" };
|
|
19188
19238
|
}
|
|
19189
19239
|
async function loadFileCredentials(loadEnvFile, absolutePath) {
|
|
19190
19240
|
let credentials;
|
|
@@ -19319,9 +19369,10 @@ function identityFields(accessToken, credentials) {
|
|
|
19319
19369
|
...scopes ? { scopes } : {}
|
|
19320
19370
|
};
|
|
19321
19371
|
}
|
|
19322
|
-
function buildRobotStatus(robotCreds) {
|
|
19372
|
+
async function buildRobotStatus(robotCreds, deps) {
|
|
19323
19373
|
const identity = resolveSessionIdentity(robotCreds.accessToken);
|
|
19324
19374
|
const scopes = readTokenScopes(robotCreds.accessToken);
|
|
19375
|
+
const tenant = robotCreds.tenantId ? { tenantId: robotCreds.tenantId } : await resolveRobotTenantId(robotCreds, deps);
|
|
19325
19376
|
return {
|
|
19326
19377
|
loginStatus: "Logged in",
|
|
19327
19378
|
accessToken: robotCreds.accessToken,
|
|
@@ -19329,14 +19380,62 @@ function buildRobotStatus(robotCreds) {
|
|
|
19329
19380
|
organizationName: robotCreds.organizationName,
|
|
19330
19381
|
organizationId: robotCreds.organizationId,
|
|
19331
19382
|
tenantName: robotCreds.tenantName,
|
|
19332
|
-
tenantId:
|
|
19383
|
+
tenantId: tenant.tenantId,
|
|
19333
19384
|
issuer: robotCreds.issuer,
|
|
19334
19385
|
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
19335
19386
|
source: "robot" /* Robot */,
|
|
19387
|
+
...tenant.hint ? { hint: tenant.hint } : {},
|
|
19336
19388
|
...identity ? { identity } : {},
|
|
19337
19389
|
...scopes ? { scopes } : {}
|
|
19338
19390
|
};
|
|
19339
19391
|
}
|
|
19392
|
+
async function resolveRobotTenantId(robotCreds, deps) {
|
|
19393
|
+
const { baseUrl, accessToken, tenantName } = robotCreds;
|
|
19394
|
+
const organization = robotCreds.organizationName ?? robotCreds.organizationId;
|
|
19395
|
+
if (!tenantName || !organization) {
|
|
19396
|
+
return {};
|
|
19397
|
+
}
|
|
19398
|
+
const key = `${baseUrl}|${organization}|${tenantName}|${accessToken}`;
|
|
19399
|
+
let pending = deps.cache.get(key);
|
|
19400
|
+
if (!pending) {
|
|
19401
|
+
pending = catchError(lookupRobotTenantId({ baseUrl, accessToken, organization, tenantName }, deps)).then(([error, resolution]) => {
|
|
19402
|
+
const settled = error ? { hint: tenantLookupFailureHint(tenantName, error.message) } : resolution;
|
|
19403
|
+
if (!settled.tenantId) {
|
|
19404
|
+
deps.cache.delete(key);
|
|
19405
|
+
}
|
|
19406
|
+
return settled;
|
|
19407
|
+
});
|
|
19408
|
+
deps.cache.set(key, pending);
|
|
19409
|
+
}
|
|
19410
|
+
return pending;
|
|
19411
|
+
}
|
|
19412
|
+
async function lookupRobotTenantId(target, deps) {
|
|
19413
|
+
const { baseUrl, accessToken, organization, tenantName } = target;
|
|
19414
|
+
const [error, data] = await catchError(deps.fetchTenantsAndOrgs(baseUrl, accessToken, organization, AbortSignal.timeout(deps.lookupTimeoutMs)));
|
|
19415
|
+
if (error) {
|
|
19416
|
+
const reason = error.name === "TimeoutError" ? `the tenant list did not answer within ${deps.lookupTimeoutMs} ms.` : error.message;
|
|
19417
|
+
return { hint: tenantLookupFailureHint(tenantName, reason) };
|
|
19418
|
+
}
|
|
19419
|
+
const rows = data?.tenants;
|
|
19420
|
+
const tenants = Array.isArray(rows) ? rows.filter((tenant) => typeof tenant?.id === "string" && typeof tenant?.name === "string") : [];
|
|
19421
|
+
const wantedName = decodePathSegment(tenantName);
|
|
19422
|
+
const wanted = wantedName.toLowerCase();
|
|
19423
|
+
const match = tenants.find((tenant) => tenant.name === wantedName) ?? tenants.find((tenant) => tenant.name.toLowerCase() === wanted);
|
|
19424
|
+
if (!match) {
|
|
19425
|
+
const names = tenants.map((tenant) => tenant.name).join(", ");
|
|
19426
|
+
return {
|
|
19427
|
+
hint: `The Robot session targets tenant '${tenantName}', but ` + `organization '${organization}' lists no tenant of that name` + `${names ? ` (available: ${names})` : ""}. Commands that need ` + `the tenant id will fail until it can be resolved.`
|
|
19428
|
+
};
|
|
19429
|
+
}
|
|
19430
|
+
return { tenantId: match.id };
|
|
19431
|
+
}
|
|
19432
|
+
function tenantLookupFailureHint(tenantName, reason) {
|
|
19433
|
+
return `Could not resolve the id of tenant '${tenantName}' from the ` + `Robot session: ${reason} Commands that need the tenant id will ` + `fail until it can be resolved.`;
|
|
19434
|
+
}
|
|
19435
|
+
function decodePathSegment(segment) {
|
|
19436
|
+
const [error, decoded] = catchError(() => decodeURIComponent(segment));
|
|
19437
|
+
return error ? segment : decoded;
|
|
19438
|
+
}
|
|
19340
19439
|
var isFileNotFoundError = (error) => {
|
|
19341
19440
|
if (!(error instanceof Object))
|
|
19342
19441
|
return false;
|
|
@@ -19780,38 +19879,6 @@ async function logoutWithDeps(options, deps = {}) {
|
|
|
19780
19879
|
async function logout(options) {
|
|
19781
19880
|
return logoutWithDeps(options);
|
|
19782
19881
|
}
|
|
19783
|
-
// src/tenantSelection.ts
|
|
19784
|
-
var LOGIN_STATUS_HINT = "Run 'uip login status' to see the organization the CLI is signed in to.";
|
|
19785
|
-
var organizationHint = (organizationId) => `The request used '${organizationId}' as the organization. ${LOGIN_STATUS_HINT}`;
|
|
19786
|
-
var IDENTIFIER_STATUSES = new Set([400, 403, 404]);
|
|
19787
|
-
var describeNonJsonResponse = (organizationId, response, parseError) => {
|
|
19788
|
-
if (response.redirected) {
|
|
19789
|
-
return `Could not find organization '${organizationId}': the request was ` + `redirected to a sign-in page instead of the organization's tenant ` + `list. ${LOGIN_STATUS_HINT}`;
|
|
19790
|
-
}
|
|
19791
|
-
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
19792
|
-
return `The organization service returned a '${contentType}' response instead ` + `of JSON: ${parseError.message}. ${organizationHint(organizationId)}`;
|
|
19793
|
-
};
|
|
19794
|
-
var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId) => {
|
|
19795
|
-
const url = `${baseUrl}/${organizationId}/portal_/api/filtering/leftnav/tenantsAndOrganizationInfo`;
|
|
19796
|
-
const response = await fetch(url, {
|
|
19797
|
-
headers: {
|
|
19798
|
-
Authorization: `Bearer ${accessToken}`
|
|
19799
|
-
}
|
|
19800
|
-
});
|
|
19801
|
-
if (!response.ok) {
|
|
19802
|
-
if (response.status === 401) {
|
|
19803
|
-
throw new Error(`Unauthorized: the access token was rejected, it was not ` + `issued for organization '${organizationId}', or ` + `'${organizationId}' was not accepted as an organization ` + `URL segment in this environment. ` + LOGIN_STATUS_HINT);
|
|
19804
|
-
}
|
|
19805
|
-
const errorText = await response.text();
|
|
19806
|
-
const message = `Failed to get tenants and organizations: ${response.status} ${errorText}`;
|
|
19807
|
-
throw new Error(IDENTIFIER_STATUSES.has(response.status) ? `${message}. ${organizationHint(organizationId)}` : message);
|
|
19808
|
-
}
|
|
19809
|
-
const [parseError, data] = await catchError(response.json());
|
|
19810
|
-
if (parseError) {
|
|
19811
|
-
throw new Error(describeNonJsonResponse(organizationId, response, parseError));
|
|
19812
|
-
}
|
|
19813
|
-
return data;
|
|
19814
|
-
};
|
|
19815
19882
|
export {
|
|
19816
19883
|
AUTH_FLOW_ENV_VAR,
|
|
19817
19884
|
AuthProfileValidationError,
|
|
@@ -19857,4 +19924,4 @@ export {
|
|
|
19857
19924
|
setAuthFileConfig
|
|
19858
19925
|
};
|
|
19859
19926
|
|
|
19860
|
-
//# debugId=
|
|
19927
|
+
//# debugId=0E042F445778F04E64756E2164756E21
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export * from "./logout";
|
|
|
12
12
|
export { clearRefreshBreaker, loadRefreshBreaker, type RefreshBreakerState, refreshTokenFingerprint, saveRefreshBreaker, } from "./refreshCircuitBreaker";
|
|
13
13
|
export { type RobotClientModuleLoader, registerRobotClientLoader, } from "./robotClientFallback";
|
|
14
14
|
export { INVALID_TENANT_CODE, InvalidTenantError, isTenantSelectionError, type SelectFromList, selectTenantWithDeps, TENANT_SELECTION_REQUIRED_CODE, TenantSelectionError, TenantSelectionRequiredError, } from "./selectTenant";
|
|
15
|
-
export { type IdentityType, parseAuthFlow, readTokenScopes, resolveSessionIdentity, type SessionIdentity, } from "./sessionIdentity";
|
|
15
|
+
export { type ApplicationSubtype, type IdentityType, parseAuthFlow, readTokenScopes, resolveSessionIdentity, type SessionIdentity, } from "./sessionIdentity";
|
|
16
16
|
export { fetchTenantsAndOrganizations, type Tenant, type TenantsAndOrganizations, } from "./tenantSelection";
|
|
17
17
|
export * from "./tokenRefresh";
|
|
18
18
|
export { AUTH_FLOW_ENV_VAR, type AuthFlow, type BaseCredentials, } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -892,6 +892,16 @@ class NodeFileSystem {
|
|
|
892
892
|
async rename(oldPath, newPath) {
|
|
893
893
|
await fs6.rename(oldPath, newPath);
|
|
894
894
|
}
|
|
895
|
+
async isSymbolicLink(filePath) {
|
|
896
|
+
try {
|
|
897
|
+
return (await fs6.lstat(filePath)).isSymbolicLink();
|
|
898
|
+
} catch (error) {
|
|
899
|
+
if (this.isEnoent(error) || this.hasErrnoCode(error, "ENOTDIR")) {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
throw error;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
895
905
|
async realpath(filePath) {
|
|
896
906
|
try {
|
|
897
907
|
return await fs6.realpath(filePath);
|
|
@@ -1571,22 +1581,12 @@ var resolveConfigAsync = async ({
|
|
|
1571
1581
|
baseUrl = DEFAULT_BASE_URL;
|
|
1572
1582
|
}
|
|
1573
1583
|
baseUrl = normalizeAndValidateBaseUrl(baseUrl);
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
clientId = fileAuth.clientId;
|
|
1577
|
-
}
|
|
1578
|
-
if (!clientId) {
|
|
1579
|
-
clientId = DEFAULT_CLIENT_ID;
|
|
1580
|
-
}
|
|
1581
|
-
let clientSecret = customClientSecret;
|
|
1582
|
-
if (!clientSecret && fileAuth.clientSecret) {
|
|
1583
|
-
clientSecret = fileAuth.clientSecret;
|
|
1584
|
-
}
|
|
1585
|
-
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && (Boolean(clientSecret) || Boolean(customClientAssertion));
|
|
1584
|
+
const clientId = customClientId || DEFAULT_CLIENT_ID;
|
|
1585
|
+
const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && (Boolean(customClientSecret) || Boolean(customClientAssertion));
|
|
1586
1586
|
const scopes = resolveScopes(isExternalAppAuth, customScopes, fileAuth.scopes);
|
|
1587
1587
|
return {
|
|
1588
1588
|
clientId,
|
|
1589
|
-
clientSecret,
|
|
1589
|
+
clientSecret: customClientSecret,
|
|
1590
1590
|
scopes,
|
|
1591
1591
|
baseUrl,
|
|
1592
1592
|
authorizationEndpoint: `${baseUrl}/identity_/connect/authorize`,
|
|
@@ -2200,6 +2200,16 @@ var resolveIdentityType = (claims, authFlow, email) => {
|
|
|
2200
2200
|
}
|
|
2201
2201
|
return;
|
|
2202
2202
|
};
|
|
2203
|
+
var resolveApplicationSubtype = (claims, authFlow) => {
|
|
2204
|
+
switch (asString(claims?.sub_type)) {
|
|
2205
|
+
case "service.external":
|
|
2206
|
+
return "External";
|
|
2207
|
+
case "service.internal":
|
|
2208
|
+
return "Internal";
|
|
2209
|
+
default:
|
|
2210
|
+
return authFlow === "client_credentials" || authFlow === "federated_credentials" ? "External" : undefined;
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2203
2213
|
var looksLikeEmail = (value) => value?.includes("@") ?? false;
|
|
2204
2214
|
var pickEmail = (claims) => {
|
|
2205
2215
|
for (const candidate of [claims?.email, claims?.preferred_username]) {
|
|
@@ -2241,6 +2251,12 @@ var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
|
2241
2251
|
const clientId = asString(claims?.client_id);
|
|
2242
2252
|
if (clientId)
|
|
2243
2253
|
identity.clientId = clientId;
|
|
2254
|
+
const subType = asString(claims?.sub_type);
|
|
2255
|
+
if (subType)
|
|
2256
|
+
identity.subType = subType;
|
|
2257
|
+
const applicationSubtype = resolveApplicationSubtype(claims, authFlow);
|
|
2258
|
+
if (applicationSubtype)
|
|
2259
|
+
identity.applicationSubtype = applicationSubtype;
|
|
2244
2260
|
return identity;
|
|
2245
2261
|
};
|
|
2246
2262
|
var readTokenScopes = (accessToken) => accessToken ? pickScopes(decodeClaims(accessToken)) : undefined;
|
|
@@ -2538,6 +2554,40 @@ var tryRobotClientFallback = async (options = {}) => {
|
|
|
2538
2554
|
}
|
|
2539
2555
|
};
|
|
2540
2556
|
|
|
2557
|
+
// src/tenantSelection.ts
|
|
2558
|
+
var LOGIN_STATUS_HINT = "Run 'uip login status' to see the organization the CLI is signed in to.";
|
|
2559
|
+
var organizationHint = (organizationId) => `The request used '${organizationId}' as the organization. ${LOGIN_STATUS_HINT}`;
|
|
2560
|
+
var IDENTIFIER_STATUSES = new Set([400, 403, 404]);
|
|
2561
|
+
var describeNonJsonResponse = (organizationId, response, parseError) => {
|
|
2562
|
+
if (response.redirected) {
|
|
2563
|
+
return `Could not find organization '${organizationId}': the request was ` + `redirected to a sign-in page instead of the organization's tenant ` + `list. ${LOGIN_STATUS_HINT}`;
|
|
2564
|
+
}
|
|
2565
|
+
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
2566
|
+
return `The organization service returned a '${contentType}' response instead ` + `of JSON: ${parseError.message}. ${organizationHint(organizationId)}`;
|
|
2567
|
+
};
|
|
2568
|
+
var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId, signal) => {
|
|
2569
|
+
const url = `${baseUrl}/${organizationId}/portal_/api/filtering/leftnav/tenantsAndOrganizationInfo`;
|
|
2570
|
+
const response = await fetch(url, {
|
|
2571
|
+
headers: {
|
|
2572
|
+
Authorization: `Bearer ${accessToken}`
|
|
2573
|
+
},
|
|
2574
|
+
...signal ? { signal } : {}
|
|
2575
|
+
});
|
|
2576
|
+
if (!response.ok) {
|
|
2577
|
+
if (response.status === 401) {
|
|
2578
|
+
throw new Error(`Unauthorized: the access token was rejected, it was not ` + `issued for organization '${organizationId}', or ` + `'${organizationId}' was not accepted as an organization ` + `URL segment in this environment. ` + LOGIN_STATUS_HINT);
|
|
2579
|
+
}
|
|
2580
|
+
const errorText = await response.text();
|
|
2581
|
+
const message = `Failed to get tenants and organizations: ${response.status} ${errorText}`;
|
|
2582
|
+
throw new Error(IDENTIFIER_STATUSES.has(response.status) ? `${message}. ${organizationHint(organizationId)}` : message);
|
|
2583
|
+
}
|
|
2584
|
+
const [parseError, data] = await catchError(response.json());
|
|
2585
|
+
if (parseError) {
|
|
2586
|
+
throw new Error(describeNonJsonResponse(organizationId, response, parseError));
|
|
2587
|
+
}
|
|
2588
|
+
return data;
|
|
2589
|
+
};
|
|
2590
|
+
|
|
2541
2591
|
// src/tokenRefresh.ts
|
|
2542
2592
|
var TOKEN_REFRESH_REAUTHENTICATE_MESSAGE = "Token refresh failed. Run 'uip login' to re-authenticate.";
|
|
2543
2593
|
|
|
@@ -2759,6 +2809,8 @@ var CredentialSource;
|
|
|
2759
2809
|
CredentialSource2["Robot"] = "robot";
|
|
2760
2810
|
CredentialSource2["EnvironmentVariables"] = "env-vars";
|
|
2761
2811
|
})(CredentialSource ||= {});
|
|
2812
|
+
var defaultRobotTenantIdCache = new Map;
|
|
2813
|
+
var ROBOT_TENANT_LOOKUP_TIMEOUT_MS = 5000;
|
|
2762
2814
|
var getLoginStatusAsync = async (options = {}) => {
|
|
2763
2815
|
return getLoginStatusWithDeps(options);
|
|
2764
2816
|
};
|
|
@@ -2771,12 +2823,20 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
2771
2823
|
refreshToken: refreshTokenFn = refreshAccessToken,
|
|
2772
2824
|
resolveConfig = resolveConfigAsync,
|
|
2773
2825
|
robotFallback = tryRobotClientFallback,
|
|
2826
|
+
fetchTenantsAndOrgs = fetchTenantsAndOrganizations,
|
|
2827
|
+
robotTenantIdCache = defaultRobotTenantIdCache,
|
|
2828
|
+
robotTenantLookupTimeoutMs = ROBOT_TENANT_LOOKUP_TIMEOUT_MS,
|
|
2774
2829
|
loadBreaker = loadRefreshBreaker,
|
|
2775
2830
|
saveBreaker = saveRefreshBreaker,
|
|
2776
2831
|
clearBreaker = clearRefreshBreaker
|
|
2777
2832
|
} = deps;
|
|
2833
|
+
const robotDeps = {
|
|
2834
|
+
fetchTenantsAndOrgs,
|
|
2835
|
+
cache: robotTenantIdCache,
|
|
2836
|
+
lookupTimeoutMs: robotTenantLookupTimeoutMs
|
|
2837
|
+
};
|
|
2778
2838
|
if (isRobotAuthEnforced()) {
|
|
2779
|
-
return resolveRobotEnforcedStatus(robotFallback);
|
|
2839
|
+
return resolveRobotEnforcedStatus(robotFallback, robotDeps);
|
|
2780
2840
|
}
|
|
2781
2841
|
if (isEnvAuthEnabled()) {
|
|
2782
2842
|
return readAuthFromEnv();
|
|
@@ -2794,7 +2854,7 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
2794
2854
|
hint: `No credentials found for profile "${activeProfile}". Run 'uip login --profile ${activeProfile}' to authenticate this profile.`
|
|
2795
2855
|
};
|
|
2796
2856
|
}
|
|
2797
|
-
return resolveBorrowedRobotStatus(robotFallback);
|
|
2857
|
+
return resolveBorrowedRobotStatus(robotFallback, robotDeps);
|
|
2798
2858
|
}
|
|
2799
2859
|
const loaded = await loadFileCredentials(loadEnvFile, absolutePath);
|
|
2800
2860
|
if ("status" in loaded) {
|
|
@@ -2836,7 +2896,7 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
|
|
|
2836
2896
|
}
|
|
2837
2897
|
return buildFileStatus(tokens, credentials, globalHint);
|
|
2838
2898
|
};
|
|
2839
|
-
async function resolveRobotEnforcedStatus(robotFallback) {
|
|
2899
|
+
async function resolveRobotEnforcedStatus(robotFallback, deps) {
|
|
2840
2900
|
if (isEnvAuthEnabled()) {
|
|
2841
2901
|
throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
|
|
2842
2902
|
}
|
|
@@ -2847,11 +2907,11 @@ async function resolveRobotEnforcedStatus(robotFallback) {
|
|
|
2847
2907
|
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.`
|
|
2848
2908
|
};
|
|
2849
2909
|
}
|
|
2850
|
-
return buildRobotStatus(robotCreds);
|
|
2910
|
+
return buildRobotStatus(robotCreds, deps);
|
|
2851
2911
|
}
|
|
2852
|
-
async function resolveBorrowedRobotStatus(robotFallback) {
|
|
2912
|
+
async function resolveBorrowedRobotStatus(robotFallback, deps) {
|
|
2853
2913
|
const robotCreds = await robotFallback();
|
|
2854
|
-
return robotCreds ? buildRobotStatus(robotCreds) : { loginStatus: "Not logged in" };
|
|
2914
|
+
return robotCreds ? buildRobotStatus(robotCreds, deps) : { loginStatus: "Not logged in" };
|
|
2855
2915
|
}
|
|
2856
2916
|
async function loadFileCredentials(loadEnvFile, absolutePath) {
|
|
2857
2917
|
let credentials;
|
|
@@ -2986,9 +3046,10 @@ function identityFields(accessToken, credentials) {
|
|
|
2986
3046
|
...scopes ? { scopes } : {}
|
|
2987
3047
|
};
|
|
2988
3048
|
}
|
|
2989
|
-
function buildRobotStatus(robotCreds) {
|
|
3049
|
+
async function buildRobotStatus(robotCreds, deps) {
|
|
2990
3050
|
const identity = resolveSessionIdentity(robotCreds.accessToken);
|
|
2991
3051
|
const scopes = readTokenScopes(robotCreds.accessToken);
|
|
3052
|
+
const tenant = robotCreds.tenantId ? { tenantId: robotCreds.tenantId } : await resolveRobotTenantId(robotCreds, deps);
|
|
2992
3053
|
return {
|
|
2993
3054
|
loginStatus: "Logged in",
|
|
2994
3055
|
accessToken: robotCreds.accessToken,
|
|
@@ -2996,14 +3057,62 @@ function buildRobotStatus(robotCreds) {
|
|
|
2996
3057
|
organizationName: robotCreds.organizationName,
|
|
2997
3058
|
organizationId: robotCreds.organizationId,
|
|
2998
3059
|
tenantName: robotCreds.tenantName,
|
|
2999
|
-
tenantId:
|
|
3060
|
+
tenantId: tenant.tenantId,
|
|
3000
3061
|
issuer: robotCreds.issuer,
|
|
3001
3062
|
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
3002
3063
|
source: "robot" /* Robot */,
|
|
3064
|
+
...tenant.hint ? { hint: tenant.hint } : {},
|
|
3003
3065
|
...identity ? { identity } : {},
|
|
3004
3066
|
...scopes ? { scopes } : {}
|
|
3005
3067
|
};
|
|
3006
3068
|
}
|
|
3069
|
+
async function resolveRobotTenantId(robotCreds, deps) {
|
|
3070
|
+
const { baseUrl, accessToken, tenantName } = robotCreds;
|
|
3071
|
+
const organization = robotCreds.organizationName ?? robotCreds.organizationId;
|
|
3072
|
+
if (!tenantName || !organization) {
|
|
3073
|
+
return {};
|
|
3074
|
+
}
|
|
3075
|
+
const key = `${baseUrl}|${organization}|${tenantName}|${accessToken}`;
|
|
3076
|
+
let pending = deps.cache.get(key);
|
|
3077
|
+
if (!pending) {
|
|
3078
|
+
pending = catchError(lookupRobotTenantId({ baseUrl, accessToken, organization, tenantName }, deps)).then(([error, resolution]) => {
|
|
3079
|
+
const settled = error ? { hint: tenantLookupFailureHint(tenantName, error.message) } : resolution;
|
|
3080
|
+
if (!settled.tenantId) {
|
|
3081
|
+
deps.cache.delete(key);
|
|
3082
|
+
}
|
|
3083
|
+
return settled;
|
|
3084
|
+
});
|
|
3085
|
+
deps.cache.set(key, pending);
|
|
3086
|
+
}
|
|
3087
|
+
return pending;
|
|
3088
|
+
}
|
|
3089
|
+
async function lookupRobotTenantId(target, deps) {
|
|
3090
|
+
const { baseUrl, accessToken, organization, tenantName } = target;
|
|
3091
|
+
const [error, data] = await catchError(deps.fetchTenantsAndOrgs(baseUrl, accessToken, organization, AbortSignal.timeout(deps.lookupTimeoutMs)));
|
|
3092
|
+
if (error) {
|
|
3093
|
+
const reason = error.name === "TimeoutError" ? `the tenant list did not answer within ${deps.lookupTimeoutMs} ms.` : error.message;
|
|
3094
|
+
return { hint: tenantLookupFailureHint(tenantName, reason) };
|
|
3095
|
+
}
|
|
3096
|
+
const rows = data?.tenants;
|
|
3097
|
+
const tenants = Array.isArray(rows) ? rows.filter((tenant) => typeof tenant?.id === "string" && typeof tenant?.name === "string") : [];
|
|
3098
|
+
const wantedName = decodePathSegment(tenantName);
|
|
3099
|
+
const wanted = wantedName.toLowerCase();
|
|
3100
|
+
const match = tenants.find((tenant) => tenant.name === wantedName) ?? tenants.find((tenant) => tenant.name.toLowerCase() === wanted);
|
|
3101
|
+
if (!match) {
|
|
3102
|
+
const names = tenants.map((tenant) => tenant.name).join(", ");
|
|
3103
|
+
return {
|
|
3104
|
+
hint: `The Robot session targets tenant '${tenantName}', but ` + `organization '${organization}' lists no tenant of that name` + `${names ? ` (available: ${names})` : ""}. Commands that need ` + `the tenant id will fail until it can be resolved.`
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
return { tenantId: match.id };
|
|
3108
|
+
}
|
|
3109
|
+
function tenantLookupFailureHint(tenantName, reason) {
|
|
3110
|
+
return `Could not resolve the id of tenant '${tenantName}' from the ` + `Robot session: ${reason} Commands that need the tenant id will ` + `fail until it can be resolved.`;
|
|
3111
|
+
}
|
|
3112
|
+
function decodePathSegment(segment) {
|
|
3113
|
+
const [error, decoded] = catchError(() => decodeURIComponent(segment));
|
|
3114
|
+
return error ? segment : decoded;
|
|
3115
|
+
}
|
|
3007
3116
|
var isFileNotFoundError = (error) => {
|
|
3008
3117
|
if (!(error instanceof Object))
|
|
3009
3118
|
return false;
|
|
@@ -3534,39 +3643,6 @@ var federatedCredentialsLogin = async ({
|
|
|
3534
3643
|
init_src();
|
|
3535
3644
|
init_constants();
|
|
3536
3645
|
|
|
3537
|
-
// src/tenantSelection.ts
|
|
3538
|
-
var LOGIN_STATUS_HINT = "Run 'uip login status' to see the organization the CLI is signed in to.";
|
|
3539
|
-
var organizationHint = (organizationId) => `The request used '${organizationId}' as the organization. ${LOGIN_STATUS_HINT}`;
|
|
3540
|
-
var IDENTIFIER_STATUSES = new Set([400, 403, 404]);
|
|
3541
|
-
var describeNonJsonResponse = (organizationId, response, parseError) => {
|
|
3542
|
-
if (response.redirected) {
|
|
3543
|
-
return `Could not find organization '${organizationId}': the request was ` + `redirected to a sign-in page instead of the organization's tenant ` + `list. ${LOGIN_STATUS_HINT}`;
|
|
3544
|
-
}
|
|
3545
|
-
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
3546
|
-
return `The organization service returned a '${contentType}' response instead ` + `of JSON: ${parseError.message}. ${organizationHint(organizationId)}`;
|
|
3547
|
-
};
|
|
3548
|
-
var fetchTenantsAndOrganizations = async (baseUrl, accessToken, organizationId) => {
|
|
3549
|
-
const url = `${baseUrl}/${organizationId}/portal_/api/filtering/leftnav/tenantsAndOrganizationInfo`;
|
|
3550
|
-
const response = await fetch(url, {
|
|
3551
|
-
headers: {
|
|
3552
|
-
Authorization: `Bearer ${accessToken}`
|
|
3553
|
-
}
|
|
3554
|
-
});
|
|
3555
|
-
if (!response.ok) {
|
|
3556
|
-
if (response.status === 401) {
|
|
3557
|
-
throw new Error(`Unauthorized: the access token was rejected, it was not ` + `issued for organization '${organizationId}', or ` + `'${organizationId}' was not accepted as an organization ` + `URL segment in this environment. ` + LOGIN_STATUS_HINT);
|
|
3558
|
-
}
|
|
3559
|
-
const errorText = await response.text();
|
|
3560
|
-
const message = `Failed to get tenants and organizations: ${response.status} ${errorText}`;
|
|
3561
|
-
throw new Error(IDENTIFIER_STATUSES.has(response.status) ? `${message}. ${organizationHint(organizationId)}` : message);
|
|
3562
|
-
}
|
|
3563
|
-
const [parseError, data] = await catchError(response.json());
|
|
3564
|
-
if (parseError) {
|
|
3565
|
-
throw new Error(describeNonJsonResponse(organizationId, response, parseError));
|
|
3566
|
-
}
|
|
3567
|
-
return data;
|
|
3568
|
-
};
|
|
3569
|
-
|
|
3570
3646
|
// src/selectTenant.ts
|
|
3571
3647
|
var TENANT_SELECTION_REQUIRED_CODE = "TENANT_SELECTION_REQUIRED";
|
|
3572
3648
|
var INVALID_TENANT_CODE = "INVALID_TENANT";
|
|
@@ -4021,4 +4097,4 @@ export {
|
|
|
4021
4097
|
setAuthFileConfig
|
|
4022
4098
|
};
|
|
4023
4099
|
|
|
4024
|
-
//# debugId=
|
|
4100
|
+
//# debugId=3C4FEB497AB4F8C264756E2164756E21
|
package/dist/loginStatus.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { resolveConfigAsync } from "./config";
|
|
|
3
3
|
import { clearRefreshBreaker, loadRefreshBreaker, saveRefreshBreaker } from "./refreshCircuitBreaker";
|
|
4
4
|
import { tryRobotClientFallback } from "./robotClientFallback";
|
|
5
5
|
import { type SessionIdentity } from "./sessionIdentity";
|
|
6
|
+
import { fetchTenantsAndOrganizations } from "./tenantSelection";
|
|
6
7
|
import { refreshAccessToken } from "./tokenRefresh";
|
|
7
8
|
import { loadEnvFileAsync, resolveEnvFilePathAsync, saveEnvFileAsync } from "./utils/envFile";
|
|
8
9
|
/**
|
|
@@ -92,10 +93,25 @@ export interface LoginStatusDependencies {
|
|
|
92
93
|
refreshToken?: typeof refreshAccessToken;
|
|
93
94
|
resolveConfig?: typeof resolveConfigAsync;
|
|
94
95
|
robotFallback?: typeof tryRobotClientFallback;
|
|
96
|
+
/**
|
|
97
|
+
* Portal lookup that turns the tenant named in the Robot's Orchestrator
|
|
98
|
+
* URL into the tenant id — see {@link resolveRobotTenantId}.
|
|
99
|
+
*/
|
|
100
|
+
fetchTenantsAndOrgs?: typeof fetchTenantsAndOrganizations;
|
|
101
|
+
/** Memo for {@link resolveRobotTenantId}; tests pass a fresh one. */
|
|
102
|
+
robotTenantIdCache?: RobotTenantIdCache;
|
|
103
|
+
/** Timeout for that lookup; tests pass a short one. */
|
|
104
|
+
robotTenantLookupTimeoutMs?: number;
|
|
95
105
|
loadBreaker?: typeof loadRefreshBreaker;
|
|
96
106
|
saveBreaker?: typeof saveRefreshBreaker;
|
|
97
107
|
clearBreaker?: typeof clearRefreshBreaker;
|
|
98
108
|
}
|
|
109
|
+
/** What the tenant lookup settled to; `hint` explains a missing `tenantId`. */
|
|
110
|
+
interface RobotTenantResolution {
|
|
111
|
+
tenantId?: string;
|
|
112
|
+
hint?: string;
|
|
113
|
+
}
|
|
114
|
+
type RobotTenantIdCache = Map<string, Promise<RobotTenantResolution>>;
|
|
99
115
|
/**
|
|
100
116
|
* Get the current login status by reading credentials from the credentials file
|
|
101
117
|
* @param envFilePath - Path to the credentials file (defaults to ".uipath/.auth")
|
|
@@ -107,3 +123,4 @@ export declare const getLoginStatusAsync: (options?: GetLoginStatusOptions) => P
|
|
|
107
123
|
* Get the current login status by reading credentials from the credentials file (with DI for testing)
|
|
108
124
|
*/
|
|
109
125
|
export declare const getLoginStatusWithDeps: (options?: GetLoginStatusOptions, deps?: LoginStatusDependencies) => Promise<LoginStatus>;
|
|
126
|
+
export {};
|
|
@@ -4,6 +4,8 @@ import type { AuthFlow } from "./types";
|
|
|
4
4
|
* Deliberately coarse — `authFlow` carries the precision.
|
|
5
5
|
*/
|
|
6
6
|
export type IdentityType = "User" | "Application";
|
|
7
|
+
/** The token subtype when Identity explicitly identifies an application. */
|
|
8
|
+
export type ApplicationSubtype = "External" | "Internal";
|
|
7
9
|
/**
|
|
8
10
|
* Who the current session acts as, as reported by the access token.
|
|
9
11
|
*
|
|
@@ -24,6 +26,17 @@ export interface SessionIdentity {
|
|
|
24
26
|
* CLI's own — and surfacing that would read as if an app were acting.
|
|
25
27
|
*/
|
|
26
28
|
clientId?: string;
|
|
29
|
+
/**
|
|
30
|
+
* The token's own `sub_type` claim, when it carries one. {@link type} is
|
|
31
|
+
* deliberately coarse — `service.internal` (S2S) and `service.external`
|
|
32
|
+
* (an external application) are both "Application" — and a caller that
|
|
33
|
+
* has to tell those two apart has nothing else to read. Unverified like
|
|
34
|
+
* every other field here, so it decides display and convenience, never
|
|
35
|
+
* access.
|
|
36
|
+
*/
|
|
37
|
+
subType?: string;
|
|
38
|
+
/** Application identities only, when the token states its service subtype. */
|
|
39
|
+
applicationSubtype?: ApplicationSubtype;
|
|
27
40
|
}
|
|
28
41
|
/** Narrow an on-disk `UIPATH_AUTH_FLOW` value to a known flow. */
|
|
29
42
|
export declare const parseAuthFlow: (value: string | undefined) => AuthFlow | undefined;
|
|
@@ -24,6 +24,10 @@ export interface TenantsAndOrganizations {
|
|
|
24
24
|
* later on: the portal has been seen answering `401` to the id (and to a
|
|
25
25
|
* case-variant of the name) on an aged-but-valid session that the exact name
|
|
26
26
|
* still resolves. So the id is a last-resort fallback, not an equal option.
|
|
27
|
+
*
|
|
28
|
+
* @param signal - Optional abort signal. Interactive login passes none; the
|
|
29
|
+
* Robot status path passes a timeout so a hung portal cannot hang every
|
|
30
|
+
* command.
|
|
27
31
|
*/
|
|
28
|
-
export declare const fetchTenantsAndOrganizations: (baseUrl: string, accessToken: string, organizationId: string) => Promise<TenantsAndOrganizations>;
|
|
32
|
+
export declare const fetchTenantsAndOrganizations: (baseUrl: string, accessToken: string, organizationId: string, signal?: AbortSignal) => Promise<TenantsAndOrganizations>;
|
|
29
33
|
export {};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@uipath/auth",
|
|
3
3
|
"author": "UiPath",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.205.0-preview.256",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/UiPath/cli.git",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"mihaigirleanu",
|
|
39
39
|
"vlad-uipath"
|
|
40
40
|
],
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "63740c6cbb7ff282fa9118cc16a148eb030b245f"
|
|
42
42
|
}
|