Permissions

Django OAuth Toolkit provides a few utility classes to use along with other permissions in Django REST Framework, so you can easily add scoped-based permission checks to your API views.

More details on how to add custom permissions to your API Endpoints can be found at the official Django REST Framework documentation

TokenHasScope

The TokenHasScope permission class allows access only when the current access token has been authorized for all the scopes listed in the required_scopes field of the view.

For example:

class SongView(views.APIView):
    authentication_classes = [OAuth2Authentication]
    permission_classes = [TokenHasScope]
    required_scopes = ['music']

The required_scopes attribute is mandatory.

TokenHasReadWriteScope

The TokenHasReadWriteScope permission class allows access based on the READ_SCOPE and WRITE_SCOPE configured in the settings.

When the current request’s method is one of the “safe” methods GET, HEAD, OPTIONS the access is allowed only if the access token has been authorized for the READ_SCOPE scope. When the request’s method is one of POST, PUT, PATCH, DELETE the access is allowed if the access token has been authorized for the WRITE_SCOPE.

The required_scopes attribute is optional and can be used by other scopes needed in the view.

For example:

class SongView(views.APIView):
    authentication_classes = [OAuth2Authentication]
    permission_classes = [TokenHasReadWriteScope]
    required_scopes = ['music']

When a request is performed both the READ_SCOPE \ WRITE_SCOPE and ‘music’ scopes are required to be authorized for the current access token.

TokenHasResourceScope

The TokenHasResourceScope permission class allows access only when the current access token has been authorized for all the scopes listed in the required_scopes field of the view but according of request’s method.

When the current request’s method is one of the “safe” methods, the access is allowed only if the access token has been authorized for the READ_SCOPE-suffixed scope (music:read with the default READ_SCOPE of read). When the request’s method is one of “non safe” methods, the access is allowed only if the access token has been authorized for the WRITE_SCOPE-suffixed scope (music:write by default). See Resource scope syntax below for the exact rules.

class SongView(views.APIView):
    authentication_classes = [OAuth2Authentication]
    permission_classes = [TokenHasResourceScope]
    required_scopes = ['music']

The required_scopes attribute is mandatory (you just need inform the resource scope).

Resource scope syntax

TokenHasResourceScope does not check the plain required_scopes value. For each entry it appends a colon and the read/write scope name — the READ_SCOPE setting for safe methods (GET, HEAD, OPTIONS) and the WRITE_SCOPE setting for the others (POST, PUT, PATCH, DELETE) — and checks that scope instead. READ_SCOPE and WRITE_SCOPE default to read and write, so with the defaults and required_scopes = ['music'] a safe method requires music:read and an unsafe method requires music:write. A token whose scope is the bare music — without the read/write suffix — is therefore rejected, because neither music:read nor music:write is present. If you customize READ_SCOPE / WRITE_SCOPE, substitute those names for read / write throughout this section.

For the check to succeed you must both declare the suffixed scopes and issue tokens for them. When using the default settings-based scopes backend, declare each read/write scope explicitly in the SCOPES setting so it can be requested and shown on the authorization form:

OAUTH2_PROVIDER = {
    'SCOPES': {
        'music:read': 'Read your music.',
        'music:write': 'Modify your music.',
        # ...
    },
}

A token then has to be authorized for music:read and/or music:write (for example scope=music:read music:write to allow both safe and unsafe methods). Requesting only the bare music scope will not satisfy this permission class.

IsAuthenticatedOrTokenHasScope

The IsAuthenticatedOrTokenHasScope permission class allows access only when the current access token has been authorized for all the scopes listed in the required_scopes field of the view but according to the request’s method. It also allows access to Authenticated users who are authenticated in django, but were not authenticated through the OAuth2Authentication class. This allows for protection of the API using scopes, but still let’s users browse the full browsable API. To restrict users to only browse the parts of the browsable API they should be allowed to see, you can combine this with the DjangoModelPermission or the DjangoObjectPermission.

For example:

class SongView(views.APIView):
    permission_classes = [IsAuthenticatedOrTokenHasScope, DjangoModelPermission]
    required_scopes = ['music']

The required_scopes attribute is mandatory.

TokenMatchesOASRequirements

The TokenMatchesOASRequirements permission class allows the access based on a per-method basis and with alternative lists of required scopes. This permission provides full functionality required by REST API specifications like the OpenAPI Specification (OAS) security requirement object.

The required_alternate_scopes attribute is a required map keyed by HTTP method name where each value is a list of alternative lists of required scopes.

In the follow example GET requires “read” scope, POST requires either “create” scope OR “post” and “widget” scopes, etc.

class SongView(views.APIView):
    authentication_classes = [OAuth2Authentication]
    permission_classes = [TokenMatchesOASRequirements]
    required_alternate_scopes = {
        "GET": [["read"]],
        "POST": [["create"], ["post", "widget"]],
        "PUT":  [["update"], ["put", "widget"]],
        "DELETE": [["delete"], ["scope2", "scope3"]],
    }

The following is a minimal OAS declaration that shows the same required alternate scopes. It is complete enough to try it in the swagger editor.

openapi: "3.0.0"
info:
  title: songs
  version: v1
components:
  securitySchemes:
    song_auth:
      type: oauth2
      flows:
        implicit:
          authorizationUrl: http://localhost:8000/o/authorize
          scopes:
            read: read about a song
            create: create a new song
            update: update an existing song
            delete: delete a song
            post: create a new song
            widget: widget scope
            scope2: scope too
            scope3: another scope
paths:
  /songs:
    get:
      security:
        - song_auth: [read]
      responses:
        '200':
          description: A list of songs.
    post:
      security:
        - song_auth: [create]
        - song_auth: [post, widget]
      responses:
        '201':
          description: new song added
    put:
      security:
        - song_auth: [update]
        - song_auth: [put, widget]
      responses:
        '204':
          description: song updated
    delete:
      security:
        - song_auth: [delete]
        - song_auth: [scope2, scope3]
      responses:
        '200':
          description: song deleted