DEV Community

Nazmi for Zenika

Posted on • Updated on

How to use AWS Cognito to access AWS Services

This article will explore how to block access to AWS S3 storage and allow only Cognito authenticated users to access their own directory.

AWS provides many services, and many times developers tend to not use the right architecture to securely access these services. The fastest and easiest way was to use an IAM user access key and secret to access. To do this, a developer needs to expose these access keys. Fortunately, there is a better way to securely access AWS services without exposing client secrets (see diagram below).

Cognito pool architecture

A user will need to authenticate first with Cognito to get his own access tokens that have an expiry. With this token, it exchanges with the Identity pool for AWS credentials. Only then, with these AWS credentials can the user gain access to AWS services. It's a long tour, but definitely, a more secure way than to use client secrets exposed to the front end.

TL;DR

Create User Pool

  • Add a Cognito domain name prefix
  • Create an app client (save the user pool, client ID)
  • Update the callback URL in the app settings
  • Enable client Identity Providers

Create Identity Pool

  • Leave all as default

Create S3 Bucket

  • Update CORS

Create Policy

  • Update bucket name in policy
  • Attach policy to Cognito Auth role

Test your AWS Cognito setup

You're done! Authenticate, upload a file and see it listed on your page!

Create User Pool

Let's start by creating a Cognito User Pool. This service will manage our user registration and user tokens. Go to https://console.aws.amazon.com/cognito/home and click on Manage User Pools.

Cognito landing page

Click on Create a user pool, enter your desired Pool name and click on Review Defaults. Leave all fields as default and click on Create Pool. It should direct you to the General Settings page.

We are going to use Cognito's default hosted UI for users to register and sign in. We will need to set the Cognito domain name for users to be redirected and sign in to their Cognito account. On the left sidebar, click on Domain Name. Enter a domain prefix and check that it's available. Once you have found an available Domain prefix, click on Save changes.

Domain name

Take note of the Cognito Domain URL, as we will use it to redirect the login page.

Next, we need to add an app client. On the left sidebar, click on App Clients > Add an app client. Here we need to insert an App client name on the client-side when calling the Cognito user pool. Leave other fields as default. Click on Create app client.

cognito app clients

On the next screen, take note of the App client ID.

Lastly, we need to add a Callback URL and a Sign out URL in App Client Settings,select Cognito User Pool for Enabled Identity Providers, and select all under OAuth 2.0 section. Don't forget to Save Changes.

Cognito Identity Provider

Now, we can register a user into our User Pool. Go to,

https://<DOMAIN_PREFIX>.auth.<REGION>.amazoncognito.com/login?response_type=token&client_id=<CLIENT_ID>&redirect_uri=http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Replace DOMAIN_PREFIX, REGION and CLIENT_ID with the values that you've created previously. You should see the sign-in page.

Cognito sign in

Create Identity Pool

With an identity pool, you can obtain temporary, limited-privilege AWS credentials to access other AWS services - AWS Docs

We will now create an Identity Pool to allow our users to access AWS services. Before we create one, we need to note the User Pool ID and App Client ID. These can be found under General Settings > Pool Id field and App Client Settings > ID.

cognito pool id

Cognito Client ID

Once you have noted down the values for User Pool ID and App Client ID, go to the home page of AWS Cognito, console.aws.amazon.com/cognito/home and click on Manage Identity Pools. Click on Create new Identity Pool and enter an Identity pool name.

Cognito new identity

Expand the Authentication providers accordion and select the Cognito tab. Enter the User Pool ID and App client ID you have previously noted and click on Create Pool. On the next screen, the wizard will prompt you to create IAM roles for creating the identity pool. These roles are crucial to define what an authenticated or unauthenticated user is allowed to access.

Cognito new details

Leave it as default and click on Allow. Take note of the Role names. Now we are done setting up Cognito!

Create an S3 Bucket

To test out our Cognito Identity Pool, we will create an S3 bucket that only allows an authenticated user to access his own directory to perform all the CRUD actions. Go to the S3 home page, https://s3.console.aws.amazon.com/s3/home and click on Create bucket.

create s3 bucket

Give your bucket a unique name, leave all the fields as default and click on Create Bucket. Click on the bucket you've just created and click on the Permissions tab. Scroll down to the Cross-origin resource sharing (CORS) section and click on Edit. Paste this JSON code into the text area,

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
    "AllowedOrigins": ["http://localhost:3000"],
    "ExposeHeaders": []
  }
]
Enter fullscreen mode Exit fullscreen mode

S3 Cors

Click on Save Changes. These CORS settings will only allow our client app to access our bucket. That's all for the S3 setup. Do take note of the Bucket name to be used on the client-side.

Create a policy

We need to create a policy that allows our Cognito authenticated users to access our S3 bucket. Go to IAM service, https://console.aws.amazon.com/iam/home, select Policies on the left sidebar and click on Create policy. Click on the JSON tab and paste this policy,

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListAllMyBuckets", "s3:GetBucketLocation"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::bucket-name",
      "Condition": {
        "StringLike": {
          "s3:prefix": ["", "/", "${cognito-identity.amazonaws.com:sub}/*"]
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::bucket-name/${cognito-identity.amazonaws.com:sub}",
        "arn:aws:s3:::bucket-name/${cognito-identity.amazonaws.com:sub}/*"
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Update the bucket name to the S3 bucket you've created above.

Click on the next screen until you reach the Review policy screen. Give a name to your policy and click on Create policy.

Create policy

Click on Roles on the left sidebar. Select the Auth_Role that you have created from the previous step in Create a new identity. Select Attach policies. Find the policy that we've just created above. Select it and click on Attach policy.

add policy

Now our authenticated Cognito user can access the S3.

It may take a while for the policy to take effect.

Test your AWS Cognito setup

Clone this repository (https://github.com/nazmifeeroz/simple-aws-upload) to test out if your AWS setup works! You simply need to add your AWS config variables into a .env file at the root of the project folder.

Create a .env file at the root of the project folder with these variables.

VITE_APP_S3_BUCKET=<YOUR_S3_BUCKET>
VITE_APP_COGNITO_LOGIN_URL=https://<COGNITO_DOMAIN_PREFIX>.auth.<COGNITO_REGION>.amazoncognito.com/login?response_type=token&client_id=<USER_POOL_ID>&redirect_uri=http://localhost:3000
VITE_APP_COGNITO_LOGOUT_URL=https://<COGNITO_DOMAIN_PREFIX>.auth.<COGNITO_REGION>.amazoncognito.com/logout?client_id=<USER_POOL_ID>&logout_uri=http://localhost:3000
VITE_APP_BUCKET_REGION=<BUCKET_REGION>
VITE_APP_COGNITO_REGION=<REGION>
VITE_APP_COGNITO_IDENTITY_POOL_ID=<IDENTITY_POOL_ID>
VITE_APP_COGNITO_IDENTITY_PROVIDER=cognito-idp.<REGION>.amazonaws.com/<IDENTITY_PROVIDER>
Enter fullscreen mode Exit fullscreen mode

Run in development

Install dependencies by running,

# if you have yarn installed
yarn install

# or if you use npm
npm install
Enter fullscreen mode Exit fullscreen mode

Run the app,

yarn dev
# OR
npm run dev
Enter fullscreen mode Exit fullscreen mode

Frontend

Once you sign in, you should be able to see this screen. Choose a file and upload it. If all works well, you should see a success alert, and your file name should be listed on the page! 😃

Discussion (3)

Collapse
johnsibly profile image
John Sibly • Edited on

Thanks Zenika. This is a great article and one of the most straightforward I've found on Cognito. One slight area of confusion for me was what to set IDENTITY_PROVIDER as.
However, I found that using the User Pool Id (which you suggest making of note of earlier in the article) worked as expected - so leaving this comment here in case anyone else gets stuck in the same way.

Collapse
davru profile image
˙˙˙ɹǝʌo ǝɯ dılɟ ʎǝɥ

Nice writeup.
A little unclear on which IDs go where in the .env but I think I've got that sorted.
Now I'm getting a CORS issue on a request:
Access to fetch at 'demo-.../some.jpg?x-id=PutObject' from origin 'localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Collapse
davru profile image
˙˙˙ɹǝʌo ǝɯ dılɟ ʎǝɥ

Actually just realized that the var usage assumes the s3 bucket and cognito are in the same region, split them out so I could configure them separately and the upload is working now without error.