Permalink
Browse files

Updating tutorial to use IAM as an authorizer (#111)

* Updating API chapters to use IAM

* IAM auth changes: Editing and moving identity pool chapter. Editing deploy chapter. Adding test API chapter.

* Adding chapter for connecting to API Gateway with IAM

* Editing sigv4 steps copy

* Editing Identity Pool IAM

* Editing IAM auth update
  • Loading branch information...
jayair committed Jul 19, 2017
1 parent 5514892 commit 717fcd89f33c6d7dd5ee5c2c2b5560136b115331
@@ -1,7 +1,7 @@
---
layout: post
title: Add a Create Note API
date: 2016-12-31 00:00:00
date: 2016-12-30 00:00:00
description: To allow users to create notes in our note taking app, we are going to add a create note POST API. To do this we are going to add a new Lambda function to our Serverless Framework project. The Lambda function will save the note to our DynamoDB table and return the newly created note. We also need to ensure to set the Access-Control headers to enable CORS for our serverless backend API.
context: backend
code: backend
@@ -30,14 +30,15 @@ export function main(event, context, callback) {
const params = {
TableName: 'notes',
// 'Item' contains the attributes of the item to be created
// - 'userId': because users are authenticated via Cognito User Pool, we
// will use the User Pool sub (a UUID) of the authenticated user
// - 'userId': user identities are federated through the
// Cognito Identity Pool, we will use the identity id
// as the user id of the authenticated user
// - 'noteId': a unique uuid
// - 'content': parsed from request body
// - 'attachment': parsed from request body
// - 'createdAt': current Unix timestamp
Item: {
userId: event.requestContext.authorizer.claims.sub,
userId: event.requestContext.identity.cognitoIdentityId,
noteId: uuid.v1(),
content: data.content,
attachment: data.attachment,
@@ -79,6 +80,7 @@ There are some helpful comments in the code but we are doing a few simple things
- We are setting the AWS JS SDK to use the region `us-east-1` while connecting to DynamoDB.
- If you have multiple profiles for your AWS SDK credentials, you will need to explicitly pick one. Add the following above the `AWS.config.update` line. `const credentials = new AWS.SharedIniFileCredentials({profile: 'my-profile'}); AWS.config.credentials = credentials;`
- Parse the input from the `event.body`. This represents the HTTP request parameters.
- The `userId` is a Federated Identity id that comes in as a part of the request. This is set after our user has been authenticated via the User Pool. We are going to expand more on where this id in the coming chapters when we set up our Cognito Identity Pool.
- Make a call to DynamoDB to put a new object with a generated `noteId` and the current date as the `createdAt`.
- Upon success, return the newly create note object with the HTTP status code `200` and response headers to enable **CORS (Cross-Origin Resource Sharing)**.
- And if the DynamoDB call fails then return an error with the HTTP status code `500`.
@@ -87,7 +89,7 @@ There are some helpful comments in the code but we are doing a few simple things

Now let's define the API endpoint for our function.

<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and replace it with the following. Replace `YOUR_USER_POOL_ARN` with the **Pool ARN** from the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter.
<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and replace it with the following.

``` yaml
service: notes-app-api
@@ -124,20 +126,18 @@ functions:
# - method: POST request
# - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
# domain api call
# - authorizer: authenticate the api via Cognito User Pool. Update the 'arn'
# with your own User Pool ARN
# - authorizer: authenticate using the AWS IAM role
create:
handler: create.main
events:
- http:
path: notes
method: post
cors: true
authorizer:
arn: YOUR_USER_POOL_ARN
authorizer: aws_iam
```

Here we are adding our newly added create function to the configuration. We specify that it handles `post` requests at the `/notes` endpoint. We set CORS support to true. This is because our frontend is going to be served from a different domain. We also specify that we want this API to authenticate via the Cognito User Pool that we had previously set up.
Here we are adding our newly added create function to the configuration. We specify that it handles `post` requests at the `/notes` endpoint. We set CORS support to true. This is because our frontend is going to be served from a different domain. As the authorizer we are going to restrict access to our API based on the user's IAM credentials. We will touch on this and how our User Pool works with this, in the Cognito Identity Pool chapter.

### Test

@@ -156,16 +156,14 @@ $ cd mocks
{
"body": "{\"content\":\"hello world\",\"attachment\":\"hello.jpg\"}",
"requestContext": {
"authorizer": {
"claims": {
"sub": "USER-SUB-1234"
}
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
```

You might have noticed that the `body` and `requestContext` fields are the ones we used in our create function. In this case the `sub` field is just a string we are going to use as our `userId`. We can use any string here; just make sure to use the same one when we test our other functions.
You might have noticed that the `body` and `requestContext` fields are the ones we used in our create function. In this case the `cognitoIdentityId` field is just a string we are going to use as our `userId`. We can use any string here; just make sure to use the same one when we test our other functions.

And to invoke our function we run the following in the root directory.

@@ -252,7 +250,7 @@ export async function main(event, context, callback) {
const params = {
TableName: 'notes',
Item: {
userId: event.requestContext.authorizer.claims.sub,
userId: event.requestContext.identity.cognitoIdentityId,
noteId: uuid.v1(),
content: data.content,
attachment: data.attachment,
@@ -1,8 +1,8 @@
---
layout: post
title: Add a Delete Note API
date: 2017-01-04 00:00:00
description: To allow users to delete their notes in our note taking app, we are going to add a DELETE note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will delete a user’s note in the DynamoDB table. We also need to ensure to set the Access-Control headers to enable CORS for our serverless backend API.
date: 2017-01-03 00:00:00
description: To allow users to delete their notes in our note taking app, we are going to add a DELETE note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will delete a user’s note in the DynamoDB table.
context: backend
code: backend
comments_id: 27
@@ -22,10 +22,10 @@ export async function main(event, context, callback) {
const params = {
TableName: 'notes',
// 'Key' defines the partition key and sort key of the item to be removed
// - 'userId': User Pool sub of the authenticated user
// - 'userId': Identity Pool identity id of the authenticated user
// - 'noteId': path parameter
Key: {
userId: event.requestContext.authorizer.claims.sub,
userId: event.requestContext.identity.cognitoIdentityId,
noteId: event.pathParameters.id,
},
};
@@ -44,7 +44,7 @@ This makes a DynamoDB `delete` call with the `userId` & `noteId` key to delete t

### Configure the API Endpoint

<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following to it. Replace `YOUR_USER_POOL_ARN` with the **Pool ARN** from the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter.
<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following to it.

``` yaml
delete:
@@ -57,8 +57,7 @@ This makes a DynamoDB `delete` call with the `userId` & `noteId` key to delete t
path: notes/{id}
method: delete
cors: true
authorizer:
arn: YOUR_USER_POOL_ARN
authorizer: aws_iam
```

This adds a DELETE request handler to the `/notes/{id}` endpoint.
@@ -75,10 +74,8 @@ Just like before we'll use the `noteId` of our note in place of the `id` in the
"id": "578eb840-f70f-11e6-9d1a-1359b3b22944"
},
"requestContext": {
"authorizer": {
"claims": {
"sub": "USER-SUB-1234"
}
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
@@ -1,8 +1,8 @@
---
layout: post
title: Add a Get Note API
date: 2017-01-01 00:00:00
description: To allow users to retrieve a note in our note taking app, we are going to add a GET note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve the note from our DynamoDB table. We also need to ensure to set the Access-Control headers to enable CORS for our serverless backend API.
date: 2016-12-31 00:00:00
description: To allow users to retrieve a note in our note taking app, we are going to add a GET note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve the note from our DynamoDB table.
context: backend
code: backend
comments_id: 24
@@ -22,10 +22,10 @@ export async function main(event, context, callback) {
const params = {
TableName: 'notes',
// 'Key' defines the partition key and sort key of the item to be retrieved
// - 'userId': federated identity ID of the authenticated user
// - 'userId': Identity Pool identity id of the authenticated user
// - 'noteId': path parameter
Key: {
userId: event.requestContext.authorizer.claims.sub,
userId: event.requestContext.identity.cognitoIdentityId,
noteId: event.pathParameters.id,
},
};
@@ -50,7 +50,7 @@ This follows exactly the same structure as our previous `create.js` function. Th

### Configure the API Endpoint

<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following to it. Replace `YOUR_USER_POOL_ARN` with the **Pool ARN** from the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter.
<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following to it.

``` yaml
get:
@@ -63,11 +63,10 @@ This follows exactly the same structure as our previous `create.js` function. Th
path: notes/{id}
method: get
cors: true
authorizer:
arn: YOUR_USER_POOL_ARN
authorizer: aws_iam
```

This defines our get note API. It adds a GET request handler with the endpoint `/notes/{id}`. And just as before we use our Cognito User Pool as the authorizer.
This defines our get note API. It adds a GET request handler with the endpoint `/notes/{id}`.

### Test

@@ -81,10 +80,8 @@ To test our get note API we need to mock passing in the `noteId` parameter. We a
"id": "578eb840-f70f-11e6-9d1a-1359b3b22944"
},
"requestContext": {
"authorizer": {
"claims": {
"sub": "USER-SUB-1234"
}
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
@@ -1,8 +1,8 @@
---
layout: post
title: Add a List All the Notes API
date: 2017-01-02 00:00:00
description: To allow users to retrieve their notes in our note taking app, we are going to add a list note GET API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve all the user’s notes from the DynamoDB table. We also need to ensure to set the Access-Control headers to enable CORS for our serverless backend API.
date: 2017-01-01 00:00:00
description: To allow users to retrieve their notes in our note taking app, we are going to add a list note GET API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve all the user’s notes from the DynamoDB table.
context: backend
code: backend
comments_id: 25
@@ -24,10 +24,10 @@ export async function main(event, context, callback) {
// 'KeyConditionExpression' defines the condition for the query
// - 'userId = :userId': only return items with matching 'userId' partition key
// 'ExpressionAttributeValues' defines the value in the condition
// - ':userId': defines 'userId' to be User Pool sub of the authenticated user
// - ':userId': defines 'userId' to be Identity Pool identity id of the authenticated user
KeyConditionExpression: "userId = :userId",
ExpressionAttributeValues: {
":userId": event.requestContext.authorizer.claims.sub,
":userId": event.requestContext.identity.cognitoIdentityId,
}
};
@@ -46,7 +46,7 @@ This is pretty much the same as our `get.js` except we only pass in the `userId`

### Configure the API Endpoint

<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following. Replace `YOUR_USER_POOL_ARN` with the **Pool ARN** from the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter.
<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following.

``` yaml
list:
@@ -59,11 +59,10 @@ This is pretty much the same as our `get.js` except we only pass in the `userId`
path: notes
method: get
cors: true
authorizer:
arn: YOUR_USER_POOL_ARN
authorizer: aws_iam
```

This defines the `/notes` endpoint that takes a GET request with the same Cognito User Pool authorizer.
This defines the `/notes` endpoint that takes a GET request.

### Test

@@ -72,10 +71,8 @@ This defines the `/notes` endpoint that takes a GET request with the same Cognit
``` json
{
"requestContext": {
"authorizer": {
"claims": {
"sub": "USER-SUB-1234"
}
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
@@ -1,8 +1,8 @@
---
layout: post
title: Add an Update Note API
date: 2017-01-03 00:00:00
description: To allow users to update their notes in our note taking app, we are going to add an update note PUT API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will update a user’s note in the DynamoDB table. We also need to ensure to set the Access-Control headers to enable CORS for our serverless backend API.
date: 2017-01-02 00:00:00
description: To allow users to update their notes in our note taking app, we are going to add an update note PUT API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will update a user’s note in the DynamoDB table.
context: backend
code: backend
comments_id: 26
@@ -23,10 +23,10 @@ export async function main(event, context, callback) {
const params = {
TableName: 'notes',
// 'Key' defines the partition key and sort key of the item to be updated
// - 'userId': User Pool sub of the authenticated user
// - 'userId': Identity Pool identity id of the authenticated user
// - 'noteId': path parameter
Key: {
userId: event.requestContext.authorizer.claims.sub,
userId: event.requestContext.identity.cognitoIdentityId,
noteId: event.pathParameters.id,
},
// 'UpdateExpression' defines the attributes to be updated
@@ -53,7 +53,7 @@ This should look similar to the `create.js` function. Here we make an `update` D

### Configure the API Endpoint

<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following to it. Replace `YOUR_USER_POOL_ARN` with the **Pool ARN** from the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter.
<img class="code-marker" src="{{ site.url }}/assets/s.png" />Open the `serverless.yml` file and append the following to it.

``` yaml
update:
@@ -66,8 +66,7 @@ This should look similar to the `create.js` function. Here we make an `update` D
path: notes/{id}
method: put
cors: true
authorizer:
arn: YOUR_USER_POOL_ARN
authorizer: aws_iam
```

Here we are adding a handler for the PUT request to the `/notes/{id}` endpoint.
@@ -85,10 +84,8 @@ Also, don't forget to use the `noteId` of the note we have been using in place o
"id": "578eb840-f70f-11e6-9d1a-1359b3b22944"
},
"requestContext": {
"authorizer": {
"claims": {
"sub": "USER-SUB-1234"
}
"identity": {
"cognitoIdentityId": "USER-SUB-1234"
}
}
}
@@ -1,7 +1,7 @@
---
layout: post
title: Add Support for ES6/ES7 JavaScript
date: 2016-12-30 12:00:00
date: 2016-12-29 12:00:00
redirect_from: /chapters/add-support-for-es6-javascript.html
description: AWS Lambda supports Node.js 6.10 and so to use async/await and other ES6/ES7 features in our Serverless Framework project we need to use Babel and Webpack to transpile our code. We can do this by adding the serverless-webpack plugin to our project and setting it up to automatically transpile our handler functions.
context: backend
Oops, something went wrong.

0 comments on commit 717fcd8

Please sign in to comment.