forked from aws/agentcore-cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathaccount.ts
More file actions
92 lines (82 loc) · 3.52 KB
/
Copy pathaccount.ts
File metadata and controls
92 lines (82 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { getAwsLoginGuidance } from '../external-requirements/checks';
import { GetCallerIdentityCommand, STSClient } from '@aws-sdk/client-sts';
import { fromEnv, fromNodeProviderChain } from '@aws-sdk/credential-providers';
import type { AwsCredentialIdentityProvider } from '@smithy/types';
/**
* Get the AWS credential provider to use for SDK clients.
* Prioritizes environment variables when set, otherwise uses the full provider chain.
* This ensures proper credential resolution without requiring ~/.aws directory.
*/
export function getCredentialProvider(): AwsCredentialIdentityProvider {
const hasEnvCreds = process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY;
return hasEnvCreds ? fromEnv() : fromNodeProviderChain();
}
/**
* Error thrown when AWS credentials are not configured or invalid.
* Supports both a short message (for interactive mode) and detailed message (for CLI mode).
*/
export class AwsCredentialsError extends Error {
/** Short message suitable for interactive mode where UI handles recovery */
readonly shortMessage: string;
constructor(shortMessage: string, detailedMessage?: string) {
super(detailedMessage ?? shortMessage);
this.name = 'AwsCredentialsError';
this.shortMessage = shortMessage;
}
}
/**
* Get AWS account ID using STS GetCallerIdentity with detailed error handling.
* Throws AwsCredentialsError with helpful messages for common credential issues.
* Returns null only for unexpected errors (triggers generic "no credentials" message).
*/
export async function detectAccount(): Promise<string | null> {
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? 'us-east-1';
try {
const client = new STSClient({
credentials: getCredentialProvider(),
region,
});
const response = await client.send(new GetCallerIdentityCommand({}));
return response.Account ?? null;
} catch (err) {
const code = (err as { name?: string })?.name ?? (err as { Code?: string })?.Code;
if (code === 'ExpiredTokenException' || code === 'ExpiredToken') {
const guidance = await getAwsLoginGuidance();
throw new AwsCredentialsError(
'AWS credentials expired.',
`AWS credentials expired.\n\nTo fix this:\n ${guidance}`
);
}
if (code === 'InvalidClientTokenId' || code === 'SignatureDoesNotMatch') {
const guidance = await getAwsLoginGuidance();
throw new AwsCredentialsError(
'AWS credentials are invalid.',
`AWS credentials are invalid.\n\nTo fix this:\n 1. Check your AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY\n 2. Or ${guidance}`
);
}
if (code === 'AccessDenied' || code === 'AccessDeniedException') {
throw new AwsCredentialsError(
'AWS credentials lack required permissions.',
'AWS credentials lack required permissions for STS:GetCallerIdentity.\n\nTo fix this:\n Ensure your IAM user/role has sts:GetCallerIdentity permission'
);
}
return null;
}
}
/**
* Validate that AWS credentials are configured and working.
* Throws AwsCredentialsError with a helpful message if not.
*/
export async function validateAwsCredentials(): Promise<void> {
const account = await detectAccount();
if (!account) {
const guidance = await getAwsLoginGuidance();
throw new AwsCredentialsError(
'No AWS credentials configured.',
'No AWS credentials configured.\n\n' +
'To fix this:\n' +
` 1. ${guidance}\n` +
' 2. Or set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables'
);
}
}