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

683 views Asked by At

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:

  • the Cloudflare R2 access key ID & secret access key are correct and active

  • the token has admin read & write access to all buckets

  • no proxies or firewalls are active

  • the environment variables are correctly loaded into the Node.js process

What is the issue?

2

There are 2 answers

1
Ermiya Eskandary On BEST ANSWER

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:

  • auto: Automatic selection of the closest region based on caller location
  • wnam: Western North America
  • enam: Eastern North America
  • weur: Western Europe
  • eeur: Eastern Europe
  • apac: Asia-Pacific

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.

0
Luís Deschamps Rudge On

Seems related to the incident they're going through during the past few days. Looking at this link from the status page, looks like tokens generated in the past few days might not be working as expected. I generated a new token and things started to work again!