Django-rest-framework: Setting Permissions on OPTIONS

Created on 4 Jun 2013  路  11Comments  路  Source: encode/django-rest-framework

Is there a way to fine tune the permissions for OPTIONS requests? I found out through my frontend javascript dev that the js programmer has 0 control over which headers are sent with the preflight OPTIONS request.

The only solution I could think of was to set 'DEFAULT_PERMISSION_CLASSES' in settings.py to AllowAny.

Is there any security risk on a non-authenticated OPTIONS on a resource? The purpose is to see if a given operation is allowed, so it seems like that might not be an entirely correct thing to do, since it should tell the client that a certain type auth is required, for example Bearer or Digest etc. So am I correct that the pre-flight request should prob. return a response with a header like: WWW-Authenticate:Bearer realm="api"?

Here is the relevant part of the spec.
http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0

It looks like the browser will strip any headers from a pre-flight request even if you were able to set them. Am I reading that correctly?

Another interesting read from a twitter developer: https://code.google.com/p/twitter-api/issues/detail?id=2273

It looks like the proper way of handling CORS options requests is not to do any authentication. The author describes it as a catch-22.

Most helpful comment

Can you explain where exactly, because I've retested again, rechecked DRF source, and OPTIONS requests go through the very same dispatch() > initial() > check_permissions(), and fails because user credentials are missing.

views.py

class MyView(APIView):

    permission_classes = (IsAuthenticated,)

urls.py

urlpatterns = [
    url(r'^myview/', MyView.as_view()),
]

console

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

All 11 comments

Believe this is what you need for dealing with CORS pre-flight requests...

https://github.com/OttoYiu/django-cors-headers

Cool, thanks I will check it out. Looks very promising.

Going to close this issue for now - if you've further issues with CORS feel free to hit up the mailing list, or reopen this ticket, whichever seems most appropriate...

It worked majestically.

I'm reopening this issue because I think the permissions for OPTIONS (metadata) requests are handled incorrectly in DRF.

As per the W3C specs, all preflight OPTIONS requests are NOT authenticated, meaning the users will always get a 401 error when preflighting a request for authenticated endpoints, because modern browsers never send this as per the specs :

Otherwise, make a preflight request. Fetch the request URL from origin source origin using referrer source as override referrer source with the manual redirect flag and the block cookies flag set, using the method OPTIONS, and with the following additional constraints:

  • Include an Access-Control-Request-Method header with as header field value the request method (even when that is a simple method).
  • If author request headers is not empty include an Access-Control-Request-Headers header with as header field value a comma-separated list of the header field names from author request headers in lexicographical order, each converted to ASCII lowercase (even when one or more are a simple header).
  • Exclude the author request headers.
  • 鉃★笍 Exclude user credentials.
  • Exclude the request entity body.

I think this shouldn't be the default behaviour here.
DRF should authorise all OPTIONS request by default for standard permission classes (IsAuthenticated, IsAdminUser, etc) and users may override this when they explicitly need to protect metadata info (infringing the standard CORS compatibility)

@Antwan86 I'm slightly confused by what you're actually proposing, although it may be that it's better suited to a new issue thread.

I'm reading two different things, here...

Fetch the request URL [...] Exclude user credentials.

And...

DRF should authorise all OPTIONS request by default for standard permission classes

We can't authorise the request if it doesn't contain any credentials.

Maybe give an example with () what the typical response looks like by default () what you think a typical response should look like by default. You could also ideally implement a test case for the behavior that you think would be more valid, but that's not a necessity.

I'm saying that each OPTIONS request should be exempt of authentication as web browsers never send authentication headers (Authorization, Cookie) when preflighting an actual request, as per W3C spec.

As a consequence, one has to override IsAuthenticated if they want to use DRF in CORS environment.

class IsAuthenticated(permissions.IsAuthenticated):

    def has_permission(self, request, view):
        if request.method == 'OPTIONS':
            return True
        return super(IsAuthenticated, self).has_permission(request, view)

Happy to create a testcase for it, but before moving to implementation I need to be sure it's valid.

OPTIONS doesn't use the same permission handling as the other cases.

Can you explain where exactly, because I've retested again, rechecked DRF source, and OPTIONS requests go through the very same dispatch() > initial() > check_permissions(), and fails because user credentials are missing.

views.py

class MyView(APIView):

    permission_classes = (IsAuthenticated,)

urls.py

urlpatterns = [
    url(r'^myview/', MyView.as_view()),
]

console

http GET http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

http OPTIONS http://127.0.0.1:8000/myview/
HTTP/1.0 401 Unauthorized

Yep, after a hours of digging this problem i found this issue.
There should be a case in DRF for OPTIONS request, bcoz browser does not send Authorization header with OPTIONS request.

Created another ticket (see above).

Was this page helpful?
0 / 5 - 0 ratings