@awsesh/core 1.0.19 → 1.1.2

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/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { AWSClient } from "./client";
3
3
  export { Credentials } from "./credentials";
4
4
  export { Sessions } from "./sessions";
5
5
  export { Storage } from "./storage";
6
+ export { createWorkflow } from "./workflow";
6
7
  import type { AwseshOptions, SSOSession, SSOLoginInfo, TokenCache, AccountCache, RoleCredentials, LastSelected, LastSelectedPerSession, ActiveCredential, LastSetCredential, SetCredentialOptions, SetCredentialResult } from "./types";
7
8
  export declare function createAwsesh(options: AwseshOptions): {
8
9
  sessions: {
@@ -46,6 +47,9 @@ export declare function createAwsesh(options: AwseshOptions): {
46
47
  save: (sessionName: string, accountId: string) => Promise<void>;
47
48
  getAll: () => Promise<LastSelectedPerSession>;
48
49
  };
50
+ lastProfiles: {
51
+ getAll: (sessionName: string) => Promise<Record<string, string>>;
52
+ };
49
53
  profileNames: {
50
54
  get: (sessionName: string, accountName: string, roleName: string) => Promise<string | undefined>;
51
55
  save: (sessionName: string, accountName: string, roleName: string, profileName: string) => Promise<void>;
package/index.js CHANGED
@@ -20238,6 +20238,209 @@ var Storage;
20238
20238
  }
20239
20239
  Storage.create = create;
20240
20240
  })(Storage ||= {});
20241
+ // src/workflow.ts
20242
+ import { setTimeout as setTimeout2 } from "node:timers/promises";
20243
+ function createWorkflow(awsesh) {
20244
+ const logins = new Map;
20245
+ function validateName(name) {
20246
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9 _-]{0,127}$/.test(name)) {
20247
+ throw new Error("Session names must contain letters, numbers, spaces, dashes or underscores");
20248
+ }
20249
+ }
20250
+ function validateRegion(region) {
20251
+ if (!/^[a-z]{2}(?:-[a-z]+)+-\d+$/.test(region))
20252
+ throw new Error("Invalid AWS region");
20253
+ }
20254
+ async function session(name) {
20255
+ validateName(name);
20256
+ const value = await awsesh.sessions.get(name);
20257
+ if (!value)
20258
+ throw new Error(`SSO session not found: ${name}`);
20259
+ return value;
20260
+ }
20261
+ async function token(value) {
20262
+ const cached = await awsesh.tokens.get(value.startUrl);
20263
+ if (!cached)
20264
+ throw new Error("Sign in to this SSO session first");
20265
+ return cached.token;
20266
+ }
20267
+ async function account(name, accountId) {
20268
+ const cache = await awsesh.accounts.get(name);
20269
+ const value = cache?.accounts.find((item) => item.accountId === accountId);
20270
+ if (!value)
20271
+ throw new Error("Account not found; refresh the account list");
20272
+ return value;
20273
+ }
20274
+ async function snapshot(name) {
20275
+ const sessions = await awsesh.sessions.list();
20276
+ const selected = name ? await session(name) : undefined;
20277
+ const credentials = await awsesh.activeCredentials.list();
20278
+ const cache = selected ? await awsesh.accounts.get(selected.name) : undefined;
20279
+ const roles = selected ? await awsesh.preferredRoles.getAll(selected.name) : {};
20280
+ const regions = selected ? await awsesh.preferredRegions.getAll(selected.name) : {};
20281
+ const profiles = selected ? await awsesh.lastProfiles.getAll(selected.name) : {};
20282
+ const previous = await awsesh.lastSetCredential.get();
20283
+ return {
20284
+ sessions: await Promise.all(sessions.map(async (value) => ({
20285
+ ...value,
20286
+ authenticated: !!await awsesh.tokens.get(value.startUrl)
20287
+ }))),
20288
+ session: selected?.name,
20289
+ accounts: await Promise.all((cache?.accounts ?? []).map(async (value) => ({
20290
+ ...value,
20291
+ preferredRole: value.roles.includes(roles[value.accountId]) ? roles[value.accountId] : undefined,
20292
+ region: regions[value.accountId] ?? selected?.defaultRegion,
20293
+ profiles: await awsesh.profileNames.getForAccount(selected?.name ?? "", value.name),
20294
+ lastProfile: profiles[value.accountId] ?? (previous?.sessionName === selected?.name && previous?.accountId === value.accountId ? previous.profileName : undefined) ?? credentials.findLast((credential) => credential.sessionName === selected?.name && credential.accountId === value.accountId)?.profileName
20295
+ }))),
20296
+ credentials,
20297
+ lastSelected: await awsesh.lastSelected.get(),
20298
+ lastAccount: selected ? await awsesh.lastAccountPerSession.get(selected.name) : undefined,
20299
+ lastSession: await awsesh.lastSession.get()
20300
+ };
20301
+ }
20302
+ return {
20303
+ snapshot,
20304
+ async selectSession(name, refresh = false) {
20305
+ const value = await session(name);
20306
+ const cache = await awsesh.accounts.get(name);
20307
+ if (refresh || !cache) {
20308
+ const accounts = await awsesh.sso.listAccounts(value, await token(value));
20309
+ await awsesh.accounts.save(name, { accounts, lastUpdated: Date.now() });
20310
+ }
20311
+ await awsesh.lastSession.save(name);
20312
+ return snapshot(name);
20313
+ },
20314
+ async saveSession(value, creating = false) {
20315
+ validateName(value.name);
20316
+ const url = new URL(value.startUrl);
20317
+ if (url.protocol !== "https:" || url.username || url.password)
20318
+ throw new Error("SSO start URL must use HTTPS");
20319
+ validateRegion(value.ssoRegion);
20320
+ validateRegion(value.defaultRegion);
20321
+ if (creating && await awsesh.sessions.exists(value.name))
20322
+ throw new Error("A session with this name already exists");
20323
+ await awsesh.sessions.save(value);
20324
+ return snapshot();
20325
+ },
20326
+ async removeSession(name) {
20327
+ await session(name);
20328
+ logins.delete(name);
20329
+ await awsesh.sessions.remove(name);
20330
+ return snapshot();
20331
+ },
20332
+ async startLogin(name) {
20333
+ const value = await session(name);
20334
+ const info = await awsesh.sso.startLogin(value);
20335
+ logins.set(name, info);
20336
+ return { url: info.verificationUriComplete || info.verificationUri, code: info.userCode };
20337
+ },
20338
+ async pollLogin(name) {
20339
+ const value = await session(name);
20340
+ const info = logins.get(name);
20341
+ if (!info)
20342
+ throw new Error("Start a new SSO login");
20343
+ if (info.expiresAt.getTime() <= Date.now()) {
20344
+ logins.delete(name);
20345
+ throw new Error("SSO authorization expired; sign in again");
20346
+ }
20347
+ await setTimeout2(Math.max(1, info.interval) * 1000);
20348
+ const result = await awsesh.sso.pollForToken(value, info);
20349
+ if (!result)
20350
+ return { complete: false };
20351
+ await awsesh.tokens.save(value.startUrl, result.token, result.expiresAt);
20352
+ logins.delete(name);
20353
+ return { complete: true };
20354
+ },
20355
+ async cancelLogin(name) {
20356
+ logins.delete(name);
20357
+ },
20358
+ async loadRoles(name, accountId, refresh = false) {
20359
+ const value = await session(name);
20360
+ const selected = await account(name, accountId);
20361
+ if (selected.rolesLoaded && !refresh)
20362
+ return snapshot(name);
20363
+ const roles = await awsesh.sso.listRoles(value, await token(value), accountId);
20364
+ const cache = await awsesh.accounts.get(name);
20365
+ if (!cache)
20366
+ throw new Error("Account cache no longer exists");
20367
+ await awsesh.accounts.save(name, {
20368
+ ...cache,
20369
+ accounts: cache.accounts.map((item) => item.accountId === accountId ? { ...item, roles, rolesLoaded: true } : item)
20370
+ });
20371
+ return snapshot(name);
20372
+ },
20373
+ async preferRole(name, accountId, role) {
20374
+ await session(name);
20375
+ const value = await account(name, accountId);
20376
+ if (!value.roles.includes(role))
20377
+ throw new Error("Role is not available for this account");
20378
+ await awsesh.preferredRoles.save(name, accountId, role);
20379
+ return snapshot(name);
20380
+ },
20381
+ async setRegion(name, accountId, region) {
20382
+ await session(name);
20383
+ await account(name, accountId);
20384
+ validateRegion(region);
20385
+ await awsesh.preferredRegions.save(name, accountId, region);
20386
+ return snapshot(name);
20387
+ },
20388
+ async setProfile(name, accountId, role, profile) {
20389
+ await session(name);
20390
+ const value = await account(name, accountId);
20391
+ if (!value.roles.includes(role))
20392
+ throw new Error("Role is not available for this account");
20393
+ if (profile && !/^[a-zA-Z0-9_-]{1,128}$/.test(profile))
20394
+ throw new Error("Invalid CLI profile name");
20395
+ if (profile)
20396
+ await awsesh.profileNames.save(name, value.name, role, profile);
20397
+ if (!profile)
20398
+ await awsesh.profileNames.remove(name, value.name, role);
20399
+ return snapshot(name);
20400
+ },
20401
+ async assumeRole(name, accountId, role) {
20402
+ const value = await session(name);
20403
+ const selected = await account(name, accountId);
20404
+ if (!selected.roles.includes(role))
20405
+ throw new Error("Role is not available for this account");
20406
+ const credentials = await awsesh.sso.getCredentials(value, await token(value), accountId, role);
20407
+ const region = await awsesh.preferredRegions.get(name, accountId) ?? value.defaultRegion;
20408
+ await awsesh.setCredential({ credentials, sessionName: name, accountId, accountName: selected.name, roleName: role, region });
20409
+ await awsesh.lastAccountPerSession.save(name, accountId);
20410
+ return snapshot(name);
20411
+ },
20412
+ async consoleUrl(name, accountId, role) {
20413
+ const value = await session(name);
20414
+ if (!accountId)
20415
+ return awsesh.sso.getDashboardUrl(value);
20416
+ const selected = await account(name, accountId);
20417
+ if (!role || !selected.roles.includes(role))
20418
+ throw new Error("Select an available role first");
20419
+ return awsesh.sso.getAccountUrl(value, accountId, "", role);
20420
+ },
20421
+ async clearCredential(accountId, role, profile, name) {
20422
+ const active = await awsesh.activeCredentials.list();
20423
+ if (!active.some((value) => value.accountId === accountId && value.roleName === role && value.profileName === profile)) {
20424
+ throw new Error("Active credential not found");
20425
+ }
20426
+ await awsesh.clearCredential(accountId, role, profile);
20427
+ return snapshot(name);
20428
+ },
20429
+ async clearAllCredentials(name) {
20430
+ if (name)
20431
+ await session(name);
20432
+ await awsesh.clearAllCredentials(true);
20433
+ return snapshot(name);
20434
+ },
20435
+ async signOut(name) {
20436
+ const value = await session(name);
20437
+ logins.delete(name);
20438
+ await awsesh.clearSessionCredentials(name, true);
20439
+ await awsesh.tokens.remove(value.startUrl);
20440
+ return snapshot(name);
20441
+ }
20442
+ };
20443
+ }
20241
20444
 
20242
20445
  // src/index.ts
20243
20446
  import { createHash } from "node:crypto";
@@ -20370,6 +20573,12 @@ function createAwsesh(options) {
20370
20573
  return data ?? {};
20371
20574
  }
20372
20575
  },
20576
+ lastProfiles: {
20577
+ getAll: async (sessionName) => {
20578
+ const data = await storage.read("preference/last-profiles");
20579
+ return data?.[sessionName] ?? {};
20580
+ }
20581
+ },
20373
20582
  profileNames: {
20374
20583
  get: async (sessionName, accountName, roleName) => {
20375
20584
  const data = await storage.read("preference/profile-names");
@@ -20555,6 +20764,11 @@ function createAwsesh(options) {
20555
20764
  region,
20556
20765
  setAt: new Date().toISOString()
20557
20766
  });
20767
+ await storage.update("preference/last-profiles", (draft) => {
20768
+ draft[sessionName] ??= {};
20769
+ draft[sessionName][accountId] = profileName;
20770
+ return draft;
20771
+ });
20558
20772
  await storage.update("preference/last-selected", (existing) => ({
20559
20773
  ...existing,
20560
20774
  session: sessionName,
@@ -20616,5 +20830,6 @@ export {
20616
20830
  Credentials,
20617
20831
  Sessions,
20618
20832
  Storage,
20619
- createAwsesh
20833
+ createAwsesh,
20834
+ createWorkflow
20620
20835
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsesh/core",
3
- "version": "1.0.19",
3
+ "version": "1.1.2",
4
4
  "description": "AWS SSO session management SDK",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/workflow.d.ts ADDED
@@ -0,0 +1,314 @@
1
+ import type { Awsesh } from "./index";
2
+ import type { SSOSession } from "./types";
3
+ /** Presentation-independent operations for interactive clients. Never returns credentials or tokens. */
4
+ export declare function createWorkflow(awsesh: Awsesh): {
5
+ snapshot: (name?: string) => Promise<{
6
+ sessions: {
7
+ authenticated: boolean;
8
+ name: string;
9
+ startUrl: string;
10
+ ssoRegion: string;
11
+ defaultRegion: string;
12
+ isChina?: boolean;
13
+ }[];
14
+ session: string | undefined;
15
+ accounts: {
16
+ preferredRole: string | undefined;
17
+ region: string;
18
+ profiles: Record<string, string>;
19
+ lastProfile: string;
20
+ accountId: string;
21
+ name: string;
22
+ roles: string[];
23
+ rolesLoaded: boolean;
24
+ }[];
25
+ credentials: import("./types").ActiveCredential[];
26
+ lastSelected: import("./types").LastSelected;
27
+ lastAccount: string | undefined;
28
+ lastSession: string | undefined;
29
+ }>;
30
+ selectSession(name: string, refresh?: boolean): Promise<{
31
+ sessions: {
32
+ authenticated: boolean;
33
+ name: string;
34
+ startUrl: string;
35
+ ssoRegion: string;
36
+ defaultRegion: string;
37
+ isChina?: boolean;
38
+ }[];
39
+ session: string | undefined;
40
+ accounts: {
41
+ preferredRole: string | undefined;
42
+ region: string;
43
+ profiles: Record<string, string>;
44
+ lastProfile: string;
45
+ accountId: string;
46
+ name: string;
47
+ roles: string[];
48
+ rolesLoaded: boolean;
49
+ }[];
50
+ credentials: import("./types").ActiveCredential[];
51
+ lastSelected: import("./types").LastSelected;
52
+ lastAccount: string | undefined;
53
+ lastSession: string | undefined;
54
+ }>;
55
+ saveSession(value: SSOSession, creating?: boolean): Promise<{
56
+ sessions: {
57
+ authenticated: boolean;
58
+ name: string;
59
+ startUrl: string;
60
+ ssoRegion: string;
61
+ defaultRegion: string;
62
+ isChina?: boolean;
63
+ }[];
64
+ session: string | undefined;
65
+ accounts: {
66
+ preferredRole: string | undefined;
67
+ region: string;
68
+ profiles: Record<string, string>;
69
+ lastProfile: string;
70
+ accountId: string;
71
+ name: string;
72
+ roles: string[];
73
+ rolesLoaded: boolean;
74
+ }[];
75
+ credentials: import("./types").ActiveCredential[];
76
+ lastSelected: import("./types").LastSelected;
77
+ lastAccount: string | undefined;
78
+ lastSession: string | undefined;
79
+ }>;
80
+ removeSession(name: string): Promise<{
81
+ sessions: {
82
+ authenticated: boolean;
83
+ name: string;
84
+ startUrl: string;
85
+ ssoRegion: string;
86
+ defaultRegion: string;
87
+ isChina?: boolean;
88
+ }[];
89
+ session: string | undefined;
90
+ accounts: {
91
+ preferredRole: string | undefined;
92
+ region: string;
93
+ profiles: Record<string, string>;
94
+ lastProfile: string;
95
+ accountId: string;
96
+ name: string;
97
+ roles: string[];
98
+ rolesLoaded: boolean;
99
+ }[];
100
+ credentials: import("./types").ActiveCredential[];
101
+ lastSelected: import("./types").LastSelected;
102
+ lastAccount: string | undefined;
103
+ lastSession: string | undefined;
104
+ }>;
105
+ startLogin(name: string): Promise<{
106
+ url: string;
107
+ code: string;
108
+ }>;
109
+ pollLogin(name: string): Promise<{
110
+ complete: boolean;
111
+ }>;
112
+ cancelLogin(name: string): Promise<void>;
113
+ loadRoles(name: string, accountId: string, refresh?: boolean): Promise<{
114
+ sessions: {
115
+ authenticated: boolean;
116
+ name: string;
117
+ startUrl: string;
118
+ ssoRegion: string;
119
+ defaultRegion: string;
120
+ isChina?: boolean;
121
+ }[];
122
+ session: string | undefined;
123
+ accounts: {
124
+ preferredRole: string | undefined;
125
+ region: string;
126
+ profiles: Record<string, string>;
127
+ lastProfile: string;
128
+ accountId: string;
129
+ name: string;
130
+ roles: string[];
131
+ rolesLoaded: boolean;
132
+ }[];
133
+ credentials: import("./types").ActiveCredential[];
134
+ lastSelected: import("./types").LastSelected;
135
+ lastAccount: string | undefined;
136
+ lastSession: string | undefined;
137
+ }>;
138
+ preferRole(name: string, accountId: string, role: string): Promise<{
139
+ sessions: {
140
+ authenticated: boolean;
141
+ name: string;
142
+ startUrl: string;
143
+ ssoRegion: string;
144
+ defaultRegion: string;
145
+ isChina?: boolean;
146
+ }[];
147
+ session: string | undefined;
148
+ accounts: {
149
+ preferredRole: string | undefined;
150
+ region: string;
151
+ profiles: Record<string, string>;
152
+ lastProfile: string;
153
+ accountId: string;
154
+ name: string;
155
+ roles: string[];
156
+ rolesLoaded: boolean;
157
+ }[];
158
+ credentials: import("./types").ActiveCredential[];
159
+ lastSelected: import("./types").LastSelected;
160
+ lastAccount: string | undefined;
161
+ lastSession: string | undefined;
162
+ }>;
163
+ setRegion(name: string, accountId: string, region: string): Promise<{
164
+ sessions: {
165
+ authenticated: boolean;
166
+ name: string;
167
+ startUrl: string;
168
+ ssoRegion: string;
169
+ defaultRegion: string;
170
+ isChina?: boolean;
171
+ }[];
172
+ session: string | undefined;
173
+ accounts: {
174
+ preferredRole: string | undefined;
175
+ region: string;
176
+ profiles: Record<string, string>;
177
+ lastProfile: string;
178
+ accountId: string;
179
+ name: string;
180
+ roles: string[];
181
+ rolesLoaded: boolean;
182
+ }[];
183
+ credentials: import("./types").ActiveCredential[];
184
+ lastSelected: import("./types").LastSelected;
185
+ lastAccount: string | undefined;
186
+ lastSession: string | undefined;
187
+ }>;
188
+ setProfile(name: string, accountId: string, role: string, profile: string): Promise<{
189
+ sessions: {
190
+ authenticated: boolean;
191
+ name: string;
192
+ startUrl: string;
193
+ ssoRegion: string;
194
+ defaultRegion: string;
195
+ isChina?: boolean;
196
+ }[];
197
+ session: string | undefined;
198
+ accounts: {
199
+ preferredRole: string | undefined;
200
+ region: string;
201
+ profiles: Record<string, string>;
202
+ lastProfile: string;
203
+ accountId: string;
204
+ name: string;
205
+ roles: string[];
206
+ rolesLoaded: boolean;
207
+ }[];
208
+ credentials: import("./types").ActiveCredential[];
209
+ lastSelected: import("./types").LastSelected;
210
+ lastAccount: string | undefined;
211
+ lastSession: string | undefined;
212
+ }>;
213
+ assumeRole(name: string, accountId: string, role: string): Promise<{
214
+ sessions: {
215
+ authenticated: boolean;
216
+ name: string;
217
+ startUrl: string;
218
+ ssoRegion: string;
219
+ defaultRegion: string;
220
+ isChina?: boolean;
221
+ }[];
222
+ session: string | undefined;
223
+ accounts: {
224
+ preferredRole: string | undefined;
225
+ region: string;
226
+ profiles: Record<string, string>;
227
+ lastProfile: string;
228
+ accountId: string;
229
+ name: string;
230
+ roles: string[];
231
+ rolesLoaded: boolean;
232
+ }[];
233
+ credentials: import("./types").ActiveCredential[];
234
+ lastSelected: import("./types").LastSelected;
235
+ lastAccount: string | undefined;
236
+ lastSession: string | undefined;
237
+ }>;
238
+ consoleUrl(name: string, accountId?: string, role?: string): Promise<string>;
239
+ clearCredential(accountId: string, role: string, profile: string, name?: string): Promise<{
240
+ sessions: {
241
+ authenticated: boolean;
242
+ name: string;
243
+ startUrl: string;
244
+ ssoRegion: string;
245
+ defaultRegion: string;
246
+ isChina?: boolean;
247
+ }[];
248
+ session: string | undefined;
249
+ accounts: {
250
+ preferredRole: string | undefined;
251
+ region: string;
252
+ profiles: Record<string, string>;
253
+ lastProfile: string;
254
+ accountId: string;
255
+ name: string;
256
+ roles: string[];
257
+ rolesLoaded: boolean;
258
+ }[];
259
+ credentials: import("./types").ActiveCredential[];
260
+ lastSelected: import("./types").LastSelected;
261
+ lastAccount: string | undefined;
262
+ lastSession: string | undefined;
263
+ }>;
264
+ clearAllCredentials(name?: string): Promise<{
265
+ sessions: {
266
+ authenticated: boolean;
267
+ name: string;
268
+ startUrl: string;
269
+ ssoRegion: string;
270
+ defaultRegion: string;
271
+ isChina?: boolean;
272
+ }[];
273
+ session: string | undefined;
274
+ accounts: {
275
+ preferredRole: string | undefined;
276
+ region: string;
277
+ profiles: Record<string, string>;
278
+ lastProfile: string;
279
+ accountId: string;
280
+ name: string;
281
+ roles: string[];
282
+ rolesLoaded: boolean;
283
+ }[];
284
+ credentials: import("./types").ActiveCredential[];
285
+ lastSelected: import("./types").LastSelected;
286
+ lastAccount: string | undefined;
287
+ lastSession: string | undefined;
288
+ }>;
289
+ signOut(name: string): Promise<{
290
+ sessions: {
291
+ authenticated: boolean;
292
+ name: string;
293
+ startUrl: string;
294
+ ssoRegion: string;
295
+ defaultRegion: string;
296
+ isChina?: boolean;
297
+ }[];
298
+ session: string | undefined;
299
+ accounts: {
300
+ preferredRole: string | undefined;
301
+ region: string;
302
+ profiles: Record<string, string>;
303
+ lastProfile: string;
304
+ accountId: string;
305
+ name: string;
306
+ roles: string[];
307
+ rolesLoaded: boolean;
308
+ }[];
309
+ credentials: import("./types").ActiveCredential[];
310
+ lastSelected: import("./types").LastSelected;
311
+ lastAccount: string | undefined;
312
+ lastSession: string | undefined;
313
+ }>;
314
+ };