Repository navigation
Can I create a private endpoint in API Gateway while using HTTP API V2? #37139
Replies: 2 comments
|
HTTP API (V2) does support private integrations via VPC Links, but it works differently from REST API private endpoints. REST API (V1): You can create a truly private API using a VPC endpoint ( HTTP API (V2): There is no So if your goal is:
In CDK, a VPC Link for HTTP API V2 looks like: import { HttpApi, VpcLink } from "aws-cdk-lib/aws-apigatewayv2";
import { HttpAlbIntegration } from "aws-cdk-lib/aws-apigatewayv2-integrations";
const vpcLink = new VpcLink(this, "VpcLink", {
vpc,
subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
});
const httpApi = new HttpApi(this, "HttpApi");
httpApi.addRoutes({
path: "/api",
integration: new HttpAlbIntegration("AlbIntegration", listener, {
vpcLink,
}),
});For JWT authorizers (your original use case), HTTP API V2 is the right choice since REST API doesn't natively support JWT authorizers. You can combine the JWT authorizer with the VPC Link integration above. References: |
|
Yes, it is true: API Gateway HTTP APIs (v2) do not support Private Endpoints (ingress from inside a VPC via AWS PrivateLink). Here is the breakdown of why this happens, the distinction between private ingress vs. egress, and the recommended architectures to achieve your requirement (Private API + JWT validation). Ingress vs. Egress: The VPC Link DistinctionA common point of confusion is the term VPC Link:
Recommended SolutionsIf your core goal is to have a Private API Gateway within your VPC that validates JWT tokens, you have two main architecture patterns: Option 1: REST API (v1) with Private Endpoint + Lambda Token/Request Authorizer (Recommended for strict private networking)REST APIs support native import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
export class PrivateRestApiWithJwtStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: false });
// 1. Interface VPC Endpoint for execute-api
const apiVpce = new ec2.InterfaceVpcEndpoint(this, 'ApiGatewayVpce', {
vpc,
service: ec2.InterfaceVpcEndpointAwsService.APIGATEWAY,
subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
});
// 2. Lambda Authorizer validating JWT against IdP (Auth0, Cognito, Okta)
const jwtAuthorizerFn = new lambda.Function(this, 'JwtAuthorizerFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`
const { CognitoJwtVerifier } = require("aws-jwt-verify");
const verifier = CognitoJwtVerifier.create({
userPoolId: process.env.USER_POOL_ID,
tokenUse: "access",
clientId: process.env.CLIENT_ID,
});
exports.handler = async (event) => {
try {
const token = event.headers.Authorization?.replace('Bearer ', '');
const payload = await verifier.verify(token);
return {
principalId: payload.sub,
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: 'Allow',
Resource: event.methodArn,
}],
},
};
} catch (err) {
throw new Error('Unauthorized');
}
};
`),
environment: {
USER_POOL_ID: 'us-east-1_example',
CLIENT_ID: 'exampleclientid',
},
});
const authorizer = new apigateway.RequestAuthorizer(this, 'JwtAuthorizer', {
handler: jwtAuthorizerFn,
identitySources: [apigateway.IdentitySource.header('Authorization')],
resultsCacheTtl: cdk.Duration.minutes(5),
});
// 3. Private REST API restricted to the VPCE
const api = new apigateway.RestApi(this, 'PrivateRestApi', {
endpointConfiguration: {
types: [apigateway.EndpointType.PRIVATE],
vpcEndpoints: [apiVpce],
},
policy: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
principals: [new iam.AnyPrincipal()],
actions: ['execute-api:Invoke'],
resources: ['execute-api:/*'],
effect: iam.Effect.DENY,
conditions: {
StringNotEquals: {
'aws:sourceVpce': apiVpce.vpcEndpointId,
},
},
}),
new iam.PolicyStatement({
principals: [new iam.AnyPrincipal()],
actions: ['execute-api:Invoke'],
resources: ['execute-api:/*'],
effect: iam.Effect.ALLOW,
}),
],
}),
});
const items = api.root.addResource('items');
items.addMethod('GET', new apigateway.MockIntegration(), {
authorizer,
authorizationType: apigateway.AuthorizationType.CUSTOM,
});
}
}Benefits:
Option 2: HTTP API (v2) with Built-in JWT Authorizer + Perimeter Restrictions (AWS WAF / mTLS)If you specifically want the zero-code, low-cost native JWT Authorizer feature of HTTP API v2 (
|
Uh oh!
There was an error while loading. Please reload this page.
Currently I have a logging validation with tokens and wanted to introduce JWT Authorizers in API Gateway. However, this seems to require HTTP API V2. I was consider to migrate from REST V1 to it, but it seems V2 does not directly support a private endpoint and would require some workaround. Is this true? If so, is there any plan to have this?
All reactions