AshMiq Digital
← All posts

How to use AWS Cognito to access AWS Services

S3 access without long-lived keys: User Pool → Identity Pool → per-user folder scope.

In 2021 the common way for a frontend to talk to AWS was an IAM user's access key pasted into the client. It worked, and it meant anyone with devtools owned your bucket. This post walks the proper path: Cognito authentication in front of everything, so no long-lived secret ever ships to the browser.

The flow: a user authenticates against a Cognito User Pool and gets short-lived tokens; those tokens are exchanged at an Identity Pool for temporary AWS credentials; those credentials are scoped so the user can only touch their own prefix in one S3 bucket.

The setup, condensed: create a User Pool with a hosted UI domain, an app client, and callback URLs. Create an Identity Pool and wire it to that User Pool. Create the S3 bucket with a CORS policy. Then attach an IAM policy to the pool's auth role that uses the Cognito identity as the folder name:

{
  "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}/*"
  ]
}

That ${cognito-identity.amazonaws.com:sub} is the whole trick: each authenticated user gets a folder that only they can read and write, with no per-user IAM setup at all. I put up a working Vite demo at github.com/nazmifeeroz/simple-aws-upload — sign in, upload a file, see it listed.

2026 note: the architecture here aged better than the console clicks — every screenshot in the original post is of a Cognito console that has since been redesigned twice. The pattern (identity provider → token exchange → scoped temporary credentials) is still exactly how I'd do it, and it's now the boring, documented way.