Авторизатор пула пользователей AWS CDK
Я пытаюсь создать шлюз API с помощью AWS-CDK и защитить конечные точки REST с помощью авторизатора пула пользователей Cognito.
Я не могу найти никаких примеров, как это можно сделать. Я думал, что это должно выглядеть примерно так, но, может быть, методы, которые мне нужны, не существуют?
const cdk = require('@aws-cdk/cdk');
const lambda = require('@aws-cdk/aws-lambda');
const apigw = require('@aws-cdk/aws-apigateway');
const path = require('path');
//
// Define the stack:
class MyStack extends cdk.Stack {
constructor (parent, id, props) {
super(parent, id, props);
var tmethodHandler = new lambda.Function(this, 'test-lambda', {
runtime: lambda.Runtime.NodeJS810,
handler: 'index.handler',
code: lambda.Code.directory( path.join( __dirname, 'lambda')),
});
var api = new apigw.RestApi(this, 'test-api');
const tmethod = api.root.addResource('testmethod');
const tmethodIntegration = new apigw.LambdaIntegration(tmethodHandler);
tmethod.addMethod('GET', getSessionIntegration, {
authorizationType: apigw.AuthorizationType.Cognito,
authorizerId : 'crap!!!?'
});
}
}
class MyApp extends cdk.App {
constructor (argv) {
super(argv);
new MyStack(this, 'test-apigw');
}
}
console.log(new MyApp(process.argv).run());
9 ответов
Я понял, что выглядит как механизм... Я смог заставить его работать так:
var auth = new apigw.cloudformation.AuthorizerResource(this, 'myAuthorizer', {
restApiId: api.restApiId,
authorizerName: 'mypoolauth',
authorizerResultTtlInSeconds: 300,
identitySource: 'method.request.header.Authorization',
providerArns: [ 'arn:aws:cognito-idp:us-west-2:redacted:userpool/redacted' ],
type: "COGNITO_USER_POOLS"
});
tmethod.addMethod('GET', getSessionIntegration, {
authorizationType: apigw.AuthorizationType.Cognito,
authorizerId : auth.authorizerId
});
Теперь, чтобы выяснить, как включить заголовки CORS на API-шлюзе...
По состоянию на September 2019
@bgdnip ответ не переводится точно для typescript
. У меня это работает со следующим:
const api = new RestApi(this, 'RestAPI', {
restApiName: 'Rest-Name',
description: 'API for journey services.',
});
const putIntegration = new LambdaIntegration(handler);
const auth = new CfnAuthorizer(this, 'APIGatewayAuthorizer', {
name: 'customer-authorizer',
identitySource: 'method.request.header.Authorization',
providerArns: [providerArn.valueAsString],
restApiId: api.restApiId,
type: AuthorizationType.COGNITO,
});
const post = api.root.addMethod('PUT', putIntegration, { authorizationType: AuthorizationType.COGNITO });
const postMethod = post.node.defaultChild as CfnMethod;
postMethod.addOverride('Properties.AuthorizerId', { Ref: auth.logicalId });
Это с https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html
ОБНОВЛЕНИЕ Октябрь
Вышеуказанное уже устарело и ненужно и может быть достигнуто с помощью следующих aws-cdk 1.12.0
const api = new RestApi(this, 'RestAPI', {
restApiName: 'Rest-Name',
description: 'API for journey services.',
});
const putIntegration = new LambdaIntegration(handler);
const auth = new CfnAuthorizer(this, 'APIGatewayAuthorizer', {
name: 'customer-authorizer',
identitySource: 'method.request.header.Authorization',
providerArns: [providerArn.valueAsString],
restApiId: api.restApiId,
type: AuthorizationType.COGNITO,
});
const post = api.root.addMethod('PUT', putIntegration, {
authorizationType: AuthorizationType.COGNITO,
authorizer: { authorizerId: auth.ref }
});
Это мое решение в TypeScript (основано на ответе bgdnlp)
import { App, Stack, Aws } from '@aws-cdk/core';
import { Code, Function, Runtime } from '@aws-cdk/aws-lambda';
import { LambdaIntegration, RestApi, CfnAuthorizer, CfnMethod } from '@aws-cdk/aws-apigateway';
const app = new App();
const stack = new Stack(app, `mystack`);
const api = new RestApi(stack, `myapi`);
const region = Aws.REGION;
const account = Aws.ACCOUNT_ID;
const cognitoArn = `arn:aws:cognito-idp:${region}:${account}:userpool/${USER_POOL_ID}`;
const authorizer = new CfnAuthorizer(stack, 'Authorizer', {
name: `myauthorizer`,
restApiId: api.restApiId,
type: 'COGNITO_USER_POOLS',
identitySource: 'method.request.header.Authorization',
providerArns: [cognitoArn],
});
const lambda = new Function(stack, 'mylambda', {
runtime: Runtime.NODEJS_10_X,
code: Code.asset('dist'),
handler: `index.handler`,
});
const integration = new LambdaIntegration(lambda);
const res = api.root.addResource('hello');
const method = res.addMethod('GET', integration);
const child = method.node.findChild('Resource') as CfnMethod;
child.addPropertyOverride('AuthorizationType', 'COGNITO_USER_POOLS');
child.addPropertyOverride('AuthorizerId', { Ref: authorizer.logicalId });
Предыдущие ответы больше не работают, потому что authorizerId
собственность была заменена на authorizer
, который не полностью реализован в настоящее время.
Вместо этого это можно сделать с помощью базовых объектов CfnResource, как описано в официальном руководстве.
Вот код Python в качестве примера:
from aws_cdk import cdk
from aws_cdk import aws_apigateway
class Stk(cdk.Stack):
def __init__(self, app, id):
super().__init__(app, id)
api_gw = aws_apigateway.RestApi(self, 'MyApp')
post_method = api_gw.root.add_method(http_method='POST')
# Create authorizer using low level CfnResource
api_gw_authorizer = aws_apigateway.CfnAuthorizer(
scope=self,
id='my_authorizer',
rest_api_id=api_gw.rest_api_id,
name='MyAuth',
type='COGNITO_USER_POOLS',
identity_source='method.request.header.name.Authorization',
provider_arns=[
'arn:aws:cognito-idp:eu-west-1:123456789012:userpool/'
'eu-west-1_MyCognito'])
# Get underlying post_method Resource object. Returns CfnMethod
post_method_resource = post_method.node.find_child('Resource')
# Add properties to low level resource
post_method_resource.add_property_override('AuthorizationType',
'COGNITO_USER_POOLS')
# AuthorizedId uses Ref, simulate with a dictionaty
post_method_resource.add_property_override(
'AuthorizerId',
{"Ref": api_gw_authorizer.logical_id})
app = cdk.App()
stk = Stk(app, "myStack")
app.synth()
В самом деле. нет примера сделать это с помощью копирования и вставки;). Вот мой пример создания пула пользователей AWS cognito и подключения авторизатора пользовательских политик к API-шлюзу и лямбда-функции с использованием AWS CDK на основе Java версии 0.24.1.
Этот пример является просто примером для предоставления защищенного API для функции под названием "Foo".
- Cognito User Pool
- API-шлюз
- лямбда
DynamoDB
// ----------------------------------------------------------------------- // Cognito User Pool // ----------------------------------------------------------------------- CfnUserPool userPool = new CfnUserPool(this, "cognito", CfnUserPoolProps.builder() .withAdminCreateUserConfig( AdminCreateUserConfigProperty.builder() .withAllowAdminCreateUserOnly(false) .build()) .withPolicies( PoliciesProperty.builder() .withPasswordPolicy( PasswordPolicyProperty.builder() .withMinimumLength(6) .withRequireLowercase(false) .withRequireNumbers(false) .withRequireSymbols(false) .withRequireUppercase(false) .build() ) .build() ) .withAutoVerifiedAttributes(Arrays.asList("email")) .withSchema(Arrays.asList( CfnUserPool.SchemaAttributeProperty.builder() .withAttributeDataType("String") .withName("email") .withRequired(true) .build())) .build()); // ----------------------------------------------------------------------- // Cognito User Pool Client // ----------------------------------------------------------------------- new CfnUserPoolClient(this, "cognitoClient", CfnUserPoolClientProps.builder() .withClientName("UserPool") .withExplicitAuthFlows(Arrays.asList("ADMIN_NO_SRP_AUTH")) .withRefreshTokenValidity(90) .withUserPoolId(userPool.getRef()) .build()); // ----------------------------------------------------------------------- // Lambda function // ----------------------------------------------------------------------- Function function = new Function(this, "function.foo", FunctionProps.builder() // lamda code located in /functions/foo .withCode(Code.asset("functions/foo")) .withHandler("index.handler") .withRuntime(Runtime.NODE_J_S810) .build()); // ----------------------------------------------------------------------- // DynamoDB Table // ----------------------------------------------------------------------- Table table = new Table(this, "dynamodb.foo", TableProps.builder() .withTableName("foo") .withPartitionKey(Attribute.builder() .withName("id") .withType(AttributeType.String) .build()) .build()); // GRANTS function -> table table.grantReadWriteData(function.getRole()); // ----------------------------------------------------------------------- // API Gateway // ----------------------------------------------------------------------- // API Gateway REST API with lambda integration LambdaIntegration lambdaIntegration = new LambdaIntegration(function); RestApi restApi = new RestApi(this, "foo"); // Authorizer configured with cognito user pool CfnAuthorizer authorizer = new CfnAuthorizer(this, "authorizer", CfnAuthorizerProps.builder() .withName("cognitoAuthorizer") .withRestApiId(restApi.getRestApiId()) .withIdentitySource("method.request.header.Authorization") .withProviderArns(Arrays.asList(userPool.getUserPoolArn())) .withType("COGNITO_USER_POOLS") .build()); // Bind authorizer to API ressource restApi.getRoot().addMethod("ANY", lambdaIntegration, MethodOptions .builder() .withAuthorizationType(AuthorizationType.Cognito) .withAuthorizerId(authorizer.getAuthorizerId()) .build());
Ты должен:
- создать шлюз API
- установить Cognito в качестве авторизатора в шлюзе API
- установите авторизацию в вашем методе
- установите для своей интеграции с лямбда-выражением значение "Использовать интеграцию с лямбда-прокси". В свойствах LambdaIntegration по умолчанию установлено значение true, поэтому не беспокойтесь об этом.
Наконец, сделайте запрос, добавив токен в заголовок. Шлюз API проверит его с помощью Cognito. Если это пройдет, ваша лямбда будет активирована, и в случае, если вы найдете претензии event.requestContext.authorizer.claims.
const lambda = require("@aws-cdk/aws-lambda");
const apiGateway = require('@aws-cdk/aws-apigateway');
const api = new apiGateway.RestApi(
this,
'<id-ApiGateway>',
{
restApiName: '<ApiGateway-name>',
},
);
const auth = new apiGateway.CfnAuthorizer(this, '<id>', {
name: "<authorizer-name>",
type: apiGateway.AuthorizationType.COGNITO,
authorizerResultTtlInSeconds: 300,
identitySource: "method.request.header.Authorization",
restApiId: api.restApiId,
providerArns: ['<userPool.userPoolArn>'],
});
const myLambda= new lambda.Function(this, "<id>", {
functionName: '<lambda-name>',
runtime: lambda.Runtime.NODEJS_10_X,
handler: "<your-handler>",
code: lambda.Code.fromAsset("<path>"), // TODO: modify the way to get the path
});
const lambdaIntegration = new apiGateway.LambdaIntegration(myLambda);
const resource = api.root.resourceForPath('<your-api-path>');
// When the API will be deployed, the URL will look like this
// https://xxxxxx.execute-api.us-east-2.amazonaws.com/dev/<your-api-path>
const authorizationOptions = {
apiKeyRequired: false,
authorizer: {authorizerId: auth.ref},
authorizationType: 'COGNITO_USER_POOLS'
};
resource.addMethod(
GET, // your method
lambdaIntegration,
authorizationOptions
);
Для чудаков, использующих Java-версию CDK (например, я), вы можете использовать сеттеры в конструкциях Cfn:
final UserPool userPool = ...
final RestApi restApi = ...
final LambdaIntegration integration = ...
final Method method = restApi.getRoot().addMethod("GET", integration);
final CfnAuthorizer cognitoAuthorizer = new CfnAuthorizer(this, "CfnCognitoAuthorizer",
CfnAuthorizerProps.builder()
.name("CognitoAuthorizer")
.restApiId(restApi.getRestApiId())
.type("COGNITO_USER_POOLS")
.providerArns(Arrays.asList(userPool.getUserPoolArn()))
.identitySource("method.request.header.Authorization")
.build());
final CfnMethod cfnMethod = (CfnMethod) method.getNode().getDefaultChild();
cfnMethod.setAuthorizationType("COGNITO_USER_POOLS");
cfnMethod.setAuthorizerId(authorizer.getRef());
Начиная с версии 1.88 это теперь поддерживается непосредственно в CDK.
const userPool = new cognito.UserPool(this, 'UserPool');
const auth = new apigateway.CognitoUserPoolsAuthorizer(this, 'booksAuthorizer', {
cognitoUserPools: [userPool]
});
declare const books: apigateway.Resource;
books.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com'), {
authorizer: auth,
authorizationType: apigateway.AuthorizationType.COGNITO,
});
Если вы в настоящее время используете CDK v2 или CDK v1(последняя). Это было сделано легко:
// Your cognito userpool
const userPool = new cognito.UserPool(this, "MyUserPool")
// Authorizer for your userpool
const cognitoAuthorizer = new apigw.CognitoUserPoolsAuthorizer(
this,
"CognitoAuthorierOnLambda",
{
cognitoUserPools: [userPool],
}
);
// Rest Api
const api = new apigw.RestApi(this, 'myApi', {
defaultCorsPreflightOptions: {
allowOrigins: apigw.Cors.ALL_ORIGINS,
allowMethods: apigw.Cors.ALL_METHODS, // this is also the default
},
deploy: true
});
// add /getItems endpoint
const getItems = api.root.addResource('getItems');
// attach lambda to this endpoint
const getItemsIntegration = new apigw.LambdaIntegration(getItemsLambdaFunction);
// make the endpoint secure with cognito userpool
getItems.addMethod('GET', getAllItemsIntegration, {
authorizer:cognitoAuthorizer
});