Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework: redirect to Amazon S3 fails when using Token Authentication

I'm using token authentication in DRF and for a certain API call, want to redirect to S3 (using a URL like https://my_bucket.s3.amazonaws.com/my/file/path/my_file.jpg?Signature=MY_AWS_SIGNATURE&AWSAccessKeyId=MY_AWS_ACCESS_KEY_ID). However, I get the following error from AWS:

<Error>
  <Code>InvalidArgument</Code>
  <Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message>
  <ArgumentName>Authorization</ArgumentName>
  <ArgumentValue>Token a3f61c10592272399099882eb178bd4b755af5bf</ArgumentValue>
  <RequestId>E4038228DD1E6330</RequestId>
  <HostId>9c2xX59cugrR0CHjxQJR8IBE4MXBbNMX+wX2JdPJEuerkAftc32rufotM7COKLIavakByuRUXOo=</HostId>
</Error>

It's clear why this happens--the Authorization header with DRF's token is propagated with the redirect and S3 doesn't like it.

After researching and trying a million ways to get rid of that header, I gave up and decided to try and override the header with an S3 value: AWS MY_AWS_SIGNATURE:MY_AWS_ACCESS_KEY_ID, after which I get a different error:

<Error>
  <Code>InvalidArgument</Code>
  <Message>Unsupported Authorization Type</Message>
  <ArgumentName>Authorization</ArgumentName>
  <ArgumentValue>Token a3f61c10592272399099882eb178bd4b755af5bf</ArgumentValue>
  <RequestId>94D5ADA28C6A5BFB</RequestId>
  <HostId>1YznL6UC3V0+nCvilsriHDAnP2/h3MoDlIJ/L+0V6w7nbHbf2bSxoQflujGmQ5PrUZpNiH7GywI=</HostId>
</Error>

As you can see, the end result is the same--even if I override the Authorization header in my response, it still keeps the original DRF token authentication value.

# relevant portion of my response construction
headers = {'Location': 'https://my_bucket.s3.amazonaws.com/my/file/path/my_file.jpg',
           'Authorization': 'AWS %s:%s' % (params['AWSAccessKeyId'], params['Signature'])}
return Response(status=status.HTTP_302_FOUND, headers=headers)

So, my question is, how can the Authorization header in a DRF response be either removed or overridden?

like image 531
Cloud Artisans Avatar asked Nov 14 '15 22:11

Cloud Artisans


People also ask

How does token authentication work in Django REST framework?

Token authentication refers to exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.

Which authentication is best in Django REST framework?

TokenAuthentication. Note: The token authentication provided by Django REST framework is a fairly simple implementation. For an implementation which allows more than one token per user, has some tighter security implementation details, and supports token expiry, please see the Django REST Knox third party package.

How do I upload files to AWS s3 using Django REST framework?

Building a simple Django Rest API application Execute the commands below to set up the project. Add the code snippet below to urls.py file in the dropboxer project directory. Create serializers.py and urls.py files in the uploader app. In models.py file, we create a simple model that represents a single file.


1 Answers

Redirecting the Authorization header is the responsibility of the client (eg. browsers, cURL, HTTP libraries/toolkits).

For example, Paw, my toolkit to query my APIs offers that kind of configuration: Paw app redirect configuration

So basically, major browsers are tend to redirect the Authorization header which cause the conflict on S3.

Also I suspect you misunderstood how redirects are performed:

  1. When DRF issues a redirect, it returns an HTTP 301 or 302 response to the client, containing the new Location header (the request is not "forwarded" directly via DRF)
  2. Then, the client requests this new URI

And finally, you're not overriding any Authorization header when you're emitting your 302 since this is the response... to the client (which can carry an Authorization header but that's useless).


Right now, you have a bunch of solutions (thus not out of the box...):

  1. Passing your token through a different header to avoid the conflict (X-Token for example)
  2. Passing your token through a HTTP GET parameter (?token=blah)
  3. Using your DRF view to proxy the S3 object (no redirect then)

The first two solutions may break in some way the consistency of your API but in some way are fair enough. They would require a custom TokenAuthentication or get_authorization_header (from rest_framework.authorization).

The last one is transparent but may be totally unsuitable depending on the object your're fetching on S3 and/or your hosting constraints...

That's all I can tell you for now. As you know, I've been stuck with the same situation too. I would be so pleased if anyone could suggest a better solution.

like image 151
Léo Avatar answered Oct 03 '22 08:10

Léo