Skip to content

Django Rest Framework Backend Integration

PropelAuth offers an end-to-end managed user authentication library for seamless integration into your Django application. This guide will show you how to connect the PropelAuth library to your backend. For the full Django Rest Framework Library Reference, click here.

Quick Example

The following example creates a view that logged-in users can only access. It’s derived from Django Rest Framework’s tutorial, where the Snippet models and Serializers originated.

from propelauth_django_rest_framework import init_auth
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import permissions

auth = init_auth("YOUR_AUTH_URL", #(1)
                 "YOUR_API_KEY") #(2)

class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [auth.IsUserAuthenticated] #(3)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.propelauth_user.user_id) #(4)
  1. The base URL is where your authentication pages are hosted. You can find your Auth URL under the Backend Integration section for your project at https://app.propelauth.com.
  2. You can manage your api keys under the Backend Integration section for your project.
  3. This permission verifies the user is logged in and has passed in a valid access token.
  4. The permission also sets request.propelauth_user which has a user_id.

How it works

The front-end receives an access token. This access token is provided in an Authorization header when an HTTP request is made.

PropelAuth provides you with metadata that you use to validate the access token and figure out who it belongs to. The complexity of fetching the metadata and validating the tokens is hidden in the Django Rest Framework library.

Installation

In your app, install the propelauth-django-rest-framework library.

$ pip install propelauth-django-rest-framework

Initialize

init_auth performs a one-time initialization of the library. It will verify your api_key is correct and fetch the metadata needed to verify access tokens in IsUserAuthenticated, AllowAny, and IsUserInOrg.

from propelauth_django_rest_framework import init_auth

auth = init_auth("YOUR_AUTH_URL", #(1)
                 "YOUR_API_KEY") #(2)
  1. The base URL where your authentication pages are hosted. You can find this under the Backend Integration section for your project at https://app.propelauth.com.
  2. You can manage your api keys under the Backend Integration section for your project.

Protect API routes

IsUserAuthenticated

A permission that will verify the request was made by a valid user. Specifically, it will:

  1. Check that a valid access token was provided. If not, the request is rejected with a 401 status code.
  2. Set request.propelauth_user with the user's information.
from propelauth_django_rest_framework import init_auth

auth = init_auth("YOUR_AUTH_URL", "YOUR_API_KEY")

@api_view(['GET'])
@permission_classes([auth.IsUserAuthenticated])
def whoami(request):
   return HttpResponse(request.propelauth_user.user_id)

or you can use it in class-based views:

class WhoAmIView(APIView):
   permission_classes = [auth.IsUserAuthenticated]

   def get(self, request):
      return HttpResponse(request.propelauth_user.user_id)

AllowAny

Similar to IsUserAuthenticated, except if an access token is missing or invalid, the request is allowed to continue, but request.propelauth_user will be None.

Note that this will still set request.propelauth_user if a valid access token is provided.

class OptionalUserView(APIView):
    permission_classes = [auth.AllowAny]

    def get(self, request):
        if request.propelauth_user is None:
            return HttpResponse("none")
        return HttpResponse(request.propelauth_user.user_id)

request.propelauth_user

A per-request value that contains user information for the user making the request. It's set by one of IsUserAuthenticated, AllowAny, or any organization checking like IsUserInOrg.

Property Description
user_id The id of the user
org_id_to_org_member_info A dictionary of org ids to metadata about the org. Includes all orgs that the user is in
legacy_user_id The original ID of the user, if the user was migrated from an external source

The values of org_id_to_org_member_info are OrgMemberInfo's, with the following fields/functions:

OrgMemberInfo fields/properties Description
org_id The id of the org
org_name The name of the org
user_assigned_role The user's role within the organization. See Roles and Permissions for more details.
user_is_role(role: str): bool Returns True if the user's role within the organization matches the role passed in
user_is_at_least_role(role: str): bool Returns True if the user's role within the organization is at least the role passed in. If the hierarchy of roles is Owner => Admin => Member, specifying "Admin" will return True for Admins and Owners, but False for Members.
user_has_permission(permission: str): bool Return True if the user has a specific permission. The users' permissions are derived from their role within this organization.
user_has_all_permissions(permissions: List[str]): bool Return True if the user has all the specified permissions. The users' permissions are derived from their role within this organization.

IsUserInOrg

A function that returns a Django permission that will verify the request was made by a valid user that belongs to a specific organization.

IsUserInOrg Argument Description
req_to_org_id A function that takes in the request and outputs the org_id to require the user to be a member of. If none is specified, it will check for a path param org_id by checking request.GET.get('org_id', '').
@api_view(['GET'])
@permission_classes([auth.IsUserInOrg()])
def org_membership(request):
   message = f"You are in {request.propelauth_org.org_name}"
   return HttpResponse(message)

Specifically, it will:

  1. Check that a valid access token was provided. If not, the request is rejected with a 401 status code.
  2. Set request.propelauth_user with the user's information.
  3. Find an id for an organization within the request. By default, it will check for a path parameter org_id.
  4. Check that the user is a member of that organization. If not, the request is rejected with a 403 status code.
  5. Set request.propelauth_org (scoped to the current request) with the organization's information for this user.

Roles and Permissions

A user has a Role within an organization. By default, the available roles are Owner, Admin, or Member, but these can be configured. These roles are also hierarchical, so Owner > Admin > Member.

Roles allow you to control what different users can do within your product. If you want to check a user's role, you can use auth.IsUserInOrgWithRole or auth.IsUserInOrgWithMinimumRole.

## Assuming a Role structure of Owner => Admin => Member

@api_view(['GET'])
@permission_classes([auth.IsUserInOrgWithRole("Admin")])
def admin_only(request):
   message = f"You are an Admin of {request.propelauth_org.org_name}"
   return HttpResponse(message)

@api_view(['GET'])
@permission_classes([auth.IsUserInOrgWithMinimumRole("Admin")])
def admin_or_owner(request):
   message = f"You are an Admin or Owner of {request.propelauth_org.org_name}"
   return HttpResponse(message)

Permissions are arbitrary strings associated with a role. For example, can_view_billing, ProductA::CanCreate, and ReadOnly are all valid permissions. The PropelAuth dashboard allows you to set up these permissions.

You can use auth.IsUserInOrgWithPermission or auth.IsUserInOrgWithAllPermissions to check for a given permission.

@api_view(['GET'])
@permission_classes([auth.IsUserInOrgWithPermission("can_view_billing")])
def billing_req(request):
   pass

All of these functions, just like IsUserInOrg, will also take in req_to_org_id, and will set both request.propelauth_user and request.propelauth_org on a valid request.

request.propelauth_org

A per-request value that contains org information for the user making the request.

Property Description
org_id The id of the org
org_name The name of the org
user_assigned_role The user's role within the organization. See Roles and Permissions for more details.
user_is_role(role: str): bool Returns True if the user's role within the organization matches the role passed in
user_is_at_least_role(role: str): bool Returns True if the user's role within the organization is at least the role passed in. If the hierarchy of roles is Owner => Admin => Member, specifying "Admin" will return True for Admins and Owners, but False for Members.
user_has_permission(permission: str): bool Return True if the user has a specific permission. The users' permissions are derived from their role within this organization.
user_has_all_permissions(permissions: List[str]): bool Return True if the user has all the specified permissions. The users' permissions are derived from their role within this organization.

API calls

In addition to protecting API routes, you can make requests to PropelAuth to fetch more information about your users or organizations. You can also create new users, update user metadata, and more.

See Django Rest Framework Library Reference for more information on available functionality.

Next Steps

Done with your backend? Next you can deploy to production.