-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat(agentcore): agentcore gateway L2 construct #35771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 69 commits
9605217
356c42d
b124520
cfad025
a4cd8b7
910cf05
7a306af
778e5b0
bae6962
e301efd
150b222
ea39263
a4a2ccf
0062054
d20e5a3
4b87383
8b227b8
f48b7bf
3c058da
13ffd1d
a74054f
7f541d5
2e18add
2bd3112
f724588
bab3f14
028bbaa
8f1e9bb
7a69a05
3ac39ab
59548d1
400f950
78e18c1
4ed2f90
aeabbb7
ee3a551
d0f8593
a1d0028
0be30ac
7422eb5
220c92a
cf415e0
b9307cf
f307d28
48b815e
8025c75
5fd0dae
f162585
0bccf0e
2e586c7
07d6e91
f1955ba
4701e39
08f1095
08d9a42
4cd4d17
9e329f7
c5dcc8e
eba7935
3494acb
ec8526f
61600d9
6cc7adc
34e308c
7ffebdf
ab59ad0
53e4f05
68744a3
039c4aa
fae1b1a
5ef288d
f5defab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| import { IUserPoolClient, IUserPool } from 'aws-cdk-lib/aws-cognito'; | ||
| import { ValidationError } from '../validation-helpers'; | ||
|
|
||
| /****************************************************************************** | ||
| * Authorizer Configuration | ||
| *****************************************************************************/ | ||
|
|
||
| /** | ||
| * Gateway authorizer type | ||
| */ | ||
| export enum GatewayAuthorizerType { | ||
| /** Custom JWT authorizer type */ | ||
| CUSTOM_JWT = 'CUSTOM_JWT', | ||
| /** AWS IAM authorizer type */ | ||
| AWS_IAM = 'AWS_IAM', | ||
| } | ||
|
|
||
| /** | ||
| * Abstract interface for gateway authorizer configuration | ||
| */ | ||
| export interface IGatewayAuthorizerConfig { | ||
| /** | ||
| * The authorizer type | ||
| */ | ||
| readonly authorizerType: GatewayAuthorizerType; | ||
|
|
||
| /** | ||
| * The authorizer configuration in CFN format | ||
| * @internal | ||
| */ | ||
| _render(): any; | ||
| } | ||
|
|
||
| /****************************************************************************** | ||
| * Custom JWT | ||
| *****************************************************************************/ | ||
| /** | ||
| * Custom JWT authorizer configuration | ||
| */ | ||
| export interface CustomJwtConfiguration { | ||
| /** | ||
| * This URL is used to fetch OpenID Connect configuration or authorization server metadata | ||
| * for validating incoming tokens. | ||
| * | ||
| * Pattern: .+/\.well-known/openid-configuration | ||
| * Required: Yes | ||
| */ | ||
| readonly discoveryUrl: string; | ||
|
|
||
| /** | ||
| * Represents individual audience values that are validated in the incoming JWT token validation process. * | ||
| * @default - No audience validation | ||
| */ | ||
| readonly allowedAudience?: string[]; | ||
|
|
||
| /** | ||
| * Represents individual client IDs that are validated in the incoming JWT token validation process. * | ||
| * @default - No client ID validation | ||
| */ | ||
| readonly allowedClients?: string[]; | ||
| } | ||
|
|
||
| /** | ||
| * Custom JWT authorizer configuration implementation | ||
| */ | ||
| export class CustomJwtAuthorizer implements IGatewayAuthorizerConfig { | ||
| /** | ||
| * Create a JWT authorizer from Cognito User Pool | ||
| * @param props - The Cognito configuration | ||
| * @returns CustomJwtAuthorizer configured for Cognito | ||
| */ | ||
| public static fromCognito(props: CognitoAuthorizerProps) { | ||
| // Construct the discovery URL from the User Pool properties | ||
| const discoveryUrl = `https://cognito-idp.${props.userPool.env.region}.amazonaws.com/${props.userPool.userPoolId}/.well-known/openid-configuration`; | ||
|
|
||
| return new CustomJwtAuthorizer({ | ||
| discoveryUrl: discoveryUrl, | ||
| allowedClients: props.allowedClients?.flatMap((client) => client.userPoolClientId), | ||
| allowedAudience: props.allowedAudiences, | ||
| }); | ||
| } | ||
|
|
||
| public readonly authorizerType = GatewayAuthorizerType.CUSTOM_JWT; | ||
| private readonly discoveryUrl: string; | ||
| private readonly allowedAudience?: string[]; | ||
| private readonly allowedClients?: string[]; | ||
|
|
||
| constructor(config: CustomJwtConfiguration) { | ||
| this.discoveryUrl = config.discoveryUrl; | ||
| this.allowedAudience = config.allowedAudience; | ||
| this.allowedClients = config.allowedClients; | ||
| } | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| public _render(): any { | ||
| return { | ||
| customJwtAuthorizer: { | ||
| discoveryUrl: this.discoveryUrl, | ||
| ...(this.allowedAudience && { allowedAudience: this.allowedAudience }), | ||
| ...(this.allowedClients && { allowedClients: this.allowedClients }), | ||
| }, | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| /****************************************************************************** | ||
| * AWS IAM | ||
| *****************************************************************************/ | ||
|
|
||
| /** | ||
| * AWS IAM authorizer configuration implementation | ||
| * | ||
| */ | ||
| export class IamAuthorizer implements IGatewayAuthorizerConfig { | ||
| public readonly authorizerType = GatewayAuthorizerType.AWS_IAM; | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| _render(): any { | ||
| // AWS IAM authorizer doesn't need additional configuration | ||
| // Return null or undefined to indicate no configuration needed | ||
| return undefined; | ||
| } | ||
| } | ||
|
|
||
| /****************************************************************************** | ||
| * Factory | ||
| *****************************************************************************/ | ||
|
|
||
| export interface CognitoAuthorizerProps { | ||
| /** | ||
| * The Cognito User Pool to use for authentication | ||
| */ | ||
| readonly userPool: IUserPool; | ||
| /** | ||
| * The allowed User Pool clients | ||
| * @default - All clients are allowed | ||
| */ | ||
| readonly allowedClients?: IUserPoolClient[]; | ||
| /** | ||
| * The allowed audiences for JWT validation | ||
| * @default - No audience validation | ||
| */ | ||
| readonly allowedAudiences?: string[]; | ||
| } | ||
| /** | ||
| * Factory class for creating Gateway Authorizers | ||
| */ | ||
| export abstract class GatewayAuthorizer { | ||
| /** | ||
| * AWS IAM authorizer instance | ||
| */ | ||
| public static awsIam = new IamAuthorizer(); | ||
|
|
||
| /** | ||
| * Create a custom JWT authorizer | ||
| * @param configuration - The JWT configuration | ||
| * @returns IGatewayAuthorizerConfig configured for custom JWT | ||
| */ | ||
| public static usingCustomJwt(configuration: CustomJwtConfiguration): IGatewayAuthorizerConfig { | ||
| // At least one of allowedAudience or allowedClients must be defined for CUSTOM_JWT authorizer | ||
| if (!configuration.allowedAudience && !configuration.allowedClients) { | ||
| throw new ValidationError('At least one of allowedAudience or allowedClients must be defined for CUSTOM_JWT authorizer'); | ||
| } | ||
| return new CustomJwtAuthorizer(configuration); | ||
| } | ||
|
|
||
| /** | ||
| * Static method for creating a Cognito authorizer | ||
| */ | ||
| public static usingCognito(props: CognitoAuthorizerProps): IGatewayAuthorizerConfig { | ||
| return CustomJwtAuthorizer.fromCognito(props); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| import { Grant, IRole, PolicyStatement } from 'aws-cdk-lib/aws-iam'; | ||
| import { CredentialProviderType, ICredentialProvider } from './credential-provider'; | ||
| import { GatewayPerms } from '../perms'; | ||
|
|
||
| /****************************************************************************** | ||
| * API KEY | ||
| *****************************************************************************/ | ||
| /** | ||
| * API Key additional configuration | ||
| */ | ||
| export interface ApiKeyAdditionalConfiguration { | ||
|
|
||
| /** | ||
| * The name of the credential parameter for the API key. | ||
| * This parameter name is used when sending the API key to the target endpoint. | ||
| * | ||
| * Length Constraints: Minimum length of 1. Maximum length of 64. | ||
| * @default - 'Authorization' for HEADER, 'api_key' for QUERY_PARAMETER | ||
| */ | ||
| readonly credentialParameterName?: string; | ||
|
|
||
| /** | ||
| * The prefix for the API key credential. | ||
| * This prefix is added to the API key when sending it to the target endpoint. | ||
| * | ||
| * Length Constraints: Minimum length of 1. Maximum length of 64. | ||
| * @default - 'Bearer ' for HEADER, no prefix for QUERY_PARAMETER | ||
| */ | ||
| readonly credentialPrefix?: string; | ||
| } | ||
|
|
||
| /** | ||
| * API Key credential location type | ||
| * @internal | ||
| */ | ||
| export enum ApiKeyCredentialLocationType { | ||
| HEADER = 'HEADER', | ||
| QUERY_PARAMETER = 'QUERY_PARAMETER', | ||
| } | ||
|
|
||
| /** | ||
| * API Key location within the request | ||
| */ | ||
| export class ApiKeyCredentialLocation { | ||
| /** | ||
| * Create a header-based API key credential location | ||
| * @param config - Optional configuration for the credential location | ||
| * @returns ApiKeyCredentialLocation configured for header placement | ||
| */ | ||
| public static header(config?: ApiKeyAdditionalConfiguration) { | ||
| return new ApiKeyCredentialLocation( | ||
| ApiKeyCredentialLocationType.HEADER, | ||
| config?.credentialParameterName ?? 'Authorization', | ||
| config?.credentialPrefix ?? 'Bearer ', | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Create a query parameter-based API key credential location | ||
| * @param config - Optional configuration for the credential location | ||
| * @returns ApiKeyCredentialLocation configured for query parameter placement | ||
| */ | ||
| public static queryParameter(config?: ApiKeyAdditionalConfiguration) { | ||
| return new ApiKeyCredentialLocation( | ||
| ApiKeyCredentialLocationType.QUERY_PARAMETER, | ||
| config?.credentialParameterName ?? 'api_key', | ||
| config?.credentialPrefix, | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * The name of the credential parameter | ||
| */ | ||
| public readonly credentialParameterName: string; | ||
| /** | ||
| * The prefix for the credential value | ||
| */ | ||
| public readonly credentialPrefix?: string; | ||
| /** | ||
| * The type of credential location (HEADER or QUERY_PARAMETER) | ||
| */ | ||
| public readonly credentialLocationType: string; | ||
|
|
||
| private constructor( | ||
| credentialLocationType: string, | ||
| credentialParameterName: string, | ||
| credentialPrefix?: string, | ||
| ) { | ||
| this.credentialLocationType = credentialLocationType; | ||
| this.credentialParameterName = credentialParameterName; | ||
| this.credentialPrefix = credentialPrefix; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * API Key configuration | ||
| */ | ||
| export interface ApiKeyCredentialProviderProps { | ||
| /** | ||
| * The API key credential provider ARN. | ||
| * This is returned when creating the API key credential provider via Console or API. | ||
| * Format: arn:aws:bedrock-agentcore:region:account:token-vault/id/apikeycredentialprovider/name | ||
| */ | ||
| readonly providerArn: string; | ||
|
|
||
| /** | ||
| * The ARN of the Secrets Manager secret containing the API key. | ||
| * This is returned when creating the API key credential provider via Console or API. | ||
| * Format: arn:aws:secretsmanager:region:account:secret:name | ||
| */ | ||
| readonly secretArn: string; | ||
|
|
||
| /** | ||
| * The location of the API key credential. | ||
| * This field specifies where in the request the API key should be placed. | ||
| * | ||
| * @default - HEADER | ||
| */ | ||
| readonly credentialLocation?: ApiKeyCredentialLocation; | ||
| } | ||
|
|
||
| /** | ||
| * API Key credential provider configuration implementation | ||
| * Can be used with OpenAPI targets | ||
| */ | ||
| export class ApiKeyCredentialProviderConfiguration implements ICredentialProvider { | ||
|
||
| public readonly credentialProviderType = CredentialProviderType.API_KEY; | ||
| /** | ||
| * The ARN of the API key provider | ||
| */ | ||
| public readonly providerArn: string; | ||
| /** | ||
| * The ARN of the Secrets Manager secret | ||
| */ | ||
| public readonly secretArn: string; | ||
| /** | ||
| * The location configuration for the API key credential | ||
| */ | ||
| public readonly credentialLocation: ApiKeyCredentialLocation; | ||
|
|
||
| constructor(configuration: ApiKeyCredentialProviderProps) { | ||
| this.providerArn = configuration.providerArn; | ||
| this.secretArn = configuration.secretArn; | ||
| this.credentialLocation = configuration.credentialLocation ?? ApiKeyCredentialLocation.header(); | ||
| } | ||
|
|
||
| /** | ||
| * Grant the needed permissions to the role for API key authentication | ||
| */ | ||
| grantNeededPermissionsToRole(role: IRole): Grant | undefined { | ||
| const statements = [ | ||
| new PolicyStatement({ | ||
| actions: [ | ||
| ...GatewayPerms.GATEWAY_API_KEY_PERMS, | ||
| ...GatewayPerms.GATEWAY_WORKLOAD_IDENTITY_PERMS, | ||
| ], | ||
| resources: [this.providerArn], | ||
| }), | ||
| new PolicyStatement({ | ||
| actions: GatewayPerms.SECRETS_PERMS, | ||
| resources: [this.secretArn], | ||
| }), | ||
| ]; | ||
|
|
||
| return Grant.addToPrincipal({ | ||
| grantee: role, | ||
| actions: statements.flatMap(s => s.actions), | ||
| resourceArns: statements.flatMap(s => s.resources), | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| _render(): any { | ||
| return { | ||
| credentialProviderType: this.credentialProviderType, | ||
| credentialProvider: { | ||
| apiKeyCredentialProvider: { | ||
| providerArn: this.providerArn, | ||
| credentialLocation: this.credentialLocation.credentialLocationType, | ||
| credentialParameterName: this.credentialLocation.credentialParameterName, | ||
| credentialPrefix: this.credentialLocation.credentialPrefix, | ||
| }, | ||
| }, | ||
| }; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think we need to redirect call from one public static function to another. We can directly use the this one and shift the logic here to ensure consistency in code design
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack. Didn't updated the old code. removed
fromCognitoand directly usingusingCognito