amazon-web-servicesamazon-s3cloudflareaws-sdk-jscloudflare-r2

Why do I get 401 Unauthorized when I use the AWS SDK with Cloudflare R2 Storage?


I'm trying to list objects in a Cloudflare R2 storage bucket using the AWS SDK for JavaScript (v3) in a Node.js application, but I'm encountering an 'Unauthorized' error.

Here is the code snippet that I'm using:

const { S3Client, ListObjectsCommand } = require('@aws-sdk/client-s3');

const s3Client = new S3Client({
  endpoint: 'https://<my-r2-subdomain>.r2.cloudflarestorage.com',
  credentials: {
    accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
    secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
  },
  signatureVersion: 'v4',
});

const bucketParams = {
  Bucket: 'my-bucket-name',
};

const listObjects = async () => {
  try {
    const data = await s3Client.send(new ListObjectsCommand(bucketParams));
    console.log('Success', data);
  } catch (err) {
    console.error('Error', err);
  }
};

listObjects();

I receive the following 401 response:

{
  "$fault": "client",
  "$metadata": {
    "httpStatusCode": 401,
    "requestId": "undefined",
    "extendedRequestId": "undefined",
    "cfId": "undefined",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "Code": "Unauthorized"
}

The error stack trace is:

Error Unauthorized: Unauthorized
    at throwDefaultError (D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\smithy-client\dist-cjs\default-error-handler.js:8:22)
    at D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\smithy-client\dist-cjs\default-error-handler.js:18:39
    at de_ListObjectsCommandError (D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\client-s3\dist-cjs\protocols\Aws_restXml.js:5032:20)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\middleware-serde\dist-cjs\deserializerMiddleware.js:7:24
    at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-signing\dist-cjs\awsAuthMiddleware.js:14:20
    at async D:\GitHub\cloudflare-r2-testing\node_modules\@smithy\middleware-retry\dist-cjs\retryMiddleware.js:27:46
    at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-sdk-s3\dist-cjs\region-redirect-endpoint-middleware.js:14:24
    at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-sdk-s3\dist-cjs\region-redirect-middleware.js:9:20
    at async D:\GitHub\cloudflare-r2-testing\node_modules\@aws-sdk\middleware-logger\dist-cjs\loggerMiddleware.js:7:26 

I have confirmed that:

What is the issue?


Solution

  • You need to specify a region, specifically a CloudFlare R2 region.

    CloudFlare R2 regions aren't the same as AWS S3 regions. If you don't manually specify an R2 region for your client, things like using aws configure, presence of a '~/.aws/config' file, or other configuration options on your machine may lead your client to using an AWS region.

    These AWS regions won't be compatible with CloudFlare R2, so you need to make sure that at some point in the credentials provider chain, your AWS SDK client uses an R2 region.

    Per CloudFlare docs, valid values are:

    Specify the region like so:

    const s3Client = new S3Client({
      endpoint: 'https://${ACCOUNT_ID}.r2.cloudflarestorage.com',
      region: "{REGION}",
      credentials: {
        accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
        secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
      },
      signatureVersion: 'v4',
    });
    

    As a side-note,

    This action has been revised. We recommend that you use the newer version, ListObjectsV2, when developing applications. For backward compatibility, Amazon S3 continues to support ListObjects.

    Use ListObjectsV2Command for new applications.