12

In my React app, I'm making a GET REST web service call to an endpoint whose purpose is to generate a presigned AWS URL to a protected S3 asset. We pass an Authorization header and a token to the service.

This initial call works as expected and the service responds with a redirect (via a 307 response code) and includes the presigned URL in the response's Location header.

The issue I'm facing is that when the redirect is followed, Amazon rejects the call with a 400 response code and the following message.

Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified.

Is there a way to remove the Authorization header before the redirect is followed?

This seems like it should be a fairly common situation (when a React application requires access to a protected S3 asset) - is there a better way to handle this use case?

Vinnie
  • 12,400
  • 15
  • 59
  • 80
  • It seems unusual, if not outright wrong, for the browser to continue to send the Authorization header after a redirect. You might check whether the same behavior results from a 302 or 303 redirect. What browser or UA are you using? See also https://stackoverflow.com/a/28671822/1695906 – Michael - sqlbot Jun 14 '18 at 17:40
  • @Michael-sqlbot - I agree - I was not expecting this. We were using Chrome to test the app - but also fails in Postman. I did look at the link you posted, but also saw this one https://stackoverflow.com/questions/49253245/authorization-header-when-following-redirects – Vinnie Jun 14 '18 at 18:02

1 Answers1

6

After several conversations with Amazon support, the way to achieve the above is to front S3 with a CloudFront distribution. CloudFront does not have the same restrictions as S3 with Only one auth mechanism allowed

Here's what I did:

  • Create a CloudFront distribution with an S3 origin.
    • Optional selection - restrict bucket access.
    • You'll need a CloudFront Access Identity (use an existing or create a new one).
    • Recommended selection - update bucket policy
  • In S3
    • Ensure the bucket policy was updated (automatically populated when "update bucket policy" is selected above)
    • update the CORS configuration with the methods/headers you need to support

The other thing you'll need is a CloudFront key pair as described here and then use that information when generating the presigned link.

MORE INFO

Amazon definitely makes this much more complicated than it needs to be (i.e. why is there the Only one auth mechanism allowed restriction on S3 at all?) but at least there's a workaround for those that need it.

Vinnie
  • 12,400
  • 15
  • 59
  • 80
  • 2
    This seems like a very awkward fix, and it means you're still sending your auth tokens to third parties. I wasn't able to find a straight-forward client-side solution either. I may try handling redirect manually in JS next... – jplatte Dec 03 '20 at 19:46
  • 1
    Narrator: he/she couldn't. You literally can't handle redirects manually in JS (by design): https://github.com/whatwg/fetch/issues/763 – cbreezier Apr 30 '21 at 02:57
  • You *can* let the first request fail with 400 from S3, then inspect the final url and resubmit without headers, but you pay the cost of an extra request and ugly 400's showing up in your console/network tab – cbreezier Apr 30 '21 at 02:59
  • **_the question and answer are really undervoted_** // AWS also provides Edge Lambda functions for its CloudFront (which can intercept the request at several points of the pipeline). I'm sure it is possible to do something like verifying a JWT token inside the lambda. But it was almost impossible to achieve an RDS connection from that Lambda in order to check user permissions (or at least its existence) for the token in the request – maxkoryukov Nov 02 '22 at 05:10