Presigned URL CORS issue

Given the following configuration

resource "aws_s3_bucket" "content" {
  bucket = "content"
}

resource "aws_s3_bucket_cors_configuration" "content" {
  bucket = aws_s3_bucket.content.bucket

  cors_rule {
    allowed_headers = ["*"]
    allowed_methods = [
      "HEAD",
      "PUT",
      "DELETE",
      "POST",
    ]
    allowed_origins = [
      "http://localhost:5713"
    ]
    expose_headers = [
      "Access-Control-Allow-Origin",
      "ETag"
    ]
    max_age_seconds = 5000
  }
}

And a route like so

import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import { s3Client } from '../lib/awsClient';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

type Method = 'PUT' | 'GET';

export const generatePresignedUrl = async (method: Method, path: string) => {
  const type = method === 'PUT' ? PutObjectCommand : GetObjectCommand;
  const command = new type({
    Bucket: 'content',
    Key: path,
  });

  const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 });

  return url;
};

If I make a request to the returned URL (I’ve mangled it in the response below on purpose) from a Chrome window the console reports

Access to fetch at 'http://127.0.0.1:4566/content/applications/1/file?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIARF52ELDSBKKXS%2F20230424%2Fus-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230424T200913Z&X-Amz-Expires=3600&X-Amz-Signature=ecea4238399f343a7cb0b22d52c1b96641a5c0c38a8263f9388276306dd2a&X-Amz-SignedHeaders=host&x-id=PutObject' from origin 'http://localhost:5173' 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.

Using * isn’t an option as the fetch request later is done with credentials: include which isn’t supported by *

awslocal s3api get-bucket-cors --bucket content reports

{
    "CORSRules": [
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "POST",
                "HEAD",
                "DELETE",
                "PUT"
            ],
            "AllowedOrigins": [
                "http://localhost:5713"
            ],
            "ExposeHeaders": [
                "ETag",
                "Access-Control-Allow-Origin"
            ],
            "MaxAgeSeconds": 5000
        }
    ]
}

Which looks correct.

What exactly is blocking me? I’ve read some questionable material about Chrome not supporting localhost CORS requests but this behavior also happens in Safari.

I found this page and followed the steps including adding more methods and the localstack app origin: S3 | Docs

The output in the console is still

content:1 Access to XMLHttpRequest at 'https://localhost.localstack.cloud:4566/content?list-type=2&max-keys=1000&prefix=' from origin 'https://app.localstack.cloud' 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.

Oh my god my port is wrong :frowning: It’s 5173 not 5713 and I can’t believe I’ve been mistaking it as such this entire time.

1 Like