can a sns.Topic.fromTopicArn be used to run a CodePipeline - javascript

I'm new to aws and my task is to rebuild the app (trigger the codepipeline) when we receive an sns message.
looking for something similar to the code below but not on a schedule instead using sns but i dont think i can use an sns event:
// A pipeline being used as a target for a CloudWatch event rule.
import * as targets from '#aws-cdk/aws-events-targets';
import * as events from '#aws-cdk/aws-events';
// kick off the pipeline every day
const rule = new events.Rule(this, 'Daily', {
schedule: events.Schedule.rate(Duration.days(1)),
});
declare const pipeline: codepipeline.Pipeline;
rule.addTarget(new targets.CodePipeline(pipeline));
these are the code fragments i collected but i dont think i can do what i want to do using a lambda function either.
const consumerTopic = sns.Topic.fromTopicArn(
this,
"myTopicId",
"arn:aws:sns:*******");
const fn = new Function(this, 'aFunction', {
runtime: Runtime.NODEJS_16_X,
handler: 'snsHandler.handler',
code: Code.fromAsset(__dirname),
});
consumerTopic.addSubscription(new LambdaSubscription(fn))

You can trigger a pipeline when you receive a sns message like this:
// create sns topic
const topic = new sns.Topic(this, 'MyTopic');
// or subscribe to an existing sns topic
const consumerTopic = sns.Topic.fromTopicArn(
this,
"myTopicId",
"arn:aws:sns:***YOUR ARN***");
const fn = new Function(this, 'aFunction', {
description: "lambda function to trigger a code pipeline build from a sns message",
runtime: Runtime.NODEJS_16_X,
handler: 'snsHandler.handler',
code: Code.fromAsset(join(__dirname, '../lambda')),
});
consumerTopic.addSubscription(new LambdaSubscription(fn))
// 👇 create a policy statement
const policy = new iam.PolicyStatement({
actions: ['codepipeline:StartPipelineExecution'],
resources: ['*'],
});
// 👇 add the policy to the Function's role
fn.role?.attachInlinePolicy(
new iam.Policy(this, 'run-pipeline-policy', {
statements: [policy],
}),
);
// ***** set a message:
// aws sns publish \
// --subject "Just testing 🚀" \
// --message "Hello world 🐊" \
// --topic-arn "***YOUR ARN***"
and the lambda function:
import { SNSMessage } from 'aws-lambda';
import {AWSError} from "aws-sdk";
import {StartPipelineExecutionOutput} from "aws-sdk/clients/codepipeline";
const AWS = require('aws-sdk');
exports.handler = async function(event:SNSMessage) {
const codePipeline = new AWS.CodePipeline({region: "ap-southeast-2" });
const params = {
name: '***NAME OF CODE PIPELINE**', /* required */
// clientRequestToken: 'STRING_VALUE'
};
return new Promise ((resolve, reject) => {
codePipeline.startPipelineExecution(params, function(err:AWSError, data:StartPipelineExecutionOutput) {
if (err) {
console.log(err, err.stack);
reject(err)
} // an error occurred
else {
console.log(data);
resolve(data)
} // successful response
});
})
};
one more example here:
AccessDeniedException while starting pipeline

Related

A metric with the name has already been registered | prometheus custom metric in nodejs app

Getting metric has already been registered when trying to publish metrics from service. To avoid that I used register.removeSingleMetric("newMetric"); but problem is that it clear register and past records every time a new call comes in.
Wondering what to address this problem:
export function workController(req: Request, res: Response) {
resolveFeaturePromises(featurePromises).then(featureAggregate => {
return rulesService.runWorkingRulesForHook(featureAggregate, hook.name, headers)
.then(([shouldLog, result]) => {
...
// publish metric
publishHookMetrics(result);
// publish metric
sendResponse(res, new CoreResponse(result.isWorking, result.responseData), req, featureAggregate, hook);
});
}).catch(err => {
sendResponse(res, new CoreResponse(Action.ALLOW, null), req, null, hook);
console.error(err);
});
}
function publishCustomMetrics(customInfoObject: CustomInfoObject) {
const counter = new promClient.Counter({
name: "newMetric",
help: "metric for custom detail",
labelNames: ["name", "isWorking"]
});
counter.inc({
name: customInfoObject.hook,
isWorking: customInfoObject.isWorking
});
}
stack trace
[Node] [2020-07-30T17:40:09+0500] [ERROR] Error: A metric with the name newMetric has already been registered.
application.ts
export async function startWebServer(): Promise<Server> {
if (!isGlobalsInitilized) {
throw new Error("Globals are noit initilized. Run initGlobals() first.");
}
// Setup prom express middleware
const metricsMiddleware = promBundle({
includeMethod: true,
includePath: true,
metricsPath: "/prometheus",
promClient: {
collectDefaultMetrics: {
}
}
});
// start http server
const app = require("express")();
app.use(bodyParser.json());
app.use(metricsMiddleware);
const routeConfig = require("./config/route-config");
routeConfig.configure(app);
const port = process.env.PORT || 3000;
return app.listen(port, function () {
console.log("service listening on port", port);
});
}
versions:
express-prom-bundle: 6.0.0
prom-client: 12.0.0
The counter should be initialize only once, then used for each call. Simplest modification from code you gave would be something like this.
const counter = new promClient.Counter({
name: "newMetric",
help: "metric for custom detail",
labelNames: ["name", "isWorking"]
});
export function publishCustomMetrics(customInfoObject: CustomInfoObject) {
counter.inc({
name: customInfoObject.hook,
isWorking: customInfoObject.isWorking
});
}
BTW, I found this thread while searching a way to avoid the "already been registered" error in unit tests. Many tests was instantiating the same class (from a library) that was initializing a counter in the constructor.
As it is in tests, and I didn't need consistency over the metrics, I found an easy solution is to clear metrics registers at the beginning of each test.
import { register } from "prom-client";
// ...
register.clear();

Key element does not match the schema with DocumentClient

I have a Lambda that I have created using the AWS Toolkit for Visual Studio Code. I have a small lambda.js file that exports the handler for API Gateway events.
const App = require('./app');
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
let config = {
aws_region: 'us-east-1',
//endpoint: 'http://localhost:8000',
};
let dynamo = new AWS.DynamoDB.DocumentClient();
let app = new App(dynamo, config, event);
try {
let response = await app.run();
return response;
} catch(err) {
return err;
}
};
The app.js file represents the logic of my Lambda. This is broken up to improve testing.
const AWS = require('aws-sdk');
class App {
constructor(datastore, configuration, event) {
this.datastore = datastore;
this.httpEvent = event;
this.configuration = configuration;
}
async run() {
AWS.config.update({
region: this.configuration.aws_region,
endpoint: this.configuration.endpoint,
});
let params = {
TableName: 'test',
Key: {
'year': 2015,
'title': 'The Big New Movie',
},
};
const getRequest = this.datastore.get(params);
// EXCEPTION THROWN HERE
var result = await getRequest.promise();
let body = {
location: this.configuration.aws_region,
url: this.configuration.endpoint,
data: result
};
return {
'statusCode': 200,
'body': JSON.stringify(body)
};
}
}
module.exports = App;
I can write the following mocha test and get it to pass.
'use strict';
const AWS = require('aws-sdk');
const chai = require('chai');
const sinon = require('sinon');
const App = require('../app');
const expect = chai.expect;
var event, context;
const dependencies = {
// sinon.stub() prevents calls to DynamoDB and allows for faking of methods.
dynamo: sinon.stub(new AWS.DynamoDB.DocumentClient()),
configuration: {
aws_region: 'us-west-2',
endpoint: 'http://localhost:5000',
},
};
describe('Tests handler', function () {
// Reset test doubles for isolating individual test cases
this.afterEach(sinon.reset);
it('verifies successful response', async () => {
dependencies.dynamo.get.returns({ promise: sinon.fake.resolves('foo bar')});
let app = new App(dependencies.dynamo, dependencies.configuration, event);
const result = await app.run()
expect(result).to.be.an('object');
expect(result.statusCode).to.equal(200);
expect(result.body).to.be.an('string');
console.log(result.body);
let response = JSON.parse(result.body);
expect(response).to.be.an('object');
expect(response.location).to.be.equal(dependencies.configuration.aws_region);
expect(response.url).to.be.equal(dependencies.configuration.endpoint);
expect(response.data).to.be.equal('foo bar');
});
});
However, when I run the Lambda locally using the Debug Locally via the Code Lens option in VS Code the results from the AWS.DynamoDB.DocumentClient.get call throws an exception.
{
"message":"The provided key element does not match the schema",
"code":"ValidationException",
...
"statusCode":400,
"retryable":false,
"retryDelay":8.354173589804192
}
I have a table created in the us-east-1 region where the non-test code is configured to go to. I've confirmed the http endpoint being hit by the DocumentClient as being dynamodb.us-east-1.amazonaws.com. The table name is correct and I have a hash key called year and a sort key called title. Why would this not find the key correctly? I pulled this example from the AWS documentation, created a table to mirror what the key was and have not had any luck.
The issue is that the Key year was provided a number value while the table was expecting it to be a string.
Wrapping the 2015 in quotes let the Document know that this was a string and it could match to the schema in Dynamo.
let params = {
TableName: 'test',
Key: {
'year': '2015',
'title': 'The Big New Movie',
},
};

Streaming video from AWS Kinesis to JS client

I'm trying to consume the video from an AWS Kinesis stream. The stream is visible in the AWS console, but I cannot consume it in the JS application I'm trying to create.
I've been following this tutorial, but cannot get the streaming URL.
My code is here:
import React, { Component} from 'react'
import ReactPlayer from 'react-player'
import AWS from "aws-sdk";
import { STREAM_NAME, ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION } from '../secrets'
var streamName = STREAM_NAME;
// Step 1: Configure SDK Clients
var options = {
accessKeyId: ACCESS_KEY_ID,
secretAccessKey: SECRET_ACCESS_KEY,
region: REGION
}
var kinesisVideo = new AWS.KinesisVideo(options);
var kinesisVideoArchivedContent = new AWS.KinesisVideoArchivedMedia(options);
// Step 2: Get a data endpoint for the stream
kinesisVideo.getDataEndpoint({
StreamName: streamName,
APIName: "GET_HLS_STREAMING_SESSION_URL"
}, function(err, response) {
if (err) { return console.error(err); }
console.log('Data endpoint: ' + response.DataEndpoint);
kinesisVideoArchivedContent.endpoint = new AWS.Endpoint(response.DataEndpoint);
});
// Step 3: Get an HLS Streaming Session URL
console.log('Fetching HLS Streaming Session URL');
var playbackMode = 'LIVE'; // 'LIVE' or 'ON_DEMAND'
//var startTimestamp = new Date('START_TIMESTAMP'); // For ON_DEMAND only
//var endTimestamp = new Date('END_TIMESTAMP'); // For ON_DEMAND only
var fragmentSelectorType = 'SERVER_TIMESTAMP'; // 'SERVER_TIMESTAMP' or 'PRODUCER_TIMESTAMP'
const SESSION_EXPIRATION_SECONDS = 60*60
console.log(kinesisVideo)
const hlsUrl = kinesisVideoArchivedContent.getHLSStreamingSessionURL({
StreamName: streamName,
//StreamARN: "arn:aws:kinesisvideo:us-east-1:635420739373:stream/mr-pinchers-dot-org/1561848963391",
PlaybackMode: playbackMode,
HLSFragmentSelector: {
FragmentSelectorType: fragmentSelectorType,
TimestampRange: playbackMode === 'LIVE' ? undefined : {
// StartTimestamp: startTimestamp,
// EndTimestamp: endTimestamp
}
},
Expires: parseInt(SESSION_EXPIRATION_SECONDS)
}, function(err, response) {
if (err) { return console.error("Darn", err); }
console.log('HLS Streaming Session URL: ' + response.HLSStreamingSessionURL, response);
}
)
console.log("here", hlsUrl)
class Home extends Component {
render () {
return <ReactPlayer url={hlsUrl} playing={true} />
}
}
export default Home
The response I'm getting in Step 3 (response.HLSStreamingSessionURL) is undefined.
Step 2 runs fine, and I get an endpoint back, so I'm confident that it's not a permissions problem.
Part of me thinks that I should be using some async/await calls but I'm not sure, still pretty new to JS and all that async stuff so didn't know how to incorporate it into this.
I've spent quite a bit of time trying to figure this out but the documentation on Kinesis is still pretty light, although if someone has a good resource for it, please let me know.
This is basic JavaScript async behavior. You're executing step 3 before step 2 is complete. You can't use the response before it's happened.
You can fix this by starting step 3 when step 2 has completed, as follows:
kinesisVideo.getDataEndpoint({
StreamName: streamName,
APIName: "GET_HLS_STREAMING_SESSION_URL"
}, function(err, response) {
if (err) { return console.error(err); }
console.log('Data endpoint: ' + response.DataEndpoint);
kinesisVideoArchivedContent.endpoint = new AWS.Endpoint(response.DataEndpoint);
var playbackMode = 'LIVE';
var fragmentSelectorType = 'SERVER_TIMESTAMP';
const SESSION_EXPIRATION_SECONDS = 60*60
kinesisVideoArchivedContent.getHLSStreamingSessionURL({...});
// remainder of code here
});
Or you can use async/await and promise variants of the AWS SDK methods like so:
(async () => {
const kv_response = await kv.getDataEndpoint({...}).promise();
// ...
const hls_response = await kvac.getHLSStreamingSessionURL({...}).promise();
})();
Note that await may only be used inside an async function, hence the anonymous async wrapper.

By using ledger nano s, I wanna sign a transaction and send it

I'm trying to send ethereum transaction that sends ERC20 tokens to someone with Ledger Nano S through Node.JS but I'm not able to successfully sign and send this transaction.
First of all, I signed the transaction through the method, signTransaction, of ledgerhq API and then after signing it, I sended it to the main net by using sendSignedTransaction. When I execute below code, Ledger receives request and shows details of a transaction. However, after pressing Ledger's confirm button, the console returns error 'Returned error: Invalid signature: Crypto error (Invalid EC signature)'.
import AppEth from "#ledgerhq/hw-app-eth";
import TransportU2F from "#ledgerhq/hw-transport-u2f";
import TransportNodeHid from "#ledgerhq/hw-transport-node-hid";
import EthereumTx from "ethereumjs-tx"
const Web3 = require('web3');
import { addHexPrefix, bufferToHex, toBuffer } from 'ethereumjs-util';
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var destAddresses = ['0xa6acFa18468786473269Dc1521fd4ff40F6481D9'];
var amount = 1000000000000;
var i=0;
var contract = new web3.eth.Contract([token contract ABI... ], '0x74a...');
const data1 = contract.methods.transfer(destAddresses[0], amount).encodeABI();
const exParams = {
gasLimit: 6e6,
gasPrice: 3e9,
from: '0x1A...',
data : data1,
to: '0x74a...',
value: '0x00',
nonce: "0x0",
chainId: 1,
v: "0x01",
r: "0x00",
s: "0x00"
}
async function makeSign(txParams) {
const tx = new EthereumTx(txParams);
const txHex = tx.serialize().toString("hex");
const signedTransaction = '0x' + txHex;
let transport;
try {
transport = await TransportNodeHid.create();
let eth2 = new AppEth(transport);
const result = await eth2.signTransaction("m/44'/60'/0'/0", txHex).then(result => {
web3.eth.sendSignedTransaction('0x' + txHex)
.then(res => {
console.log(res);
}).catch(err => {
console.log('sendSignedTransaction');
console.log(err);
});
}).catch(err => {
console.log('signTransaction');
console.log(err);
});
txParams.r = `0x${result.r, 'hex'}`;
txParams.s = `0x${result.s, 'hex'}`;
txParams.v = `0x${result.v, 'hex'}`;
return result;
} catch (e) {
console.log(e);
}
}
makeSign(exParams).then(function () {
console.log("Promise Resolved2");
}.catch(function () {
console.log("Promise Rejected2");
});
When I only use signTransaction function, I can confirm the transaction in the ledger device and return txhash on the console. However, ultimately I want to broadcast a transaction to the main net. Could you please give me any idea? I want any feedback. Also, if there are any examples of creating and broadcasting a raw transaction by using the ledger, notice me please.
Your code already sends the transaction to the network. However, just awaiting the "send" promise only gives you the transaction hash, not the receipt. You need to treat it as an event emitter and wait for the 'confirmation' event.
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction(serializedTx.toString('hex'))
.once('transactionHash', hash => console.log('Tx hash', hash))
.on('confirmation', (confNumber, receipt) => {
console.log(`Confirmation #${confNumber}`, receipt);
})
.on('error', console.error);
To send it to mainnet as you mention, you can either run a local geth node on port 8545 and use your code unchanged, or point web3 at infura or similar.

AWS CDK user pool authorizer

I'm trying to create an API gateway using the AWS-CDK and protect the REST endpoints with a Cognito user pool authorizer.
I cannot find any examples how one would do this. I thought it should look something like this but maybe the methods I need do not exist?
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());
As of September 2019 #bgdnip answer doesnt translate exactly for typescript. I got it working with the following:
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 });
This is from https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_resource_props
UPDATE October
The above is already out of date and unnecessary and can be achieved with the following with 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 }
});
The previous answers no longer work because the authorizerId property was replaced with authorizer, which isn't fully implemented at this time.
Instead, it can be done by using the underlying CfnResource objects, as described in the official guide.
Here's Python code as an example:
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()
This is my solution in TypeScript (based somewhat on bgdnlp's response)
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 });
Indeed. there is no example to do this via copy and paste ;). here is my example to create AWS cognito user pool and connect user pol authorizer with API gateway and lambda function using AWS CDK based on Java with Version 0.24.1.
This example ist just an example to provide an protected API for function called "Foo".
Cognito User Pool
API Gateway
Lambda
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());
I figured out what looks like a mechanism... I was able to get it to work like this:
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
});
Now to figure out how to enable CORS headers on API Gateway...
You have to:
create the api gateway
set Cognito as authorizer in the api gateway
set the authorization in your method
set your integration with the lambda to 'Use Lambda Proxy integration'. The LambdaIntegration properties has on true this value by default, so don't worry for it
Finally, make a request adding the token in the Header. The API gateway will validate it with Cognito. If this pass then, your lambda will be triggered and in the event you can find the claims 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
);
For the weirdos using the Java version of the CDK (like me), you can utilize the setters on the Cfn constructs:
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(cognitoAuthorizer.getRef());
As of v1.88 this is now supported directly in 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,
});
AWS CDK API V1 Reference
AWS CDK API V2 Reference
If you are currently using CDK v2 or CDK v1(latest). This has been made easy:
// 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
});

Categories

Resources