This example demonstrates how to setup an Authorized RESTful Web Services allowing you to create, and List Todos. DynamoDB is used to store the data, and the Cognito client credentials flow is used to secure the API. This is just an example and of course you could use any data storage as a backend.
This service has a separate directory for all the quiz question operations. For each operation exactly one file exists e.g. app/create.js
. In each of these files there is exactly one function which is directly attached to module.exports
.
The idea behind the app
directory is that in case you want to create a service containing multiple resources e.g. users, notes, comments you could do so in the same service. While this is certainly possible you might consider creating a separate service for each resource. It depends on the use-case and your preference.
npm install
-
User Pool : A user pool is collections of users. The users can be federated, can be manually set up, or imported. User Pools are the foundational entity in Cognito. You may compare this to a typical AD or LDAP directory.
-
Identity Pools : An identity pool allows access to AWS services via federated or custom identity. This way we do not have to manage a separate directory of users who need to access the dev AWS account.
-
Domain : A Domain is tied to a user pool in a 1:1 relationship, and is used to host the signup/login/challenge pages for the auth experience for the application.
-
App Client : A User Pool can have multiple app clients. App Clients are also where we set up OAuth2 grant types. This is similar to OAuth2 clients that can access resources using various grant types. The app client also has a list of associated scopes that it may allow requests for. These scopes are declared by the Resource Server(s) in the User Pool.
-
Resource Server : A resource server is where the users’ data resides, and is protected by the configured User Pool. There can be multiple resource servers associated with a single User Pool. Think of a Resource Server as a microservice which handles authenticated requests. By the time the request makes it to the Resource Server, it has an access token which contains information about the authenticated user, and the session. The resource server(s) verify the authenticity and validity of the access token they receive. A resource server has an identifier (usually the URL of the service), and a list of scopes. Scopes are the granular level levels of access - like read, write, admin, etc.
-
JWT : Cognito access tokens are JWT, which are signed with JWK. The JWT contains standard claims, but can also be extended to contain custom claims.
- This tutorial leverages the Cognito IDP API.
- We will add our own resource servers and app clients to a previously created Cognito User Pool (code provided in Step 1)
- Be sure to replace to update the USERNAME variable in each of these scripts
#!/usr/bin/env bash
ID=
USERNAME=
aws cognito-idp create-resource-server \
--region us-east-1 \
--user-pool-id ${ID} \
--identifier "quizResourceServer" \
--name "Quiz Application Resource Server" \
--scopes "ScopeName=questions.read,ScopeDescription=Get all questions" "ScopeName=questions.write,ScopeDescription=Create question"
#!/bin/sh
ID=
aws cognito-idp create-user-pool-client \
--region us-east-1 \
--user-pool-id ${ID} \
--client-name "quiznessAppClient" \
--generate-secret \
--refresh-token-validity 1 \
--read-attributes '[ "address","birthdate","email","email_verified","family_name","gender","given_name","locale","middle_name","name","nickname","phone_number","phone_number_verified","picture","preferred_username","profile","updated_at","website","zoneinfo"]' \
--write-attributes '[ "address","birthdate","email","family_name","gender","given_name","locale","middle_name","name","nickname","phone_number","picture","preferred_username","profile","updated_at","website","zoneinfo"]' \
--allowed-o-auth-flows "client_credentials" \
--allowed-o-auth-scopes "quizResourceServer/questions.read" "quizResourceServer/questions.write" \
--supported-identity-providers "COGNITO"
We will use this token as the Authorization header in our curl commands.
Notice that we are using the scopes that we create d on the resource server in
## Deploy
In order to deploy the endpoint simply run the [Publish Lambdas Script](/scripts/07_publishLambdas.sh)<br/>
Make sure you have logged in to AWS via single-sign-on.
```bash
#!/usr/bin/env bash
cd..
SLS_DEBUG=* serverless deploy --stage dev
The expected result should be similar to:
Serverless: Packaging service…
Serverless: Uploading CloudFormation file to S3…
Serverless: Uploading service .zip file to S3…
Serverless: Updating Stack…
Serverless: Checking Stack update progress…
Serverless: Stack update finished…
Service Information
service: serverless-rest-api-with-dynamodb
stage: dev
region: us-east-1
api keys:
None
endpoints:
POST - https://45wf34z5yf.execute-api.us-east-1.amazonaws.com/dev/questions
GET - https://45wf34z5yf.execute-api.us-east-1.amazonaws.com/dev/questions
GET - https://45wf34z5yf.execute-api.us-east-1.amazonaws.com/dev/questions/{id}
PUT - https://45wf34z5yf.execute-api.us-east-1.amazonaws.com/dev/questions/{id}
DELETE - https://45wf34z5yf.execute-api.us-east-1.amazonaws.com/dev/questions/{id}
functions:
serverless-rest-api-with-dynamodb-dev-update: arn:aws:lambda:us-east-1:488110005556:function:serverless-rest-api-with-dynamodb-dev-update
serverless-rest-api-with-dynamodb-dev-get: arn:aws:lambda:us-east-1:488110005556:function:serverless-rest-api-with-dynamodb-dev-get
serverless-rest-api-with-dynamodb-dev-list: arn:aws:lambda:us-east-1:488110005556:function:serverless-rest-api-with-dynamodb-dev-list
serverless-rest-api-with-dynamodb-dev-create: arn:aws:lambda:us-east-1:488110005556:function:serverless-rest-api-with-dynamodb-dev-create
serverless-rest-api-with-dynamodb-dev-delete: arn:aws:lambda:us-east-1:488110005556:function:serverless-rest-api-with-dynamodb-dev-delete
Be sure to replace the the variables in the scripts below.
You can create, and retrieve with the following commands:
#!/usr/bin/env bash
QUESTIONS_URL=
ACCESS_TOKEN=
API_KEY=
curl -X POST \
${QUESTIONS_URL} \
-H "Authorization: ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-H "x-api-key: ${API_KEY}" \
-d '{"text":"myQuestionText", answers: []}'
Example Result:
{"text":"Learn Serverless","id":"ee6490d0-aa11e6-9ede-afdfa051af86","createdAt":1479138570824,"checked":false,"updatedAt":1479138570824}%
#!/usr/bin/env bash
QUESTIONS_URL=
ACCESS_TOKEN=
API_KEY=
curl -X GET \
${QUESTIONS_URL} \
-H "Authorization: ${ACCESS_TOKEN}" \
-H "x-api-key: ${API_KEY}"
Example output:
[{"text":"Deploy my first service","id":"ac90feaa11e6-9ede-afdfa051af86","checked":true,"updatedAt":1479139961304},{"text":"Learn Serverless","id":"206793aa11e6-9ede-afdfa051af86","createdAt":1479139943241,"checked":false,"updatedAt":1479139943241}]%
##Cleanup
#!/usr/bin/env bash
cd ..
SLS_DEBUG=* serverless remove --stage dev