OAuth2 Provider for Django web applications with complete server functionality, token management, and authorization endpoints.
—
Django OAuth Toolkit provides complete OAuth2 server endpoints implementing RFC 6749 and related specifications. These views handle authorization, token issuance, revocation, and introspection with support for all standard OAuth2 flows.
OAuth2 authorization endpoint that handles authorization requests and user consent.
class AuthorizationView(BaseAuthorizationView):
"""
OAuth2 authorization endpoint (/o/authorize/).
Handles authorization requests for all grant types:
- Authorization Code Grant
- Implicit Grant
- OpenID Connect Hybrid Flow
Methods:
GET: Display authorization form to user
POST: Process user authorization decision
Query Parameters:
response_type: 'code', 'token', or 'id_token' combinations
client_id: OAuth2 client identifier
redirect_uri: Where to redirect after authorization
scope: Requested OAuth2 scopes
state: Client state parameter
nonce: OIDC nonce for ID token
code_challenge: PKCE code challenge
code_challenge_method: PKCE challenge method ('plain' or 'S256')
claims: OIDC claims parameter
Returns:
Authorization form or redirect to client with code/token
"""
template_name = "oauth2_provider/authorize.html"
form_class = AllowForm
def get(self, request, *args, **kwargs):
"""Display authorization form"""
def post(self, request, *args, **kwargs):
"""Process authorization decision"""OAuth2 token endpoint for issuing and refreshing access tokens.
class TokenView(BaseTokenView):
"""
OAuth2 token endpoint (/o/token/).
Handles token requests for all grant types:
- Authorization Code Grant
- Resource Owner Password Credentials Grant
- Client Credentials Grant
- Refresh Token Grant
Methods:
POST: Issue or refresh access tokens
Form Parameters:
grant_type: Type of grant being used
code: Authorization code (authorization_code grant)
redirect_uri: Redirect URI used in authorization
username: Resource owner username (password grant)
password: Resource owner password (password grant)
refresh_token: Refresh token (refresh_token grant)
scope: Requested scopes
client_id: OAuth2 client identifier
client_secret: OAuth2 client secret
code_verifier: PKCE code verifier
Returns:
JSON response with access_token, token_type, expires_in, refresh_token, scope
"""
def post(self, request, *args, **kwargs):
"""Process token request and return JSON response"""OAuth2 token revocation endpoint implementing RFC 7009.
class RevokeTokenView(BaseRevokeTokenView):
"""
OAuth2 token revocation endpoint (/o/revoke_token/).
Revokes access tokens and refresh tokens as per RFC 7009.
Methods:
POST: Revoke specified token
Form Parameters:
token: The token to revoke (access or refresh token)
token_type_hint: Hint about token type ('access_token' or 'refresh_token')
client_id: OAuth2 client identifier
client_secret: OAuth2 client secret (for confidential clients)
Returns:
HTTP 200 on successful revocation
HTTP 400 for invalid requests
"""
def post(self, request, *args, **kwargs):
"""Revoke the specified token"""OAuth2 token introspection endpoint implementing RFC 7662.
class IntrospectTokenView(BaseIntrospectTokenView):
"""
OAuth2 token introspection endpoint (/o/introspect/).
Provides metadata about OAuth2 tokens as per RFC 7662.
Methods:
POST: Return token metadata
Form Parameters:
token: The token to introspect
token_type_hint: Hint about token type
client_id: OAuth2 client identifier
client_secret: OAuth2 client secret
Returns:
JSON response with token metadata:
- active: Boolean indicating if token is active
- scope: Space-separated list of scopes
- client_id: Client identifier
- username: Resource owner username
- token_type: Type of token
- exp: Expiration timestamp
- iat: Issued at timestamp
- sub: Subject identifier
- aud: Audience
- iss: Issuer
"""
def post(self, request, *args, **kwargs):
"""Return token introspection data"""Core OAuth2 endpoint URL patterns that should be included in your Django URL configuration.
base_urlpatterns = [
path("authorize/", views.AuthorizationView.as_view(), name="authorize"),
path("token/", views.TokenView.as_view(), name="token"),
path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"),
path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"),
]# urls.py
from django.urls import path, include
urlpatterns = [
# Include OAuth2 URLs
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
# Your app URLs
path('api/', include('myapp.urls')),
]# Client initiates authorization request
# GET /o/authorize/?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=read+write&state=STATE
# After user authorization, client receives:
# HTTP/1.1 302 Found
# Location: REDIRECT_URI?code=AUTHORIZATION_CODE&state=STATE
# Client exchanges code for token
# POST /o/token/
# Content-Type: application/x-www-form-urlencoded
#
# grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
# Response:
# {
# "access_token": "ACCESS_TOKEN",
# "token_type": "Bearer",
# "expires_in": 3600,
# "refresh_token": "REFRESH_TOKEN",
# "scope": "read write"
# }# Server-to-server authentication
# POST /o/token/
# Content-Type: application/x-www-form-urlencoded
# Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)
#
# grant_type=client_credentials&scope=api
# Response:
# {
# "access_token": "ACCESS_TOKEN",
# "token_type": "Bearer",
# "expires_in": 3600,
# "scope": "api"
# }# Direct username/password authentication (use carefully)
# POST /o/token/
# Content-Type: application/x-www-form-urlencoded
#
# grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&scope=read+write
# Response:
# {
# "access_token": "ACCESS_TOKEN",
# "token_type": "Bearer",
# "expires_in": 3600,
# "refresh_token": "REFRESH_TOKEN",
# "scope": "read write"
# }# Refresh expired access token
# POST /o/token/
# Content-Type: application/x-www-form-urlencoded
#
# grant_type=refresh_token&refresh_token=REFRESH_TOKEN&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
# Response:
# {
# "access_token": "NEW_ACCESS_TOKEN",
# "token_type": "Bearer",
# "expires_in": 3600,
# "refresh_token": "NEW_REFRESH_TOKEN",
# "scope": "read write"
# }# Revoke access token
# POST /o/revoke_token/
# Content-Type: application/x-www-form-urlencoded
# Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)
#
# token=ACCESS_TOKEN&token_type_hint=access_token
# Response: HTTP 200 OK (empty body)
# Revoke refresh token (also revokes associated access tokens)
# POST /o/revoke_token/
# Content-Type: application/x-www-form-urlencoded
#
# token=REFRESH_TOKEN&token_type_hint=refresh_token&client_id=CLIENT_ID&client_secret=CLIENT_SECRET# Introspect token for metadata
# POST /o/introspect/
# Content-Type: application/x-www-form-urlencoded
# Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)
#
# token=ACCESS_TOKEN
# Active token response:
# {
# "active": true,
# "scope": "read write",
# "client_id": "client123",
# "username": "user@example.com",
# "token_type": "Bearer",
# "exp": 1640995200,
# "iat": 1640991600,
# "sub": "user@example.com",
# "aud": "client123"
# }
# Inactive token response:
# {
# "active": false
# }# Authorization request with PKCE
# GET /o/authorize/?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=read&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
# Token exchange with code verifier
# POST /o/token/
# Content-Type: application/x-www-form-urlencoded
#
# grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&code_verifier=CODE_VERIFIERfrom oauth2_provider.views.base import BaseAuthorizationView
from django.shortcuts import render
class CustomAuthorizationView(BaseAuthorizationView):
"""Custom authorization view with custom template"""
template_name = "myapp/custom_authorize.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Add custom context
context['custom_data'] = 'Custom authorization page'
return context
# Use in URL configuration
# path('custom/authorize/', CustomAuthorizationView.as_view(), name='custom_authorize')OAuth2 endpoints return standard error responses following RFC 6749:
# Authorization endpoint errors (redirect to client):
# error=invalid_request&error_description=Missing+client_id+parameter
# error=unauthorized_client&error_description=Client+not+authorized
# error=access_denied&error_description=User+denied+authorization
# error=unsupported_response_type&error_description=Response+type+not+supported
# error=invalid_scope&error_description=Requested+scope+invalid
# error=server_error&error_description=Server+encountered+error
# Token endpoint errors (JSON response):
# {
# "error": "invalid_request",
# "error_description": "Missing grant_type parameter"
# }
#
# {
# "error": "invalid_client",
# "error_description": "Client authentication failed"
# }
#
# {
# "error": "invalid_grant",
# "error_description": "Authorization code has expired"
# }
#
# {
# "error": "unsupported_grant_type",
# "error_description": "Grant type not supported"
# }Install with Tessl CLI
npx tessl i tessl/pypi-django-oauth-toolkit