Skip to content

Commit 07c4a0d

Browse files
authored
feat(agentcore): agentcore gateway L2 construct (#35771)
### Issue # (if applicable) Related to aws/aws-cdk-rfcs#785 ### Reason for this change Adding bedrock agent core gateway and gateway target ### Description of changes - Added a new L2 construct for agentcore gateway - Added a new L2 construct for agentcore gateway target - Added validation helper methods, convenience method like addLambdaTarget, addOpenApiTarget, and other static methods. - Added integ and unit test cases ### Describe any new or updated permissions being added The gateway creates a role with permission to bedrock agentcore , s3 ### Description of how you validated changes Unit tests, integration tests, manual tests ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 8df9b06 commit 07c4a0d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+44951
-9
lines changed

packages/@aws-cdk/aws-bedrock-agentcore-alpha/README.md

Lines changed: 822 additions & 2 deletions
Large diffs are not rendered by default.

packages/@aws-cdk/aws-bedrock-agentcore-alpha/agentcore/gateway/gateway-base.ts

Lines changed: 400 additions & 0 deletions
Large diffs are not rendered by default.

packages/@aws-cdk/aws-bedrock-agentcore-alpha/agentcore/gateway/gateway.ts

Lines changed: 696 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { IUserPoolClient, IUserPool } from 'aws-cdk-lib/aws-cognito';
2+
import { ValidationError } from '../validation-helpers';
3+
4+
/******************************************************************************
5+
* Authorizer Configuration
6+
*****************************************************************************/
7+
8+
/**
9+
* Gateway authorizer type
10+
*/
11+
export enum GatewayAuthorizerType {
12+
/** Custom JWT authorizer type */
13+
CUSTOM_JWT = 'CUSTOM_JWT',
14+
/** AWS IAM authorizer type */
15+
AWS_IAM = 'AWS_IAM',
16+
}
17+
18+
/**
19+
* Abstract interface for gateway authorizer configuration
20+
*/
21+
export interface IGatewayAuthorizerConfig {
22+
/**
23+
* The authorizer type
24+
*/
25+
readonly authorizerType: GatewayAuthorizerType;
26+
27+
/**
28+
* The authorizer configuration in CFN format
29+
* @internal
30+
*/
31+
_render(): any;
32+
}
33+
34+
/******************************************************************************
35+
* Custom JWT
36+
*****************************************************************************/
37+
/**
38+
* Custom JWT authorizer configuration
39+
*/
40+
export interface CustomJwtConfiguration {
41+
/**
42+
* This URL is used to fetch OpenID Connect configuration or authorization server metadata
43+
* for validating incoming tokens.
44+
*
45+
* Pattern: .+/\.well-known/openid-configuration
46+
* Required: Yes
47+
*/
48+
readonly discoveryUrl: string;
49+
50+
/**
51+
* Represents individual audience values that are validated in the incoming JWT token validation process.
52+
* @default - No audience validation
53+
*/
54+
readonly allowedAudience?: string[];
55+
56+
/**
57+
* Represents individual client IDs that are validated in the incoming JWT token validation process.
58+
* @default - No client ID validation
59+
*/
60+
readonly allowedClients?: string[];
61+
}
62+
63+
/**
64+
* Custom JWT authorizer configuration implementation
65+
*/
66+
export class CustomJwtAuthorizer implements IGatewayAuthorizerConfig {
67+
public readonly authorizerType = GatewayAuthorizerType.CUSTOM_JWT;
68+
private readonly discoveryUrl: string;
69+
private readonly allowedAudience?: string[];
70+
private readonly allowedClients?: string[];
71+
72+
constructor(config: CustomJwtConfiguration) {
73+
this.discoveryUrl = config.discoveryUrl;
74+
this.allowedAudience = config.allowedAudience;
75+
this.allowedClients = config.allowedClients;
76+
}
77+
78+
/**
79+
* @internal
80+
*/
81+
public _render(): any {
82+
return {
83+
customJwtAuthorizer: {
84+
discoveryUrl: this.discoveryUrl,
85+
...(this.allowedAudience && { allowedAudience: this.allowedAudience }),
86+
...(this.allowedClients && { allowedClients: this.allowedClients }),
87+
},
88+
};
89+
}
90+
}
91+
92+
/******************************************************************************
93+
* AWS IAM
94+
*****************************************************************************/
95+
96+
/**
97+
* AWS IAM authorizer configuration implementation
98+
*
99+
*/
100+
export class IamAuthorizer implements IGatewayAuthorizerConfig {
101+
public readonly authorizerType = GatewayAuthorizerType.AWS_IAM;
102+
103+
/**
104+
* @internal
105+
*/
106+
_render(): any {
107+
// AWS IAM authorizer doesn't need additional configuration
108+
// Return null or undefined to indicate no configuration needed
109+
return undefined;
110+
}
111+
}
112+
113+
/******************************************************************************
114+
* Factory
115+
*****************************************************************************/
116+
117+
export interface CognitoAuthorizerProps {
118+
/**
119+
* The Cognito User Pool to use for authentication
120+
*/
121+
readonly userPool: IUserPool;
122+
/**
123+
* The allowed User Pool clients
124+
* @default - All clients are allowed
125+
*/
126+
readonly allowedClients?: IUserPoolClient[];
127+
/**
128+
* The allowed audiences for JWT validation
129+
* @default - No audience validation
130+
*/
131+
readonly allowedAudiences?: string[];
132+
}
133+
/**
134+
* Factory class for creating Gateway Authorizers
135+
*/
136+
export abstract class GatewayAuthorizer {
137+
/**
138+
* AWS IAM authorizer instance
139+
*/
140+
public static usingAwsIam(): IGatewayAuthorizerConfig {
141+
return new IamAuthorizer();
142+
}
143+
144+
/**
145+
* Create a custom JWT authorizer
146+
* @param configuration - The JWT configuration
147+
* @returns IGatewayAuthorizerConfig configured for custom JWT
148+
*/
149+
public static usingCustomJwt(configuration: CustomJwtConfiguration): IGatewayAuthorizerConfig {
150+
// At least one of allowedAudience or allowedClients must be defined for CUSTOM_JWT authorizer
151+
if (!configuration.allowedAudience && !configuration.allowedClients) {
152+
throw new ValidationError('At least one of allowedAudience or allowedClients must be defined for CUSTOM_JWT authorizer');
153+
}
154+
return new CustomJwtAuthorizer(configuration);
155+
}
156+
157+
/**
158+
* Create a JWT authorizer from Cognito User Pool
159+
* @param props - The Cognito configuration
160+
* @returns CustomJwtAuthorizer configured for Cognito
161+
*/
162+
public static usingCognito(props: CognitoAuthorizerProps): IGatewayAuthorizerConfig {
163+
const discoveryUrl = `https://cognito-idp.${props.userPool.env.region}.amazonaws.com/${props.userPool.userPoolId}/.well-known/openid-configuration`;
164+
165+
return new CustomJwtAuthorizer({
166+
discoveryUrl: discoveryUrl,
167+
allowedClients: props.allowedClients?.flatMap((client) => client.userPoolClientId),
168+
allowedAudience: props.allowedAudiences,
169+
});
170+
}
171+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { Grant, IRole, PolicyStatement } from 'aws-cdk-lib/aws-iam';
2+
import { CredentialProviderType, ICredentialProviderConfig } from './credential-provider';
3+
import { GatewayPerms } from '../perms';
4+
5+
/******************************************************************************
6+
* API KEY
7+
*****************************************************************************/
8+
/**
9+
* API Key additional configuration
10+
*/
11+
export interface ApiKeyAdditionalConfiguration {
12+
13+
/**
14+
* The name of the credential parameter for the API key.
15+
* This parameter name is used when sending the API key to the target endpoint.
16+
*
17+
* Length Constraints: Minimum length of 1. Maximum length of 64.
18+
* @default - 'Authorization' for HEADER, 'api_key' for QUERY_PARAMETER
19+
*/
20+
readonly credentialParameterName?: string;
21+
22+
/**
23+
* The prefix for the API key credential.
24+
* This prefix is added to the API key when sending it to the target endpoint.
25+
*
26+
* Length Constraints: Minimum length of 1. Maximum length of 64.
27+
* @default - 'Bearer ' for HEADER, no prefix for QUERY_PARAMETER
28+
*/
29+
readonly credentialPrefix?: string;
30+
}
31+
32+
/**
33+
* API Key credential location type
34+
* @internal
35+
*/
36+
export enum ApiKeyCredentialLocationType {
37+
HEADER = 'HEADER',
38+
QUERY_PARAMETER = 'QUERY_PARAMETER',
39+
}
40+
41+
/**
42+
* API Key location within the request
43+
*/
44+
export class ApiKeyCredentialLocation {
45+
/**
46+
* Create a header-based API key credential location
47+
* @param config - Optional configuration for the credential location
48+
* @returns ApiKeyCredentialLocation configured for header placement
49+
*/
50+
public static header(config?: ApiKeyAdditionalConfiguration) {
51+
return new ApiKeyCredentialLocation(
52+
ApiKeyCredentialLocationType.HEADER,
53+
config?.credentialParameterName ?? 'Authorization',
54+
config?.credentialPrefix ?? 'Bearer ',
55+
);
56+
}
57+
58+
/**
59+
* Create a query parameter-based API key credential location
60+
* @param config - Optional configuration for the credential location
61+
* @returns ApiKeyCredentialLocation configured for query parameter placement
62+
*/
63+
public static queryParameter(config?: ApiKeyAdditionalConfiguration) {
64+
return new ApiKeyCredentialLocation(
65+
ApiKeyCredentialLocationType.QUERY_PARAMETER,
66+
config?.credentialParameterName ?? 'api_key',
67+
config?.credentialPrefix,
68+
);
69+
}
70+
71+
/**
72+
* The name of the credential parameter
73+
*/
74+
public readonly credentialParameterName: string;
75+
/**
76+
* The prefix for the credential value
77+
*/
78+
public readonly credentialPrefix?: string;
79+
/**
80+
* The type of credential location (HEADER or QUERY_PARAMETER)
81+
*/
82+
public readonly credentialLocationType: string;
83+
84+
private constructor(
85+
credentialLocationType: string,
86+
credentialParameterName: string,
87+
credentialPrefix?: string,
88+
) {
89+
this.credentialLocationType = credentialLocationType;
90+
this.credentialParameterName = credentialParameterName;
91+
this.credentialPrefix = credentialPrefix;
92+
}
93+
}
94+
95+
/**
96+
* API Key configuration
97+
*/
98+
export interface ApiKeyCredentialProviderProps {
99+
/**
100+
* The API key credential provider ARN.
101+
* This is returned when creating the API key credential provider via Console or API.
102+
* Format: arn:aws:bedrock-agentcore:region:account:token-vault/id/apikeycredentialprovider/name
103+
*/
104+
readonly providerArn: string;
105+
106+
/**
107+
* The ARN of the Secrets Manager secret containing the API key.
108+
* This is returned when creating the API key credential provider via Console or API.
109+
* Format: arn:aws:secretsmanager:region:account:secret:name
110+
*/
111+
readonly secretArn: string;
112+
113+
/**
114+
* The location of the API key credential.
115+
* This field specifies where in the request the API key should be placed.
116+
*
117+
* @default - HEADER
118+
*/
119+
readonly credentialLocation?: ApiKeyCredentialLocation;
120+
}
121+
122+
/**
123+
* API Key credential provider configuration implementation
124+
* Can be used with OpenAPI targets
125+
* @internal
126+
*/
127+
export class ApiKeyCredentialProviderConfiguration implements ICredentialProviderConfig {
128+
public readonly credentialProviderType = CredentialProviderType.API_KEY;
129+
/**
130+
* The ARN of the API key provider
131+
*/
132+
public readonly providerArn: string;
133+
/**
134+
* The ARN of the Secrets Manager secret
135+
*/
136+
public readonly secretArn: string;
137+
/**
138+
* The location configuration for the API key credential
139+
*/
140+
public readonly credentialLocation: ApiKeyCredentialLocation;
141+
142+
constructor(configuration: ApiKeyCredentialProviderProps) {
143+
this.providerArn = configuration.providerArn;
144+
this.secretArn = configuration.secretArn;
145+
this.credentialLocation = configuration.credentialLocation ?? ApiKeyCredentialLocation.header();
146+
}
147+
148+
/**
149+
* Grant the needed permissions to the role for API key authentication
150+
*/
151+
grantNeededPermissionsToRole(role: IRole): Grant | undefined {
152+
const statements = [
153+
new PolicyStatement({
154+
actions: [
155+
...GatewayPerms.GATEWAY_API_KEY_PERMS,
156+
...GatewayPerms.GATEWAY_WORKLOAD_IDENTITY_PERMS,
157+
],
158+
resources: [this.providerArn],
159+
}),
160+
new PolicyStatement({
161+
actions: GatewayPerms.SECRETS_PERMS,
162+
resources: [this.secretArn],
163+
}),
164+
];
165+
166+
return Grant.addToPrincipal({
167+
grantee: role,
168+
actions: statements.flatMap(s => s.actions),
169+
resourceArns: statements.flatMap(s => s.resources),
170+
});
171+
}
172+
173+
/**
174+
* @internal
175+
*/
176+
_render(): any {
177+
return {
178+
credentialProviderType: this.credentialProviderType,
179+
credentialProvider: {
180+
apiKeyCredentialProvider: {
181+
providerArn: this.providerArn,
182+
credentialLocation: this.credentialLocation.credentialLocationType,
183+
credentialParameterName: this.credentialLocation.credentialParameterName,
184+
credentialPrefix: this.credentialLocation.credentialPrefix,
185+
},
186+
},
187+
};
188+
}
189+
}

0 commit comments

Comments
 (0)