@onivoro/server-aws-sts 22.11.0 → 24.0.1
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 +830 -0
- package/jest.config.ts +11 -0
- package/package.json +10 -45
- package/project.json +23 -0
- package/{dist/cjs/index.d.ts → src/index.ts} +2 -0
- package/src/lib/classes/server-aws-sts-config.class.ts +4 -0
- package/src/lib/server-aws-sts.module.ts +33 -0
- package/src/lib/services/sts.service.ts +13 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +8 -0
- package/tsconfig.spec.json +21 -0
- package/dist/cjs/index.js +0 -19
- package/dist/cjs/lib/classes/server-aws-sts-config.class.d.ts +0 -6
- package/dist/cjs/lib/classes/server-aws-sts-config.class.js +0 -10
- package/dist/cjs/lib/server-aws-sts.module.d.ts +0 -4
- package/dist/cjs/lib/server-aws-sts.module.js +0 -46
- package/dist/cjs/lib/services/sts.service.d.ts +0 -8
- package/dist/cjs/lib/services/sts.service.js +0 -32
- package/dist/esm/index.d.ts +0 -3
- package/dist/esm/index.js +0 -19
- package/dist/esm/lib/classes/server-aws-sts-config.class.d.ts +0 -6
- package/dist/esm/lib/classes/server-aws-sts-config.class.js +0 -10
- package/dist/esm/lib/server-aws-sts.module.d.ts +0 -4
- package/dist/esm/lib/server-aws-sts.module.js +0 -46
- package/dist/esm/lib/services/sts.service.d.ts +0 -8
- package/dist/esm/lib/services/sts.service.js +0 -32
- package/dist/types/index.d.ts +0 -3
- package/dist/types/lib/classes/server-aws-sts-config.class.d.ts +0 -6
- package/dist/types/lib/server-aws-sts.module.d.ts +0 -4
- package/dist/types/lib/services/sts.service.d.ts +0 -8
package/README.md
ADDED
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
# @onivoro/server-aws-sts
|
|
2
|
+
|
|
3
|
+
A NestJS module for integrating with AWS STS (Security Token Service), providing temporary credential management, role assumption, identity verification, and cross-account access capabilities for your server applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @onivoro/server-aws-sts
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Identity Verification**: Get caller identity and account information
|
|
14
|
+
- **Role Assumption**: Assume IAM roles for temporary access
|
|
15
|
+
- **Cross-Account Access**: Access resources across different AWS accounts
|
|
16
|
+
- **Temporary Credentials**: Generate and manage temporary AWS credentials
|
|
17
|
+
- **Session Management**: Create and manage temporary security sessions
|
|
18
|
+
- **Token Management**: Handle session tokens and their expiration
|
|
19
|
+
- **MFA Support**: Support for multi-factor authentication in role assumption
|
|
20
|
+
- **External ID Validation**: Secure cross-account access with external IDs
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Module Configuration
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { ServerAwsStsModule } from '@onivoro/server-aws-sts';
|
|
28
|
+
|
|
29
|
+
@Module({
|
|
30
|
+
imports: [
|
|
31
|
+
ServerAwsStsModule.configure({
|
|
32
|
+
AWS_REGION: 'us-east-1',
|
|
33
|
+
AWS_PROFILE: process.env.AWS_PROFILE || 'default',
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
})
|
|
37
|
+
export class AppModule {}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Basic Usage
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
44
|
+
|
|
45
|
+
@Injectable()
|
|
46
|
+
export class IdentityService {
|
|
47
|
+
constructor(private stsService: StsService) {}
|
|
48
|
+
|
|
49
|
+
async getCurrentAccount() {
|
|
50
|
+
const accountId = await this.stsService.getAccountId();
|
|
51
|
+
return accountId;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async verifyIdentity() {
|
|
55
|
+
const identity = await this.stsService.stsClient.send(new GetCallerIdentityCommand({}));
|
|
56
|
+
return {
|
|
57
|
+
account: identity.Account,
|
|
58
|
+
arn: identity.Arn,
|
|
59
|
+
userId: identity.UserId
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
### ServerAwsStsConfig
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { ServerAwsStsConfig } from '@onivoro/server-aws-sts';
|
|
71
|
+
|
|
72
|
+
export class AppStsConfig extends ServerAwsStsConfig {
|
|
73
|
+
AWS_REGION = process.env.AWS_REGION || 'us-east-1';
|
|
74
|
+
AWS_PROFILE = process.env.AWS_PROFILE || 'default';
|
|
75
|
+
DEFAULT_SESSION_DURATION = parseInt(process.env.STS_SESSION_DURATION) || 3600; // 1 hour
|
|
76
|
+
MAX_SESSION_DURATION = parseInt(process.env.STS_MAX_SESSION_DURATION) || 43200; // 12 hours
|
|
77
|
+
EXTERNAL_ID = process.env.STS_EXTERNAL_ID; // For cross-account access
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Environment Variables
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# AWS Configuration
|
|
85
|
+
AWS_REGION=us-east-1
|
|
86
|
+
AWS_PROFILE=default
|
|
87
|
+
|
|
88
|
+
# STS Configuration
|
|
89
|
+
STS_SESSION_DURATION=3600
|
|
90
|
+
STS_MAX_SESSION_DURATION=43200
|
|
91
|
+
STS_EXTERNAL_ID=unique-external-identifier
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Services
|
|
95
|
+
|
|
96
|
+
### StsService
|
|
97
|
+
|
|
98
|
+
The main service for STS operations:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
102
|
+
|
|
103
|
+
@Injectable()
|
|
104
|
+
export class CredentialManagementService {
|
|
105
|
+
constructor(private stsService: StsService) {}
|
|
106
|
+
|
|
107
|
+
async getCurrentAccountInfo() {
|
|
108
|
+
const accountId = await this.stsService.getAccountId();
|
|
109
|
+
const identity = await this.stsService.stsClient.send(new GetCallerIdentityCommand({}));
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
accountId,
|
|
113
|
+
arn: identity.Arn,
|
|
114
|
+
userId: identity.UserId,
|
|
115
|
+
type: this.getIdentityType(identity.Arn!)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private getIdentityType(arn: string): string {
|
|
120
|
+
if (arn.includes(':user/')) return 'IAM User';
|
|
121
|
+
if (arn.includes(':role/')) return 'IAM Role';
|
|
122
|
+
if (arn.includes(':assumed-role/')) return 'Assumed Role';
|
|
123
|
+
if (arn.includes(':federated-user/')) return 'Federated User';
|
|
124
|
+
return 'Unknown';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Usage Examples
|
|
130
|
+
|
|
131
|
+
### Role Assumption Service
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
135
|
+
import { AssumeRoleCommand, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts';
|
|
136
|
+
|
|
137
|
+
@Injectable()
|
|
138
|
+
export class RoleAssumptionService {
|
|
139
|
+
constructor(private stsService: StsService) {}
|
|
140
|
+
|
|
141
|
+
async assumeRole(roleArn: string, sessionName: string, durationSeconds?: number) {
|
|
142
|
+
const command = new AssumeRoleCommand({
|
|
143
|
+
RoleArn: roleArn,
|
|
144
|
+
RoleSessionName: sessionName,
|
|
145
|
+
DurationSeconds: durationSeconds || 3600
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const response = await this.stsService.stsClient.send(command);
|
|
149
|
+
|
|
150
|
+
if (!response.Credentials) {
|
|
151
|
+
throw new Error('Failed to assume role');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
156
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
157
|
+
sessionToken: response.Credentials.SessionToken!,
|
|
158
|
+
expiration: response.Credentials.Expiration!,
|
|
159
|
+
assumedRoleUser: response.AssumedRoleUser
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async assumeRoleWithExternalId(roleArn: string, sessionName: string, externalId: string, durationSeconds?: number) {
|
|
164
|
+
const command = new AssumeRoleCommand({
|
|
165
|
+
RoleArn: roleArn,
|
|
166
|
+
RoleSessionName: sessionName,
|
|
167
|
+
ExternalId: externalId,
|
|
168
|
+
DurationSeconds: durationSeconds || 3600
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return this.executeAssumeRole(command);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async assumeRoleWithMFA(roleArn: string, sessionName: string, mfaDeviceArn: string, mfaToken: string) {
|
|
175
|
+
const command = new AssumeRoleCommand({
|
|
176
|
+
RoleArn: roleArn,
|
|
177
|
+
RoleSessionName: sessionName,
|
|
178
|
+
SerialNumber: mfaDeviceArn,
|
|
179
|
+
TokenCode: mfaToken,
|
|
180
|
+
DurationSeconds: 3600
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return this.executeAssumeRole(command);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async assumeRoleWithWebIdentity(roleArn: string, sessionName: string, webIdentityToken: string, providerId?: string) {
|
|
187
|
+
const command = new AssumeRoleWithWebIdentityCommand({
|
|
188
|
+
RoleArn: roleArn,
|
|
189
|
+
RoleSessionName: sessionName,
|
|
190
|
+
WebIdentityToken: webIdentityToken,
|
|
191
|
+
ProviderId: providerId,
|
|
192
|
+
DurationSeconds: 3600
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const response = await this.stsService.stsClient.send(command);
|
|
196
|
+
|
|
197
|
+
if (!response.Credentials) {
|
|
198
|
+
throw new Error('Failed to assume role with web identity');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
203
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
204
|
+
sessionToken: response.Credentials.SessionToken!,
|
|
205
|
+
expiration: response.Credentials.Expiration!,
|
|
206
|
+
assumedRoleUser: response.AssumedRoleUser,
|
|
207
|
+
audience: response.Audience,
|
|
208
|
+
provider: response.Provider
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private async executeAssumeRole(command: AssumeRoleCommand) {
|
|
213
|
+
const response = await this.stsService.stsClient.send(command);
|
|
214
|
+
|
|
215
|
+
if (!response.Credentials) {
|
|
216
|
+
throw new Error('Failed to assume role');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
221
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
222
|
+
sessionToken: response.Credentials.SessionToken!,
|
|
223
|
+
expiration: response.Credentials.Expiration!,
|
|
224
|
+
assumedRoleUser: response.AssumedRoleUser
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Cross-Account Access Service
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
234
|
+
|
|
235
|
+
@Injectable()
|
|
236
|
+
export class CrossAccountService {
|
|
237
|
+
constructor(private stsService: StsService) {}
|
|
238
|
+
|
|
239
|
+
async accessCrossAccountResource(
|
|
240
|
+
targetAccountId: string,
|
|
241
|
+
roleName: string,
|
|
242
|
+
externalId?: string,
|
|
243
|
+
sessionDuration: number = 3600
|
|
244
|
+
) {
|
|
245
|
+
const roleArn = `arn:aws:iam::${targetAccountId}:role/${roleName}`;
|
|
246
|
+
const sessionName = `cross-account-${Date.now()}`;
|
|
247
|
+
|
|
248
|
+
const command = new AssumeRoleCommand({
|
|
249
|
+
RoleArn: roleArn,
|
|
250
|
+
RoleSessionName: sessionName,
|
|
251
|
+
DurationSeconds: sessionDuration,
|
|
252
|
+
...(externalId && { ExternalId: externalId })
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const response = await this.stsService.stsClient.send(command);
|
|
256
|
+
|
|
257
|
+
if (!response.Credentials) {
|
|
258
|
+
throw new Error(`Failed to assume role in account ${targetAccountId}`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
credentials: {
|
|
263
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
264
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
265
|
+
sessionToken: response.Credentials.SessionToken!
|
|
266
|
+
},
|
|
267
|
+
expiration: response.Credentials.Expiration!,
|
|
268
|
+
targetAccount: targetAccountId,
|
|
269
|
+
assumedRole: roleArn
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async createCrossAccountClient<T>(
|
|
274
|
+
clientClass: new (config: any) => T,
|
|
275
|
+
targetAccountId: string,
|
|
276
|
+
roleName: string,
|
|
277
|
+
region: string,
|
|
278
|
+
externalId?: string
|
|
279
|
+
): Promise<T> {
|
|
280
|
+
const crossAccountAccess = await this.accessCrossAccountResource(
|
|
281
|
+
targetAccountId,
|
|
282
|
+
roleName,
|
|
283
|
+
externalId
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
return new clientClass({
|
|
287
|
+
region,
|
|
288
|
+
credentials: crossAccountAccess.credentials
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async validateCrossAccountAccess(targetAccountId: string, roleName: string, externalId?: string): Promise<boolean> {
|
|
293
|
+
try {
|
|
294
|
+
const access = await this.accessCrossAccountResource(targetAccountId, roleName, externalId, 900); // 15 minutes
|
|
295
|
+
|
|
296
|
+
// Test the credentials by calling GetCallerIdentity
|
|
297
|
+
const testStsClient = new STSClient({
|
|
298
|
+
credentials: access.credentials
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const identity = await testStsClient.send(new GetCallerIdentityCommand({}));
|
|
302
|
+
|
|
303
|
+
return identity.Account === targetAccountId;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error('Cross-account access validation failed:', error);
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Session Management Service
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
316
|
+
import { GetSessionTokenCommand } from '@aws-sdk/client-sts';
|
|
317
|
+
|
|
318
|
+
@Injectable()
|
|
319
|
+
export class SessionManagementService {
|
|
320
|
+
private activeSessions = new Map<string, SessionInfo>();
|
|
321
|
+
|
|
322
|
+
constructor(private stsService: StsService) {}
|
|
323
|
+
|
|
324
|
+
async createSession(sessionName: string, durationSeconds?: number, mfaDevice?: string, mfaToken?: string) {
|
|
325
|
+
const command = new GetSessionTokenCommand({
|
|
326
|
+
DurationSeconds: durationSeconds || 3600,
|
|
327
|
+
...(mfaDevice && mfaToken && {
|
|
328
|
+
SerialNumber: mfaDevice,
|
|
329
|
+
TokenCode: mfaToken
|
|
330
|
+
})
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const response = await this.stsService.stsClient.send(command);
|
|
334
|
+
|
|
335
|
+
if (!response.Credentials) {
|
|
336
|
+
throw new Error('Failed to create session token');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const sessionInfo: SessionInfo = {
|
|
340
|
+
sessionName,
|
|
341
|
+
credentials: {
|
|
342
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
343
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
344
|
+
sessionToken: response.Credentials.SessionToken!
|
|
345
|
+
},
|
|
346
|
+
expiration: response.Credentials.Expiration!,
|
|
347
|
+
createdAt: new Date(),
|
|
348
|
+
mfaUsed: !!(mfaDevice && mfaToken)
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
this.activeSessions.set(sessionName, sessionInfo);
|
|
352
|
+
|
|
353
|
+
return sessionInfo;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async refreshSession(sessionName: string, durationSeconds?: number) {
|
|
357
|
+
const existingSession = this.activeSessions.get(sessionName);
|
|
358
|
+
|
|
359
|
+
if (!existingSession) {
|
|
360
|
+
throw new Error(`Session ${sessionName} not found`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Create new session with same parameters
|
|
364
|
+
return this.createSession(sessionName, durationSeconds);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async getSession(sessionName: string): Promise<SessionInfo | undefined> {
|
|
368
|
+
const session = this.activeSessions.get(sessionName);
|
|
369
|
+
|
|
370
|
+
if (session && this.isSessionExpired(session)) {
|
|
371
|
+
this.activeSessions.delete(sessionName);
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return session;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async revokeSession(sessionName: string) {
|
|
379
|
+
this.activeSessions.delete(sessionName);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async getActiveSessions(): Promise<SessionInfo[]> {
|
|
383
|
+
const activeSessions = [];
|
|
384
|
+
|
|
385
|
+
for (const [name, session] of this.activeSessions.entries()) {
|
|
386
|
+
if (this.isSessionExpired(session)) {
|
|
387
|
+
this.activeSessions.delete(name);
|
|
388
|
+
} else {
|
|
389
|
+
activeSessions.push(session);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return activeSessions;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private isSessionExpired(session: SessionInfo): boolean {
|
|
397
|
+
return new Date() >= session.expiration;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async cleanupExpiredSessions() {
|
|
401
|
+
const expired = [];
|
|
402
|
+
|
|
403
|
+
for (const [name, session] of this.activeSessions.entries()) {
|
|
404
|
+
if (this.isSessionExpired(session)) {
|
|
405
|
+
expired.push(name);
|
|
406
|
+
this.activeSessions.delete(name);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
console.log(`Cleaned up ${expired.length} expired sessions`);
|
|
411
|
+
return expired;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Federated Identity Service
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
420
|
+
import { AssumeRoleWithSAMLCommand, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts';
|
|
421
|
+
|
|
422
|
+
@Injectable()
|
|
423
|
+
export class FederatedIdentityService {
|
|
424
|
+
constructor(private stsService: StsService) {}
|
|
425
|
+
|
|
426
|
+
async assumeRoleWithSAML(
|
|
427
|
+
roleArn: string,
|
|
428
|
+
principalArn: string,
|
|
429
|
+
samlAssertion: string,
|
|
430
|
+
sessionName?: string
|
|
431
|
+
) {
|
|
432
|
+
const command = new AssumeRoleWithSAMLCommand({
|
|
433
|
+
RoleArn: roleArn,
|
|
434
|
+
PrincipalArn: principalArn,
|
|
435
|
+
SAMLAssertion: samlAssertion,
|
|
436
|
+
DurationSeconds: 3600
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
const response = await this.stsService.stsClient.send(command);
|
|
440
|
+
|
|
441
|
+
if (!response.Credentials) {
|
|
442
|
+
throw new Error('Failed to assume role with SAML');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
credentials: {
|
|
447
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
448
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
449
|
+
sessionToken: response.Credentials.SessionToken!
|
|
450
|
+
},
|
|
451
|
+
expiration: response.Credentials.Expiration!,
|
|
452
|
+
assumedRoleUser: response.AssumedRoleUser,
|
|
453
|
+
audience: response.Audience,
|
|
454
|
+
issuer: response.Issuer,
|
|
455
|
+
subject: response.Subject,
|
|
456
|
+
subjectType: response.SubjectType
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async assumeRoleWithOIDC(
|
|
461
|
+
roleArn: string,
|
|
462
|
+
webIdentityToken: string,
|
|
463
|
+
providerId?: string,
|
|
464
|
+
sessionName?: string
|
|
465
|
+
) {
|
|
466
|
+
const command = new AssumeRoleWithWebIdentityCommand({
|
|
467
|
+
RoleArn: roleArn,
|
|
468
|
+
RoleSessionName: sessionName || `oidc-session-${Date.now()}`,
|
|
469
|
+
WebIdentityToken: webIdentityToken,
|
|
470
|
+
ProviderId: providerId,
|
|
471
|
+
DurationSeconds: 3600
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const response = await this.stsService.stsClient.send(command);
|
|
475
|
+
|
|
476
|
+
if (!response.Credentials) {
|
|
477
|
+
throw new Error('Failed to assume role with OIDC');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
credentials: {
|
|
482
|
+
accessKeyId: response.Credentials.AccessKeyId!,
|
|
483
|
+
secretAccessKey: response.Credentials.SecretAccessKey!,
|
|
484
|
+
sessionToken: response.Credentials.SessionToken!
|
|
485
|
+
},
|
|
486
|
+
expiration: response.Credentials.Expiration!,
|
|
487
|
+
assumedRoleUser: response.AssumedRoleUser,
|
|
488
|
+
audience: response.Audience,
|
|
489
|
+
provider: response.Provider
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async createFederatedSession(identityProvider: IdentityProvider, token: string, roleArn: string) {
|
|
494
|
+
switch (identityProvider.type) {
|
|
495
|
+
case 'SAML':
|
|
496
|
+
return this.assumeRoleWithSAML(
|
|
497
|
+
roleArn,
|
|
498
|
+
identityProvider.principalArn!,
|
|
499
|
+
token
|
|
500
|
+
);
|
|
501
|
+
case 'OIDC':
|
|
502
|
+
return this.assumeRoleWithOIDC(
|
|
503
|
+
roleArn,
|
|
504
|
+
token,
|
|
505
|
+
identityProvider.providerId
|
|
506
|
+
);
|
|
507
|
+
default:
|
|
508
|
+
throw new Error(`Unsupported identity provider type: ${identityProvider.type}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Token Validation Service
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { StsService } from '@onivoro/server-aws-sts';
|
|
518
|
+
|
|
519
|
+
@Injectable()
|
|
520
|
+
export class TokenValidationService {
|
|
521
|
+
constructor(private stsService: StsService) {}
|
|
522
|
+
|
|
523
|
+
async validateCredentials(credentials: AWSCredentials): Promise<ValidationResult> {
|
|
524
|
+
try {
|
|
525
|
+
const testStsClient = new STSClient({
|
|
526
|
+
credentials: {
|
|
527
|
+
accessKeyId: credentials.accessKeyId,
|
|
528
|
+
secretAccessKey: credentials.secretAccessKey,
|
|
529
|
+
sessionToken: credentials.sessionToken
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const identity = await testStsClient.send(new GetCallerIdentityCommand({}));
|
|
534
|
+
|
|
535
|
+
return {
|
|
536
|
+
valid: true,
|
|
537
|
+
identity: {
|
|
538
|
+
account: identity.Account!,
|
|
539
|
+
arn: identity.Arn!,
|
|
540
|
+
userId: identity.UserId!
|
|
541
|
+
},
|
|
542
|
+
expiresAt: credentials.expiration
|
|
543
|
+
};
|
|
544
|
+
} catch (error: any) {
|
|
545
|
+
return {
|
|
546
|
+
valid: false,
|
|
547
|
+
error: error.message,
|
|
548
|
+
errorCode: error.name
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async checkTokenExpiration(expiration: Date, bufferMinutes: number = 5): Promise<ExpirationCheck> {
|
|
554
|
+
const now = new Date();
|
|
555
|
+
const bufferTime = new Date(expiration.getTime() - (bufferMinutes * 60 * 1000));
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
isExpired: now >= expiration,
|
|
559
|
+
isNearExpiration: now >= bufferTime,
|
|
560
|
+
remainingMinutes: Math.max(0, Math.floor((expiration.getTime() - now.getTime()) / (60 * 1000))),
|
|
561
|
+
expiration
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async getIdentityFromToken(credentials: AWSCredentials) {
|
|
566
|
+
const validation = await this.validateCredentials(credentials);
|
|
567
|
+
|
|
568
|
+
if (!validation.valid) {
|
|
569
|
+
throw new Error(`Invalid credentials: ${validation.error}`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return validation.identity;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async isTemporaryCredential(credentials: AWSCredentials): Promise<boolean> {
|
|
576
|
+
// Temporary credentials always have a session token
|
|
577
|
+
return !!credentials.sessionToken;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Advanced Usage
|
|
583
|
+
|
|
584
|
+
### Credential Rotation Service
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
@Injectable()
|
|
588
|
+
export class CredentialRotationService {
|
|
589
|
+
private rotationSchedules = new Map<string, NodeJS.Timeout>();
|
|
590
|
+
|
|
591
|
+
constructor(
|
|
592
|
+
private stsService: StsService,
|
|
593
|
+
private sessionService: SessionManagementService
|
|
594
|
+
) {}
|
|
595
|
+
|
|
596
|
+
async scheduleCredentialRotation(
|
|
597
|
+
sessionName: string,
|
|
598
|
+
rotationIntervalMinutes: number = 50,
|
|
599
|
+
sessionDurationSeconds: number = 3600
|
|
600
|
+
) {
|
|
601
|
+
// Clear existing rotation if any
|
|
602
|
+
this.clearRotationSchedule(sessionName);
|
|
603
|
+
|
|
604
|
+
const rotationInterval = rotationIntervalMinutes * 60 * 1000;
|
|
605
|
+
|
|
606
|
+
const timer = setInterval(async () => {
|
|
607
|
+
try {
|
|
608
|
+
console.log(`Rotating credentials for session: ${sessionName}`);
|
|
609
|
+
await this.sessionService.refreshSession(sessionName, sessionDurationSeconds);
|
|
610
|
+
console.log(`Successfully rotated credentials for session: ${sessionName}`);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.error(`Failed to rotate credentials for session ${sessionName}:`, error);
|
|
613
|
+
}
|
|
614
|
+
}, rotationInterval);
|
|
615
|
+
|
|
616
|
+
this.rotationSchedules.set(sessionName, timer);
|
|
617
|
+
|
|
618
|
+
console.log(`Scheduled credential rotation for ${sessionName} every ${rotationIntervalMinutes} minutes`);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
clearRotationSchedule(sessionName: string) {
|
|
622
|
+
const existingTimer = this.rotationSchedules.get(sessionName);
|
|
623
|
+
if (existingTimer) {
|
|
624
|
+
clearInterval(existingTimer);
|
|
625
|
+
this.rotationSchedules.delete(sessionName);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
clearAllRotationSchedules() {
|
|
630
|
+
for (const [sessionName, timer] of this.rotationSchedules.entries()) {
|
|
631
|
+
clearInterval(timer);
|
|
632
|
+
}
|
|
633
|
+
this.rotationSchedules.clear();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
onModuleDestroy() {
|
|
637
|
+
this.clearAllRotationSchedules();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Multi-Account Manager
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
@Injectable()
|
|
646
|
+
export class MultiAccountManagerService {
|
|
647
|
+
constructor(
|
|
648
|
+
private stsService: StsService,
|
|
649
|
+
private crossAccountService: CrossAccountService
|
|
650
|
+
) {}
|
|
651
|
+
|
|
652
|
+
async assumeRoleInMultipleAccounts(
|
|
653
|
+
accounts: Array<{ accountId: string; roleName: string; externalId?: string }>,
|
|
654
|
+
sessionDuration: number = 3600
|
|
655
|
+
) {
|
|
656
|
+
const results = await Promise.allSettled(
|
|
657
|
+
accounts.map(account =>
|
|
658
|
+
this.crossAccountService.accessCrossAccountResource(
|
|
659
|
+
account.accountId,
|
|
660
|
+
account.roleName,
|
|
661
|
+
account.externalId,
|
|
662
|
+
sessionDuration
|
|
663
|
+
)
|
|
664
|
+
)
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
return accounts.map((account, index) => ({
|
|
668
|
+
accountId: account.accountId,
|
|
669
|
+
roleName: account.roleName,
|
|
670
|
+
success: results[index].status === 'fulfilled',
|
|
671
|
+
...(results[index].status === 'fulfilled'
|
|
672
|
+
? { credentials: (results[index] as PromiseFulfilledResult<any>).value }
|
|
673
|
+
: { error: (results[index] as PromiseRejectedResult).reason.message }
|
|
674
|
+
)
|
|
675
|
+
}));
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
async getAccountInventory() {
|
|
679
|
+
const currentAccount = await this.stsService.getAccountId();
|
|
680
|
+
const identity = await this.stsService.stsClient.send(new GetCallerIdentityCommand({}));
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
currentAccount,
|
|
684
|
+
currentIdentity: {
|
|
685
|
+
arn: identity.Arn!,
|
|
686
|
+
userId: identity.UserId!,
|
|
687
|
+
type: this.getIdentityType(identity.Arn!)
|
|
688
|
+
},
|
|
689
|
+
timestamp: new Date().toISOString()
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private getIdentityType(arn: string): string {
|
|
694
|
+
if (arn.includes(':user/')) return 'IAM User';
|
|
695
|
+
if (arn.includes(':role/')) return 'IAM Role';
|
|
696
|
+
if (arn.includes(':assumed-role/')) return 'Assumed Role';
|
|
697
|
+
return 'Unknown';
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
## Types and Interfaces
|
|
703
|
+
|
|
704
|
+
```typescript
|
|
705
|
+
interface SessionInfo {
|
|
706
|
+
sessionName: string;
|
|
707
|
+
credentials: {
|
|
708
|
+
accessKeyId: string;
|
|
709
|
+
secretAccessKey: string;
|
|
710
|
+
sessionToken: string;
|
|
711
|
+
};
|
|
712
|
+
expiration: Date;
|
|
713
|
+
createdAt: Date;
|
|
714
|
+
mfaUsed: boolean;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
interface AWSCredentials {
|
|
718
|
+
accessKeyId: string;
|
|
719
|
+
secretAccessKey: string;
|
|
720
|
+
sessionToken?: string;
|
|
721
|
+
expiration?: Date;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
interface ValidationResult {
|
|
725
|
+
valid: boolean;
|
|
726
|
+
identity?: {
|
|
727
|
+
account: string;
|
|
728
|
+
arn: string;
|
|
729
|
+
userId: string;
|
|
730
|
+
};
|
|
731
|
+
error?: string;
|
|
732
|
+
errorCode?: string;
|
|
733
|
+
expiresAt?: Date;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
interface ExpirationCheck {
|
|
737
|
+
isExpired: boolean;
|
|
738
|
+
isNearExpiration: boolean;
|
|
739
|
+
remainingMinutes: number;
|
|
740
|
+
expiration: Date;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
interface IdentityProvider {
|
|
744
|
+
type: 'SAML' | 'OIDC';
|
|
745
|
+
providerId?: string;
|
|
746
|
+
principalArn?: string;
|
|
747
|
+
}
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
## Best Practices
|
|
751
|
+
|
|
752
|
+
### 1. Session Duration Management
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
// Use appropriate session durations
|
|
756
|
+
const shortSession = 900; // 15 minutes for high-privilege operations
|
|
757
|
+
const normalSession = 3600; // 1 hour for regular operations
|
|
758
|
+
const longSession = 43200; // 12 hours for batch jobs (max)
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### 2. Error Handling
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
async safeAssumeRole(roleArn: string, sessionName: string): Promise<any | null> {
|
|
765
|
+
try {
|
|
766
|
+
return await this.assumeRole(roleArn, sessionName);
|
|
767
|
+
} catch (error: any) {
|
|
768
|
+
if (error.name === 'AccessDenied') {
|
|
769
|
+
console.error('Insufficient permissions to assume role');
|
|
770
|
+
} else if (error.name === 'InvalidParameterValue') {
|
|
771
|
+
console.error('Invalid role ARN or session name');
|
|
772
|
+
}
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### 3. Security Best Practices
|
|
779
|
+
|
|
780
|
+
```typescript
|
|
781
|
+
// Always use external IDs for cross-account access
|
|
782
|
+
// Rotate credentials regularly
|
|
783
|
+
// Use MFA when possible
|
|
784
|
+
// Monitor session usage
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## Testing
|
|
788
|
+
|
|
789
|
+
```typescript
|
|
790
|
+
import { Test, TestingModule } from '@nestjs/testing';
|
|
791
|
+
import { ServerAwsStsModule, StsService } from '@onivoro/server-aws-sts';
|
|
792
|
+
|
|
793
|
+
describe('StsService', () => {
|
|
794
|
+
let service: StsService;
|
|
795
|
+
|
|
796
|
+
beforeEach(async () => {
|
|
797
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
798
|
+
imports: [ServerAwsStsModule.configure({
|
|
799
|
+
AWS_REGION: 'us-east-1',
|
|
800
|
+
AWS_PROFILE: 'test'
|
|
801
|
+
})],
|
|
802
|
+
}).compile();
|
|
803
|
+
|
|
804
|
+
service = module.get<StsService>(StsService);
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
it('should be defined', () => {
|
|
808
|
+
expect(service).toBeDefined();
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it('should get account ID', async () => {
|
|
812
|
+
const accountId = await service.getAccountId();
|
|
813
|
+
expect(accountId).toBeDefined();
|
|
814
|
+
expect(typeof accountId).toBe('string');
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
## API Reference
|
|
820
|
+
|
|
821
|
+
### Exported Classes
|
|
822
|
+
- `ServerAwsStsConfig`: Configuration class for STS settings
|
|
823
|
+
- `ServerAwsStsModule`: NestJS module for STS integration
|
|
824
|
+
|
|
825
|
+
### Exported Services
|
|
826
|
+
- `StsService`: Main STS service with identity verification and account management capabilities
|
|
827
|
+
|
|
828
|
+
## License
|
|
829
|
+
|
|
830
|
+
This package is part of the Onivoro monorepo and follows the same licensing terms.
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
export default {
|
|
3
|
+
displayName: 'lib-server-aws-sts',
|
|
4
|
+
preset: '../../../jest.preset.js',
|
|
5
|
+
testEnvironment: 'node',
|
|
6
|
+
transform: {
|
|
7
|
+
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
|
8
|
+
},
|
|
9
|
+
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
10
|
+
coverageDirectory: '../../../coverage/libs/server/aws-sts',
|
|
11
|
+
};
|
package/package.json
CHANGED
|
@@ -1,47 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
],
|
|
13
|
-
"scripts": {
|
|
14
|
-
"onx": "onx",
|
|
15
|
-
"build": "onx Build",
|
|
16
|
-
"deploy": "onx Publish",
|
|
17
|
-
"test": "onx Test",
|
|
18
|
-
"update": "onx Update"
|
|
19
|
-
},
|
|
20
|
-
"exports": {
|
|
21
|
-
".": {
|
|
22
|
-
"types": "./dist/types/index.d.ts",
|
|
23
|
-
"require": "./dist/cjs/index.js",
|
|
24
|
-
"import": "./dist/esm/index.js",
|
|
25
|
-
"default": "./dist/esm/lib.js"
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
"onx": {
|
|
29
|
-
"platform": "server",
|
|
30
|
-
"module": "commonjs"
|
|
31
|
-
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@onivoro/cli": "^22.0.8",
|
|
34
|
-
"@types/jest": "*",
|
|
35
|
-
"@types/node": "22.10.7",
|
|
36
|
-
"typescript": "*"
|
|
37
|
-
},
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": "22.10.0",
|
|
40
|
-
"npm": "10.9.0"
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@aws-sdk/client-sts": "^3.731.1",
|
|
44
|
-
"@nestjs/common": "^11.0.1",
|
|
45
|
-
"@onivoro/server-common": "^22.11.0"
|
|
46
|
-
}
|
|
2
|
+
"name": "@onivoro/server-aws-sts",
|
|
3
|
+
"version": "24.0.1",
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@onivoro/server-aws-credential-providers": "24.0.1",
|
|
9
|
+
"@onivoro/server-common": "24.0.1",
|
|
10
|
+
"tslib": "^2.3.0"
|
|
11
|
+
}
|
|
47
12
|
}
|
package/project.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lib-server-aws-sts",
|
|
3
|
+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "libs/server/aws-sts/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"targets": {
|
|
7
|
+
"build": {
|
|
8
|
+
"executor": "@nx/js:tsc",
|
|
9
|
+
"outputs": ["{options.outputPath}"],
|
|
10
|
+
"options": {
|
|
11
|
+
"outputPath": "dist/libs/server/aws-sts",
|
|
12
|
+
"main": "libs/server/aws-sts/src/index.ts",
|
|
13
|
+
"tsConfig": "libs/server/aws-sts/tsconfig.lib.json",
|
|
14
|
+
"assets": [
|
|
15
|
+
"libs/server/aws-sts/README.md",
|
|
16
|
+
"libs/server/aws-sts/package.json"
|
|
17
|
+
],
|
|
18
|
+
"declaration": true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"tags": []
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { moduleFactory } from '@onivoro/server-common';
|
|
3
|
+
import { STSClient } from '@aws-sdk/client-sts';
|
|
4
|
+
import { StsService } from './services/sts.service';
|
|
5
|
+
import { ServerAwsStsConfig } from './classes/server-aws-sts-config.class';
|
|
6
|
+
import { AwsCredentials, ServerAwsCredentialProvidersModule } from '@onivoro/server-aws-credential-providers';
|
|
7
|
+
|
|
8
|
+
let stsClient: STSClient | null = null;
|
|
9
|
+
|
|
10
|
+
@Module({})
|
|
11
|
+
export class ServerAwsStsModule {
|
|
12
|
+
static configure(config: ServerAwsStsConfig) {
|
|
13
|
+
return moduleFactory({
|
|
14
|
+
module: ServerAwsStsModule,
|
|
15
|
+
imports: [ServerAwsCredentialProvidersModule.configure(config)],
|
|
16
|
+
providers: [
|
|
17
|
+
{
|
|
18
|
+
provide: STSClient,
|
|
19
|
+
useFactory: (credentials: AwsCredentials) => stsClient
|
|
20
|
+
? stsClient
|
|
21
|
+
: stsClient = new STSClient({
|
|
22
|
+
region: config.AWS_REGION,
|
|
23
|
+
logger: console,
|
|
24
|
+
credentials
|
|
25
|
+
}),
|
|
26
|
+
inject: [AwsCredentials]
|
|
27
|
+
},
|
|
28
|
+
{ provide: ServerAwsStsConfig, useValue: config },
|
|
29
|
+
StsService
|
|
30
|
+
]
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
|
|
3
|
+
import { ServerAwsStsConfig } from '../classes/server-aws-sts-config.class';
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class StsService {
|
|
7
|
+
constructor(public readonly stsClient: STSClient, private config: ServerAwsStsConfig) { }
|
|
8
|
+
|
|
9
|
+
async getAccountId() {
|
|
10
|
+
const response = await this.stsClient.send(new GetCallerIdentityCommand({}));
|
|
11
|
+
return response.Account;
|
|
12
|
+
}
|
|
13
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.server.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../../dist/out-tsc"
|
|
5
|
+
},
|
|
6
|
+
"files": [],
|
|
7
|
+
"include": [],
|
|
8
|
+
"references": [
|
|
9
|
+
{
|
|
10
|
+
"path": "./tsconfig.lib.json"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"path": "./tsconfig.spec.json"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"types": [
|
|
5
|
+
"jest",
|
|
6
|
+
"node"
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
"include": [
|
|
10
|
+
"jest.config.ts",
|
|
11
|
+
"**/*.test.ts",
|
|
12
|
+
"**/*.spec.ts",
|
|
13
|
+
"**/*.test.tsx",
|
|
14
|
+
"**/*.spec.tsx",
|
|
15
|
+
"**/*.test.js",
|
|
16
|
+
"**/*.spec.js",
|
|
17
|
+
"**/*.test.jsx",
|
|
18
|
+
"**/*.spec.jsx",
|
|
19
|
+
"**/*.d.ts"
|
|
20
|
+
]
|
|
21
|
+
}
|
package/dist/cjs/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./lib/classes/server-aws-sts-config.class"), exports);
|
|
18
|
-
__exportStar(require("./lib/services/sts.service"), exports);
|
|
19
|
-
__exportStar(require("./lib/server-aws-sts.module"), exports);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ServerAwsStsConfig = void 0;
|
|
4
|
-
class ServerAwsStsConfig {
|
|
5
|
-
AWS_ACCESS_KEY_ID;
|
|
6
|
-
AWS_REGION;
|
|
7
|
-
AWS_SECRET_ACCESS_KEY;
|
|
8
|
-
NODE_ENV;
|
|
9
|
-
}
|
|
10
|
-
exports.ServerAwsStsConfig = ServerAwsStsConfig;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var ServerAwsStsModule_1;
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.ServerAwsStsModule = void 0;
|
|
11
|
-
const common_1 = require("@nestjs/common");
|
|
12
|
-
const server_common_1 = require("@onivoro/server-common");
|
|
13
|
-
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
14
|
-
const sts_service_1 = require("./services/sts.service");
|
|
15
|
-
const server_aws_sts_config_class_1 = require("./classes/server-aws-sts-config.class");
|
|
16
|
-
let stsClient = null;
|
|
17
|
-
let ServerAwsStsModule = ServerAwsStsModule_1 = class ServerAwsStsModule {
|
|
18
|
-
static configure(config) {
|
|
19
|
-
return (0, server_common_1.moduleFactory)({
|
|
20
|
-
module: ServerAwsStsModule_1,
|
|
21
|
-
providers: [
|
|
22
|
-
{
|
|
23
|
-
provide: client_sts_1.STSClient,
|
|
24
|
-
useFactory: () => stsClient
|
|
25
|
-
? stsClient
|
|
26
|
-
: stsClient = new client_sts_1.STSClient({
|
|
27
|
-
region: config.AWS_REGION,
|
|
28
|
-
logger: console,
|
|
29
|
-
credentials: config.NODE_ENV === 'production'
|
|
30
|
-
? undefined
|
|
31
|
-
: {
|
|
32
|
-
accessKeyId: config.AWS_ACCESS_KEY_ID,
|
|
33
|
-
secretAccessKey: config.AWS_SECRET_ACCESS_KEY
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
},
|
|
37
|
-
{ provide: server_aws_sts_config_class_1.ServerAwsStsConfig, useValue: config },
|
|
38
|
-
sts_service_1.StsService
|
|
39
|
-
]
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
exports.ServerAwsStsModule = ServerAwsStsModule;
|
|
44
|
-
exports.ServerAwsStsModule = ServerAwsStsModule = ServerAwsStsModule_1 = __decorate([
|
|
45
|
-
(0, common_1.Module)({})
|
|
46
|
-
], ServerAwsStsModule);
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { STSClient } from '@aws-sdk/client-sts';
|
|
2
|
-
import { ServerAwsStsConfig } from '../classes/server-aws-sts-config.class';
|
|
3
|
-
export declare class StsService {
|
|
4
|
-
readonly stsClient: STSClient;
|
|
5
|
-
private config;
|
|
6
|
-
constructor(stsClient: STSClient, config: ServerAwsStsConfig);
|
|
7
|
-
getAccountId(): Promise<string>;
|
|
8
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.StsService = void 0;
|
|
13
|
-
const common_1 = require("@nestjs/common");
|
|
14
|
-
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
15
|
-
const server_aws_sts_config_class_1 = require("../classes/server-aws-sts-config.class");
|
|
16
|
-
let StsService = class StsService {
|
|
17
|
-
stsClient;
|
|
18
|
-
config;
|
|
19
|
-
constructor(stsClient, config) {
|
|
20
|
-
this.stsClient = stsClient;
|
|
21
|
-
this.config = config;
|
|
22
|
-
}
|
|
23
|
-
async getAccountId() {
|
|
24
|
-
const response = await this.stsClient.send(new client_sts_1.GetCallerIdentityCommand({}));
|
|
25
|
-
return response.Account;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
exports.StsService = StsService;
|
|
29
|
-
exports.StsService = StsService = __decorate([
|
|
30
|
-
(0, common_1.Injectable)(),
|
|
31
|
-
__metadata("design:paramtypes", [client_sts_1.STSClient, server_aws_sts_config_class_1.ServerAwsStsConfig])
|
|
32
|
-
], StsService);
|
package/dist/esm/index.d.ts
DELETED
package/dist/esm/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./lib/classes/server-aws-sts-config.class"), exports);
|
|
18
|
-
__exportStar(require("./lib/services/sts.service"), exports);
|
|
19
|
-
__exportStar(require("./lib/server-aws-sts.module"), exports);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ServerAwsStsConfig = void 0;
|
|
4
|
-
class ServerAwsStsConfig {
|
|
5
|
-
AWS_ACCESS_KEY_ID;
|
|
6
|
-
AWS_REGION;
|
|
7
|
-
AWS_SECRET_ACCESS_KEY;
|
|
8
|
-
NODE_ENV;
|
|
9
|
-
}
|
|
10
|
-
exports.ServerAwsStsConfig = ServerAwsStsConfig;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var ServerAwsStsModule_1;
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.ServerAwsStsModule = void 0;
|
|
11
|
-
const common_1 = require("@nestjs/common");
|
|
12
|
-
const server_common_1 = require("@onivoro/server-common");
|
|
13
|
-
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
14
|
-
const sts_service_1 = require("./services/sts.service");
|
|
15
|
-
const server_aws_sts_config_class_1 = require("./classes/server-aws-sts-config.class");
|
|
16
|
-
let stsClient = null;
|
|
17
|
-
let ServerAwsStsModule = ServerAwsStsModule_1 = class ServerAwsStsModule {
|
|
18
|
-
static configure(config) {
|
|
19
|
-
return (0, server_common_1.moduleFactory)({
|
|
20
|
-
module: ServerAwsStsModule_1,
|
|
21
|
-
providers: [
|
|
22
|
-
{
|
|
23
|
-
provide: client_sts_1.STSClient,
|
|
24
|
-
useFactory: () => stsClient
|
|
25
|
-
? stsClient
|
|
26
|
-
: stsClient = new client_sts_1.STSClient({
|
|
27
|
-
region: config.AWS_REGION,
|
|
28
|
-
logger: console,
|
|
29
|
-
credentials: config.NODE_ENV === 'production'
|
|
30
|
-
? undefined
|
|
31
|
-
: {
|
|
32
|
-
accessKeyId: config.AWS_ACCESS_KEY_ID,
|
|
33
|
-
secretAccessKey: config.AWS_SECRET_ACCESS_KEY
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
},
|
|
37
|
-
{ provide: server_aws_sts_config_class_1.ServerAwsStsConfig, useValue: config },
|
|
38
|
-
sts_service_1.StsService
|
|
39
|
-
]
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
exports.ServerAwsStsModule = ServerAwsStsModule;
|
|
44
|
-
exports.ServerAwsStsModule = ServerAwsStsModule = ServerAwsStsModule_1 = __decorate([
|
|
45
|
-
(0, common_1.Module)({})
|
|
46
|
-
], ServerAwsStsModule);
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { STSClient } from '@aws-sdk/client-sts';
|
|
2
|
-
import { ServerAwsStsConfig } from '../classes/server-aws-sts-config.class';
|
|
3
|
-
export declare class StsService {
|
|
4
|
-
readonly stsClient: STSClient;
|
|
5
|
-
private config;
|
|
6
|
-
constructor(stsClient: STSClient, config: ServerAwsStsConfig);
|
|
7
|
-
getAccountId(): Promise<string>;
|
|
8
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.StsService = void 0;
|
|
13
|
-
const common_1 = require("@nestjs/common");
|
|
14
|
-
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
15
|
-
const server_aws_sts_config_class_1 = require("../classes/server-aws-sts-config.class");
|
|
16
|
-
let StsService = class StsService {
|
|
17
|
-
stsClient;
|
|
18
|
-
config;
|
|
19
|
-
constructor(stsClient, config) {
|
|
20
|
-
this.stsClient = stsClient;
|
|
21
|
-
this.config = config;
|
|
22
|
-
}
|
|
23
|
-
async getAccountId() {
|
|
24
|
-
const response = await this.stsClient.send(new client_sts_1.GetCallerIdentityCommand({}));
|
|
25
|
-
return response.Account;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
exports.StsService = StsService;
|
|
29
|
-
exports.StsService = StsService = __decorate([
|
|
30
|
-
(0, common_1.Injectable)(),
|
|
31
|
-
__metadata("design:paramtypes", [client_sts_1.STSClient, server_aws_sts_config_class_1.ServerAwsStsConfig])
|
|
32
|
-
], StsService);
|
package/dist/types/index.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { STSClient } from '@aws-sdk/client-sts';
|
|
2
|
-
import { ServerAwsStsConfig } from '../classes/server-aws-sts-config.class';
|
|
3
|
-
export declare class StsService {
|
|
4
|
-
readonly stsClient: STSClient;
|
|
5
|
-
private config;
|
|
6
|
-
constructor(stsClient: STSClient, config: ServerAwsStsConfig);
|
|
7
|
-
getAccountId(): Promise<string>;
|
|
8
|
-
}
|