Skip to main content

Authentication & Authorization

For web and mobile applications uploading resumes directly to S3, use AWS Cognito Identity Pools (Federated Identities) to issue short-lived, scoped AWS credentials to the client. This avoids exposing long-lived AWS access keys in your frontend code.

How It Works

The Cognito Identity Pool acts as a credential broker. Your frontend requests temporary AWS credentials from the pool, which issues them via an IAM role — scoped to only s3:PutObject on the RAISE bucket. The credentials expire automatically (typically after 1 hour).

Two modes are supported:

ModeWhoHow
UnauthenticatedAnonymous users (no login required)Identity Pool issues credentials for the Unauthenticated IAM Role
AuthenticatedLogged-in users (Cognito User Pool, Google, etc.)Frontend passes an ID Token; Identity Pool validates it and issues credentials for the Authenticated IAM Role

IAM Policy for S3 Uploads

Both the Unauthenticated and Authenticated IAM Roles need a policy granting upload access to the RAISE S3 bucket. Replace YOUR_BUCKET_NAME with the value from the CDK stack output.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}
]
}
warning

Do not grant s3:GetObject, s3:ListBucket, or s3:DeleteObject. The client only needs to upload — Lambda handles the rest. Least-privilege is critical when issuing credentials to browser clients.

Unauthenticated Access

Use this for applications where users upload without logging in.

import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity";
import { S3Client } from "@aws-sdk/client-s3";

const REGION = "us-east-1";
const IDENTITY_POOL_ID = "us-east-1:YOUR_IDENTITY_POOL_ID";

export function createS3Client(): S3Client {
return new S3Client({
region: REGION,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: REGION }),
identityPoolId: IDENTITY_POOL_ID,
}),
});
}

Authenticated Access

Use this when users log in via Cognito User Pools or a third-party provider (Google, Facebook, etc.). Pass the ID Token from the login provider to the Identity Pool via the logins map.

import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity";
import { S3Client } from "@aws-sdk/client-s3";

const REGION = "us-east-1";
const IDENTITY_POOL_ID = "us-east-1:YOUR_IDENTITY_POOL_ID";
const USER_POOL_ID = "us-east-1_YOUR_USER_POOL_ID";

export function createAuthenticatedS3Client(idToken: string): S3Client {
return new S3Client({
region: REGION,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: REGION }),
identityPoolId: IDENTITY_POOL_ID,
logins: {
[`cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}`]: idToken,
},
}),
});
}

Benefits of This Approach

  • No backend proxy — Resumes upload directly from the browser to S3, reducing server load and complexity.
  • Scalability — S3 handles upload scaling directly; no bottleneck through your application servers.
  • Security — Temporary credentials expire automatically. IAM roles enforce least-privilege — clients can only PutObject, nothing else.
  • Flexibility — The same pattern works for anonymous uploads and authenticated uploads with minimal code change.