Serverless authorizers - IAM authorizer

By Marcin Piczkowski Comment
Subscribe

In the first article about Servlerless Authorizers I explained how to write custom authorizer for REST endpoint. This was when we wanted to develop user storage from scratch ourselves.

In this article I will show you that we can use something already provided by Amazon called Identity and Access Management (IAM) which is an Amazon way to manage users, credentials and authorization. Access to all AWS resources can be protected using IAM. It also allows integrating with external Identity Providers like Facebook, Google, Twitter, etc. This way we can develop signup and login features in our Serverless Reservation app easily.

IAM Authorizer

Serverless has already support for IAM and we do not need to write a custom authorizer for this. You can find many examples how to implement this on the web, e.g. here

Let’s see how it works.

AWS Service which allows securing our app is called Cognito.

Having this we can create our own users’ directory on Amazon. When we release the app we should use the region which complies with local legacy policies regarding storing user sensitive data. In my case, it would be Frankfurt in EU as US policies are more liberal than EU in this context. For our demonstration purpose we put everything in us-east-1 region.

Create User Pool

Login to AWS console and navigate to Cognito service, then go to Manage your User Pools. Make sure you are in us-east-1 region.

image

Then create a new pool called authorizers-demo.

image

Go to defaults.

image

Select Choose username attributes… Select Email address or phone numbers and Allow email addresses. Our users will use their email as a username.

image

Then go to next step, leave default password policies or if you with you can change them to easier type passwords for testing purpose.

image

Now go to Review section and make sure that field Username attributes is set to email.

image

Click Create pool and we’re done. The summary page will show with Pool Id, we will need this later for configuration.

image

Create new App client

We also need to add a new app to the pool to be able to use it from our application. Go to App clients and add a new client.

image

Uncheck Generate client secret and check Enable sign-in API for server-based authentication.

Amazon documentation says:

The Amazon Cognito JavaScript SDK does not use the app client secret. If you configure your user pool app client with an app client secret, the SDK will throw exceptions.

image

Click Create app client and note the App client id, we will need it later.

image

Create a Cognito user for test

At the moment we do not yet have any UI for our application so we need to somehow create any user for tests using command line. We can do this with AWS CLI like that:

aws cognito-idp sign-up \
  --region us-east-1 \
  --client-id YOUR_COGNITO_APP_CLIENT_ID \
  --username sls@test.com \
  --password Passw0rd!

Just replace YOUR_COGNITO_APP_CLIENT_ID with your own.

e.g.:

aws cognito-idp sign-up \
  --region us-east-1 \
  --client-id 75ujpfhk63kt39tn0hmplpddr3 \
  --username sls@test.com \
  --password Passw0rd!

You can now check AWS console for new user created.

image

You see the user status is UNCONFIRMED. Before the user can authenticate with the User Pool, the account needs to be verified. We can do it with command:

aws cognito-idp admin-confirm-sign-up \
  --region us-east-1 \
  --user-pool-id YOUR_USER_POOL_ID \
  --username sls@test.com

Replace YOUR_USER_POOL_ID with your own. e.g.:

aws cognito-idp admin-confirm-sign-up \
  --region us-east-1 \
  --user-pool-id us-east-1_Fmwg96qkp \
  --username sls@test.com

Now the user is CONFIRMED.

Add IAM secured REST endpoint

Let’s continue our app from the previous article and add another endpoint in our application: /hello/rest/iam.

In serverless.yml you should add new function like this:

functions:
  helloRestIam:
    handler: helloRestIam.handler
    events:
      - http:
          path: hello/rest/iam
          method: get
          authorizer: aws_iam

Deploy application

Execute command

sls deploy -v function -f helloRestIam

Note the API Gateway ID in endpoint url, e.g.:

https://28p4ur5tx8.execute-api.us-east-1.amazonaws.com/dev/hello/rest/iam

The API Gateway ID is 28p4ur5tx8. It will be needed in next step.

Create Cognito Identity Pool

Amazon Cognito Federated Identities enables developers to create unique identities for users and authenticate them with the identity provider. In our case identity provider is Cognito User Pool but it could be as well Facebook, Google or different custom provider.

Go to Cognito service in AWS console and select Manage Federated Identities

image

Create new identity pool.

Enter identity pool name.

In Authentication providers section select Cognito and enter User Pool ID and App client id obtained earlier when creating User Pool.

image

Submit the form.

Go to View Details.

Two Role Summary sections are expanded. The top section summarizes the permission policy for authenticated users, and the bottom section summarizes that for unauthenticated users.

Select View Policy Document in the top section. Then select Edit and add the following policy document:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "mobileanalytics:PutEvents",
        "cognito-sync:*",
        "cognito-identity:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "execute-api:Invoke"
      ],
      "Resource": [
        "arn:aws:execute-api:YOUR_API_GATEWAY_REGION:*:YOUR_API_GATEWAY_ID/*"
      ]
    }
  ]
}

Replace YOUR_API_GATEWAY_REGION and YOUR_API_GATEWAY_ID with your values, in our case it’s YOUR_API_GATEWAY_REGION = us-east-1, YOUR_API_GATEWAY_ID = 28p4ur5tx8

Save identity pool.

Then edit identity pool and see Identity Pool Id (e.g. us-east-1:addccfed-eb42-4802-817f-700f13e51d8e), we will need it for API queries.

image

Call endpoint from client

Amazon Cognito authenticated requests needs a few steps:

  1. Authenticate against User Pool and acquire a user token.
  2. With the user token get temporary IAM credentials from our Identity Pool.
  3. Use the IAM credentials to sign our API request with Signature Version 4

This is a bit tricky if done manually. Fortunately, there is API Gateway SDK from Amazon which we can use to script the requests. There is even a test app ready to use. What we need to do is

npm install -g aws-api-gateway-cli-test
apig-test \
--username='sls@test.com' \
--password='Passw0rd!' \
--user-pool-id='us-east-1_Fmwg96qkp' \
--app-client-id='75ujpfhk63kt39tn0hmplpddr3' \
--cognito-region='us-east-1' \
--identity-pool-id='us-east-1:addccfed-eb42-4802-817f-700f13e51d8e' \
--invoke-url='https://28p4ur5tx8.execute-api.us-east-1.amazonaws.com/dev' \
--api-gateway-region='us-east-1' \
--path-template='/hello/restiam' \
--method='GET'

the response should be like:

{ status: 200,
  statusText: 'OK',
  data: 
   { message: 'Hello IAM, authenticated user identity: us-east-1:cbc99154-7dab-41e1-8309-0a55d46d21a1 !',
     input: 
      { resource: '/hello/restiam',
        path: '/hello/restiam',
        httpMethod: 'GET',
        headers: [Object],
        queryStringParameters: null,
        pathParameters: null,
        stageVariables: null,
        requestContext: [Object],
        body: null,
        isBase64Encoded: false } } }

Note how we access the user identity in the implementation of the function. It is passed by IAM authorizer to function as event.requestContext.identity.cognitoIdentityId

The fill code can be found here.

Stay tuned for next articles in series of Serverless Authorizers.


You can easily find the series articles with hashtag #authorizer

comments powered by Disqus