@uipath/auth 1.203.0-preview.205 → 1.204.0-preview.206
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 +135 -15
- package/dist/authContext.d.ts +9 -0
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +21 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +21 -5
- package/dist/loginStatus.d.ts +7 -0
- package/dist/sessionIdentity.d.ts +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,52 +2,172 @@
|
|
|
2
2
|
|
|
3
3
|
Authentication module for the UiPath CLI. Handles OIDC login, token management, and credential storage.
|
|
4
4
|
|
|
5
|
+
This README covers the library API. For how the `uip` CLI uses it (login methods, named profiles, environment-variable auth, credential files), see the [Authentication Guide](https://github.com/UiPath/cli/blob/main/docs/guides/authentication.md) and the [Authentication Reference](https://github.com/UiPath/cli/blob/main/docs/reference/authentication.md).
|
|
6
|
+
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
9
|
+
The package is published to the GitHub Packages registry (`https://npm.pkg.github.com/@uipath`). With that registry configured for the `@uipath` scope:
|
|
10
|
+
|
|
7
11
|
```bash
|
|
8
12
|
npm install @uipath/auth
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
## Usage
|
|
12
16
|
|
|
13
|
-
###
|
|
17
|
+
### Log in and save credentials
|
|
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. The client secret can come from the `clientSecret` option or from a `clientSecret` pushed with `setAuthFileConfig` (the CLI pushes `auth.clientSecret` from `.uipath/config.json`), so with that set, a call with no `clientSecret` uses client credentials, not the browser.
|
|
14
20
|
|
|
15
21
|
```ts
|
|
16
22
|
import { interactiveLogin } from "@uipath/auth";
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
function requireEnv(name: string): string {
|
|
25
|
+
const value = process.env[name];
|
|
26
|
+
if (!value) {
|
|
27
|
+
throw new Error(`${name} is not set`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Browser login, saved to the default .uipath/.auth
|
|
33
|
+
await interactiveLogin({ tenant: "DefaultTenant" });
|
|
34
|
+
|
|
35
|
+
// Client credentials. Check the variables first: with no clientSecret,
|
|
36
|
+
// interactiveLogin falls back to the browser flow instead of failing.
|
|
37
|
+
await interactiveLogin({
|
|
38
|
+
clientId: requireEnv("UIPATH_CLIENT_ID"),
|
|
39
|
+
clientSecret: requireEnv("UIPATH_CLIENT_SECRET"),
|
|
40
|
+
tenant: "DefaultTenant",
|
|
41
|
+
});
|
|
19
42
|
```
|
|
20
43
|
|
|
21
|
-
|
|
44
|
+
`envFilePath` picks the file to write. An absolute path is used as is. A relative path (the default is `.uipath/.auth`) is looked for from the current directory upward, and a file found there is reused only if it already holds a `UIPATH_ACCESS_TOKEN`. A file without one, or one that cannot be read, is skipped. When nothing is reused, the credentials go under the home directory: `~/.uipath/.auth` for the default. The read functions below still pick a readable file that has no token, so an empty `.uipath/.auth` in the current directory or a parent hides the saved login from them.
|
|
45
|
+
|
|
46
|
+
### Get tokens without saving them
|
|
22
47
|
|
|
23
48
|
```ts
|
|
24
|
-
import { clientCredentialsLogin } from "@uipath/auth";
|
|
49
|
+
import { clientCredentialsLogin, federatedCredentialsLogin } from "@uipath/auth";
|
|
25
50
|
|
|
51
|
+
// requireEnv as in the example above: both calls take plain strings
|
|
26
52
|
const tokens = await clientCredentialsLogin({
|
|
27
|
-
clientId:
|
|
28
|
-
clientSecret:
|
|
53
|
+
clientId: requireEnv("UIPATH_CLIENT_ID"),
|
|
54
|
+
clientSecret: requireEnv("UIPATH_CLIENT_SECRET"),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const federated = await federatedCredentialsLogin({
|
|
58
|
+
clientId: requireEnv("UIPATH_CLIENT_ID"),
|
|
59
|
+
clientAssertion: requireEnv("UIPATH_OIDC_TOKEN"),
|
|
29
60
|
});
|
|
30
61
|
```
|
|
31
62
|
|
|
32
|
-
|
|
63
|
+
Both return the token response and write nothing to disk.
|
|
64
|
+
|
|
65
|
+
### Read the current session
|
|
33
66
|
|
|
34
67
|
```ts
|
|
35
|
-
import { getLoginStatusAsync } from "@uipath/auth";
|
|
68
|
+
import { getAuthContext, getLoginStatusAsync } from "@uipath/auth";
|
|
69
|
+
|
|
70
|
+
// Status report: Logged in / Not logged in / Expired / Refresh Failed
|
|
71
|
+
const status = await getLoginStatusAsync();
|
|
72
|
+
|
|
73
|
+
// What a command needs to call an API; throws when there is no usable session
|
|
74
|
+
const { baseUrl, accessToken, tenantName } = await getAuthContext({
|
|
75
|
+
requireTenantName: true,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Both pick credentials the same way as the CLI, using the first rule that applies:
|
|
80
|
+
|
|
81
|
+
1. `UIPATH_CLI_ENFORCE_ROBOT_AUTH=true`: the local Robot only. No file is read.
|
|
82
|
+
2. `UIPATH_CLI_ENABLE_ENV_AUTH=true`: the environment variables only.
|
|
83
|
+
3. An active named profile: that profile's file only. If it is missing there is no session (`Not logged in`). There is no `.uipath/.auth` search and no Robot fallback.
|
|
84
|
+
4. Otherwise the `.uipath/.auth` search. When it finds no file, a token is borrowed from the local Robot if one answers. This is skipped when `CI`, `GITHUB_ACTIONS` or `UIPATH_URL` is set.
|
|
85
|
+
|
|
86
|
+
An expired token is renewed when the file holds a refresh token; pass `ensureTokenValidityMinutes` to renew earlier.
|
|
36
87
|
|
|
37
|
-
|
|
88
|
+
### Named profiles
|
|
89
|
+
|
|
90
|
+
A named profile is a credentials file at `~/.uipath/profiles/<name>/.auth`. The CLI host calls `setActiveAuthProfile` once per invocation from `--profile`; after that, `getLoginStatusAsync`, `getAuthContext`, `getAuthEnv`, and `logout` read and write that profile's file.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import {
|
|
94
|
+
describeAuthProfilesAsync,
|
|
95
|
+
getLoginStatusAsync,
|
|
96
|
+
interactiveLogin,
|
|
97
|
+
resolveAuthProfileFilePath,
|
|
98
|
+
runWithAuthProfile,
|
|
99
|
+
} from "@uipath/auth";
|
|
100
|
+
|
|
101
|
+
// interactiveLogin does not follow the active profile: pass the path
|
|
102
|
+
await interactiveLogin({ envFilePath: resolveAuthProfileFilePath("acme") });
|
|
103
|
+
|
|
104
|
+
// Run code as a profile, isolated from other concurrent work in the process
|
|
105
|
+
const status = await runWithAuthProfile("acme", () => getLoginStatusAsync());
|
|
106
|
+
|
|
107
|
+
// Every profile with its status (never refreshes, never returns tokens)
|
|
108
|
+
const profiles = await describeAuthProfilesAsync();
|
|
38
109
|
```
|
|
39
110
|
|
|
111
|
+
`runWithAuthProfile` scopes the profile with `AsyncLocalStorage`, so in-process hosts such as the MCP server can serve several profiles at once. Profile names may contain only letters, numbers, `.`, `_`, and `-`; others throw `AuthProfileValidationError`.
|
|
112
|
+
|
|
40
113
|
## API
|
|
41
114
|
|
|
115
|
+
### Login
|
|
116
|
+
|
|
117
|
+
| Export | Description |
|
|
118
|
+
| :--- | :--- |
|
|
119
|
+
| `interactiveLogin(options)` | Full login (browser, client credentials, or federated) that saves credentials to `envFilePath` |
|
|
120
|
+
| `authenticate(options)` | Browser authorization-code + PKCE step only; returns tokens without saving |
|
|
121
|
+
| `clientCredentialsLogin(props)` | Client-credentials token request; returns tokens without saving |
|
|
122
|
+
| `federatedCredentialsLogin(props)` | Client credentials with a JWT assertion (`jwt-bearer`); returns tokens without saving |
|
|
123
|
+
| `logout(options)` | Delete the resolved credentials file (or `options.file`). Does not revoke tokens on the server |
|
|
124
|
+
|
|
125
|
+
### Session
|
|
126
|
+
|
|
127
|
+
| Export | Description |
|
|
128
|
+
| :--- | :--- |
|
|
129
|
+
| `getLoginStatusAsync(options)` | Current status, identity, and tokens; renews an expired token when it can |
|
|
130
|
+
| `getAuthContext(options)` | Base URL, token, and org/tenant for an API call; throws when a required value is missing |
|
|
131
|
+
| `getAuthEnv(options)` | The session as `UIPATH_*` key/value pairs, for handing to a child process |
|
|
132
|
+
| `resolveSessionIdentity()`, `readTokenScopes()` | Who the token belongs to, and the scopes it carries |
|
|
133
|
+
| `refreshAccessToken(params)` | Low-level refresh-token grant |
|
|
134
|
+
|
|
135
|
+
### Environment and Robot auth
|
|
136
|
+
|
|
137
|
+
| Export | Description |
|
|
138
|
+
| :--- | :--- |
|
|
139
|
+
| `isEnvAuthEnabled()`, `readAuthFromEnv()` | `UIPATH_CLI_ENABLE_ENV_AUTH` gate and the session it builds from `UIPATH_CLI_*` variables |
|
|
140
|
+
| `ENV_AUTH_VARS`, `ENV_AUTH_ENABLE_VAR`, `EnvAuthConfigError` | Variable names and the error raised when one is missing |
|
|
141
|
+
| `isRobotAuthEnforced()`, `ENFORCE_ROBOT_AUTH_VAR` | `UIPATH_CLI_ENFORCE_ROBOT_AUTH` gate |
|
|
142
|
+
| `registerRobotClientLoader(loader)` | Host hook that lets the CLI borrow a token from the local Robot |
|
|
143
|
+
|
|
144
|
+
### Named profiles
|
|
145
|
+
|
|
146
|
+
| Export | Description |
|
|
147
|
+
| :--- | :--- |
|
|
148
|
+
| `setActiveAuthProfile(name)`, `getActiveAuthProfile()`, `clearActiveAuthProfile()` | Select the profile for the rest of the invocation |
|
|
149
|
+
| `runWithAuthProfile(name, fn)` | Run `fn` with a profile in an isolated async scope |
|
|
150
|
+
| `resolveAuthProfileFilePath(name)`, `getActiveAuthProfileFilePath()` | Path to a profile's `.auth` file |
|
|
151
|
+
| `listAuthProfileNamesAsync()`, `describeAuthProfilesAsync()` | Profile names; profile names with org, tenant, and status |
|
|
152
|
+
| `authProfileExistsAsync(name)`, `deleteAuthProfileAsync(name)` | Check for, or remove, a profile directory |
|
|
153
|
+
| `normalizeAuthProfileName(name)`, `AuthProfileValidationError` | Name validation (`default` normalizes to `undefined`) |
|
|
154
|
+
|
|
155
|
+
### Tenants
|
|
156
|
+
|
|
157
|
+
| Export | Description |
|
|
158
|
+
| :--- | :--- |
|
|
159
|
+
| `fetchTenantsAndOrganizations()` | Tenants the token can use |
|
|
160
|
+
| `selectTenantWithDeps()` | Tenant choice with a host-supplied prompt (`SelectFromList`) |
|
|
161
|
+
| `TenantSelectionRequiredError`, `InvalidTenantError` | Several tenants and no choice; an unknown tenant name |
|
|
162
|
+
|
|
163
|
+
### Credentials file and config
|
|
164
|
+
|
|
42
165
|
| Export | Description |
|
|
43
166
|
| :--- | :--- |
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `getLoginStatusAsync(options)` | Check current login status, auto-refreshes expired tokens |
|
|
47
|
-
| `refreshAccessToken()` | Refresh an expired access token |
|
|
48
|
-
| `selectTenant()` | Interactive tenant selection |
|
|
167
|
+
| `resolveEnvFilePathAsync()`, `resolveEnvFileLocationAsync()` | Find the `.uipath/.auth` a path resolves to, and where it was found |
|
|
168
|
+
| `loadEnvFileAsync()`, `saveEnvFileAsync()` | Read and write a credentials file (atomic writes) |
|
|
49
169
|
| `setAuthFileConfig(cfg)` | Push the on-disk `auth.*` config slice into the auth singleton (called once by the CLI host at startup) |
|
|
50
|
-
| `
|
|
170
|
+
| `InvalidBaseUrlError` | Raised for a malformed or non-HTTPS base URL (there is no host allowlist) |
|
|
51
171
|
|
|
52
172
|
## License
|
|
53
173
|
|
package/dist/authContext.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type GetLoginStatusOptions, type LoginStatus } from "./loginStatus";
|
|
2
|
+
import type { SessionIdentity } from "./sessionIdentity";
|
|
2
3
|
export interface AuthContext {
|
|
3
4
|
baseUrl: string;
|
|
4
5
|
accessToken: string;
|
|
@@ -6,6 +7,14 @@ export interface AuthContext {
|
|
|
6
7
|
organizationName?: string;
|
|
7
8
|
tenantId?: string;
|
|
8
9
|
tenantName?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Who the session acts as, as the login status resolved it — from the
|
|
12
|
+
* token's claims *and* the auth flow this CLI persisted at login, which a
|
|
13
|
+
* bare token decode does not know. Absent when neither said anything.
|
|
14
|
+
* Read from unverified claims: for display and for predicting server
|
|
15
|
+
* behaviour, never for an authorization decision.
|
|
16
|
+
*/
|
|
17
|
+
identity?: SessionIdentity;
|
|
9
18
|
}
|
|
10
19
|
export interface GetAuthContextOptions {
|
|
11
20
|
tenant?: string;
|
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, resolveSessionIdentity, type SessionIdentity, } from "./sessionIdentity";
|
|
7
|
+
export { 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
|
@@ -18550,6 +18550,11 @@ var pickName = (claims) => {
|
|
|
18550
18550
|
const username = asString(claims?.preferred_username);
|
|
18551
18551
|
return asString(claims?.name) ?? (looksLikeEmail(username) ? undefined : username);
|
|
18552
18552
|
};
|
|
18553
|
+
var pickScopes = (claims) => {
|
|
18554
|
+
const raw = claims?.scope;
|
|
18555
|
+
const scopes = Array.isArray(raw) ? raw.filter((entry) => asString(entry) !== undefined) : typeof raw === "string" ? raw.split(/\s+/).filter(Boolean) : [];
|
|
18556
|
+
return scopes.length > 0 ? scopes : undefined;
|
|
18557
|
+
};
|
|
18553
18558
|
var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
18554
18559
|
const claims = accessToken ? decodeClaims(accessToken) : undefined;
|
|
18555
18560
|
const email = pickEmail(claims);
|
|
@@ -18575,6 +18580,7 @@ var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
|
18575
18580
|
identity.clientId = clientId;
|
|
18576
18581
|
return identity;
|
|
18577
18582
|
};
|
|
18583
|
+
var readTokenScopes = (accessToken) => accessToken ? pickScopes(decodeClaims(accessToken)) : undefined;
|
|
18578
18584
|
|
|
18579
18585
|
// src/envAuth.ts
|
|
18580
18586
|
var ENV_AUTH_ENABLE_VAR = "UIPATH_CLI_ENABLE_ENV_AUTH";
|
|
@@ -18647,6 +18653,7 @@ var readAuthFromEnv = () => {
|
|
|
18647
18653
|
const expiration = getTokenExpiration(accessToken);
|
|
18648
18654
|
const loginStatus = expiration && expiration <= new Date ? "Expired" : "Logged in";
|
|
18649
18655
|
const identity = resolveSessionIdentity(accessToken);
|
|
18656
|
+
const scopes = readTokenScopes(accessToken);
|
|
18650
18657
|
return {
|
|
18651
18658
|
loginStatus,
|
|
18652
18659
|
accessToken,
|
|
@@ -18657,7 +18664,8 @@ var readAuthFromEnv = () => {
|
|
|
18657
18664
|
tenantId,
|
|
18658
18665
|
expiration,
|
|
18659
18666
|
source: "env-vars" /* EnvironmentVariables */,
|
|
18660
|
-
...identity ? { identity } : {}
|
|
18667
|
+
...identity ? { identity } : {},
|
|
18668
|
+
...scopes ? { scopes } : {}
|
|
18661
18669
|
};
|
|
18662
18670
|
};
|
|
18663
18671
|
|
|
@@ -19305,10 +19313,15 @@ async function buildFileStatus(tokens, credentials, globalHint) {
|
|
|
19305
19313
|
}
|
|
19306
19314
|
function identityFields(accessToken, credentials) {
|
|
19307
19315
|
const identity = resolveSessionIdentity(accessToken, parseAuthFlow(credentials[AUTH_FLOW_ENV_VAR]));
|
|
19308
|
-
|
|
19316
|
+
const scopes = readTokenScopes(accessToken);
|
|
19317
|
+
return {
|
|
19318
|
+
...identity ? { identity } : {},
|
|
19319
|
+
...scopes ? { scopes } : {}
|
|
19320
|
+
};
|
|
19309
19321
|
}
|
|
19310
19322
|
function buildRobotStatus(robotCreds) {
|
|
19311
19323
|
const identity = resolveSessionIdentity(robotCreds.accessToken);
|
|
19324
|
+
const scopes = readTokenScopes(robotCreds.accessToken);
|
|
19312
19325
|
return {
|
|
19313
19326
|
loginStatus: "Logged in",
|
|
19314
19327
|
accessToken: robotCreds.accessToken,
|
|
@@ -19320,7 +19333,8 @@ function buildRobotStatus(robotCreds) {
|
|
|
19320
19333
|
issuer: robotCreds.issuer,
|
|
19321
19334
|
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
19322
19335
|
source: "robot" /* Robot */,
|
|
19323
|
-
...identity ? { identity } : {}
|
|
19336
|
+
...identity ? { identity } : {},
|
|
19337
|
+
...scopes ? { scopes } : {}
|
|
19324
19338
|
};
|
|
19325
19339
|
}
|
|
19326
19340
|
var isFileNotFoundError = (error) => {
|
|
@@ -19587,7 +19601,8 @@ var getAuthContext = async (options = {}) => {
|
|
|
19587
19601
|
organizationId: status.organizationId,
|
|
19588
19602
|
organizationName: status.organizationName,
|
|
19589
19603
|
tenantId: status.tenantId,
|
|
19590
|
-
tenantName
|
|
19604
|
+
tenantName,
|
|
19605
|
+
identity: status.identity
|
|
19591
19606
|
};
|
|
19592
19607
|
};
|
|
19593
19608
|
var getAuthEnv = async (options = {}) => {
|
|
@@ -19829,6 +19844,7 @@ export {
|
|
|
19829
19844
|
normalizeAuthProfileName,
|
|
19830
19845
|
parseAuthFlow,
|
|
19831
19846
|
parseJWT,
|
|
19847
|
+
readTokenScopes,
|
|
19832
19848
|
refreshAccessToken,
|
|
19833
19849
|
resolveAuthProfileDirPath,
|
|
19834
19850
|
resolveAuthProfileFilePath,
|
|
@@ -19841,4 +19857,4 @@ export {
|
|
|
19841
19857
|
setAuthFileConfig
|
|
19842
19858
|
};
|
|
19843
19859
|
|
|
19844
|
-
//# debugId=
|
|
19860
|
+
//# debugId=444F587B7EE9CE7764756E2164756E21
|
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, resolveSessionIdentity, type SessionIdentity, } from "./sessionIdentity";
|
|
15
|
+
export { 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
|
@@ -2213,6 +2213,11 @@ var pickName = (claims) => {
|
|
|
2213
2213
|
const username = asString(claims?.preferred_username);
|
|
2214
2214
|
return asString(claims?.name) ?? (looksLikeEmail(username) ? undefined : username);
|
|
2215
2215
|
};
|
|
2216
|
+
var pickScopes = (claims) => {
|
|
2217
|
+
const raw = claims?.scope;
|
|
2218
|
+
const scopes = Array.isArray(raw) ? raw.filter((entry) => asString(entry) !== undefined) : typeof raw === "string" ? raw.split(/\s+/).filter(Boolean) : [];
|
|
2219
|
+
return scopes.length > 0 ? scopes : undefined;
|
|
2220
|
+
};
|
|
2216
2221
|
var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
2217
2222
|
const claims = accessToken ? decodeClaims(accessToken) : undefined;
|
|
2218
2223
|
const email = pickEmail(claims);
|
|
@@ -2238,6 +2243,7 @@ var resolveSessionIdentity = (accessToken, authFlow) => {
|
|
|
2238
2243
|
identity.clientId = clientId;
|
|
2239
2244
|
return identity;
|
|
2240
2245
|
};
|
|
2246
|
+
var readTokenScopes = (accessToken) => accessToken ? pickScopes(decodeClaims(accessToken)) : undefined;
|
|
2241
2247
|
|
|
2242
2248
|
// src/envAuth.ts
|
|
2243
2249
|
var ENV_AUTH_ENABLE_VAR = "UIPATH_CLI_ENABLE_ENV_AUTH";
|
|
@@ -2310,6 +2316,7 @@ var readAuthFromEnv = () => {
|
|
|
2310
2316
|
const expiration = getTokenExpiration(accessToken);
|
|
2311
2317
|
const loginStatus = expiration && expiration <= new Date ? "Expired" : "Logged in";
|
|
2312
2318
|
const identity = resolveSessionIdentity(accessToken);
|
|
2319
|
+
const scopes = readTokenScopes(accessToken);
|
|
2313
2320
|
return {
|
|
2314
2321
|
loginStatus,
|
|
2315
2322
|
accessToken,
|
|
@@ -2320,7 +2327,8 @@ var readAuthFromEnv = () => {
|
|
|
2320
2327
|
tenantId,
|
|
2321
2328
|
expiration,
|
|
2322
2329
|
source: "env-vars" /* EnvironmentVariables */,
|
|
2323
|
-
...identity ? { identity } : {}
|
|
2330
|
+
...identity ? { identity } : {},
|
|
2331
|
+
...scopes ? { scopes } : {}
|
|
2324
2332
|
};
|
|
2325
2333
|
};
|
|
2326
2334
|
|
|
@@ -2972,10 +2980,15 @@ async function buildFileStatus(tokens, credentials, globalHint) {
|
|
|
2972
2980
|
}
|
|
2973
2981
|
function identityFields(accessToken, credentials) {
|
|
2974
2982
|
const identity = resolveSessionIdentity(accessToken, parseAuthFlow(credentials[AUTH_FLOW_ENV_VAR]));
|
|
2975
|
-
|
|
2983
|
+
const scopes = readTokenScopes(accessToken);
|
|
2984
|
+
return {
|
|
2985
|
+
...identity ? { identity } : {},
|
|
2986
|
+
...scopes ? { scopes } : {}
|
|
2987
|
+
};
|
|
2976
2988
|
}
|
|
2977
2989
|
function buildRobotStatus(robotCreds) {
|
|
2978
2990
|
const identity = resolveSessionIdentity(robotCreds.accessToken);
|
|
2991
|
+
const scopes = readTokenScopes(robotCreds.accessToken);
|
|
2979
2992
|
return {
|
|
2980
2993
|
loginStatus: "Logged in",
|
|
2981
2994
|
accessToken: robotCreds.accessToken,
|
|
@@ -2987,7 +3000,8 @@ function buildRobotStatus(robotCreds) {
|
|
|
2987
3000
|
issuer: robotCreds.issuer,
|
|
2988
3001
|
expiration: getTokenExpiration(robotCreds.accessToken),
|
|
2989
3002
|
source: "robot" /* Robot */,
|
|
2990
|
-
...identity ? { identity } : {}
|
|
3003
|
+
...identity ? { identity } : {},
|
|
3004
|
+
...scopes ? { scopes } : {}
|
|
2991
3005
|
};
|
|
2992
3006
|
}
|
|
2993
3007
|
var isFileNotFoundError = (error) => {
|
|
@@ -3254,7 +3268,8 @@ var getAuthContext = async (options = {}) => {
|
|
|
3254
3268
|
organizationId: status.organizationId,
|
|
3255
3269
|
organizationName: status.organizationName,
|
|
3256
3270
|
tenantId: status.tenantId,
|
|
3257
|
-
tenantName
|
|
3271
|
+
tenantName,
|
|
3272
|
+
identity: status.identity
|
|
3258
3273
|
};
|
|
3259
3274
|
};
|
|
3260
3275
|
var getAuthEnv = async (options = {}) => {
|
|
@@ -3989,6 +4004,7 @@ export {
|
|
|
3989
4004
|
parseAuthFlow,
|
|
3990
4005
|
parseJWT,
|
|
3991
4006
|
readAuthFromEnv,
|
|
4007
|
+
readTokenScopes,
|
|
3992
4008
|
refreshAccessToken,
|
|
3993
4009
|
refreshTokenFingerprint,
|
|
3994
4010
|
registerRobotClientLoader,
|
|
@@ -4005,4 +4021,4 @@ export {
|
|
|
4005
4021
|
setAuthFileConfig
|
|
4006
4022
|
};
|
|
4007
4023
|
|
|
4008
|
-
//# debugId=
|
|
4024
|
+
//# debugId=4C387A5C17766AD564756E2164756E21
|
package/dist/loginStatus.d.ts
CHANGED
|
@@ -52,6 +52,13 @@ export interface LoginStatus {
|
|
|
52
52
|
* not signature-verified.
|
|
53
53
|
*/
|
|
54
54
|
identity?: SessionIdentity;
|
|
55
|
+
/**
|
|
56
|
+
* Scopes the access token carries. Kept beside `identity` rather than
|
|
57
|
+
* inside it because a token can carry the claim while saying too little
|
|
58
|
+
* to name who is acting, and dropping the scopes there would hide them
|
|
59
|
+
* from exactly the sessions that are hardest to diagnose.
|
|
60
|
+
*/
|
|
61
|
+
scopes?: string[];
|
|
55
62
|
/**
|
|
56
63
|
* True when a refresh rotated successfully at the identity server but
|
|
57
64
|
* persisting the new pair to disk failed. The returned `accessToken` is
|
|
@@ -33,3 +33,16 @@ export declare const parseAuthFlow: (value: string | undefined) => AuthFlow | un
|
|
|
33
33
|
* omit the fields than to guess at who is acting.
|
|
34
34
|
*/
|
|
35
35
|
export declare const resolveSessionIdentity: (accessToken: string | undefined, authFlow?: AuthFlow) => SessionIdentity | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Scopes carried by an access token, in the order Identity returned them.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately independent of {@link resolveSessionIdentity}: a token can
|
|
40
|
+
* carry a `scope` claim while saying too little to name who is acting — no
|
|
41
|
+
* `sub_type`, no user claims, no known flow — and those are exactly the
|
|
42
|
+
* sessions that are hardest to diagnose. Hanging the scopes off the identity
|
|
43
|
+
* dropped them for that shape.
|
|
44
|
+
*
|
|
45
|
+
* Read from an *unverified* claim (base64 decode, no signature check), for
|
|
46
|
+
* display only. Never branch on it for an authorization decision.
|
|
47
|
+
*/
|
|
48
|
+
export declare const readTokenScopes: (accessToken: string | undefined) => string[] | undefined;
|
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.204.0-preview.206",
|
|
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": "ea7fd32a02acccf14e8e83522e69101430627b83"
|
|
42
42
|
}
|