DRF And React By Schema - OAuth
OAuth 2.1 authorization server + resource server for projects built with DRF And React By Schema (DRBS).
It turns any DRBS Django/DRF project into a standard OAuth 2.1 provider — with PKCE and Dynamic Client Registration (RFC 7591) — so MCP clients such as opencode (and, later, the SPA/mobile app) can authenticate through a server-rendered Django login/consent page, with no React changes.
About the suite
drf-react-by-schema is a suite that builds headless Django + React applications directly from your models:
drf-react-by-schema-django (PyPI) — generates the DRF API (serializers, viewsets, filters, schemas) from your models;
drf-react-by-schema-react (npm) — consumes those schemas to build the React forms, grids and CRUD interfaces;
drf-react-by-schema-oauth (this package) — adds the authorization layer so clients can obtain and use OAuth 2.1 tokens against the same API;
drf-react-by-schema-mcp — a generic, schema-driven MCP server that exposes a DRBS project to MCP clients, authorized by this package’s tokens.
About this package
DRBS projects expose a rich DRF API generated from the Django models. This package adds the missing authorization layer on top of django-oauth-toolkit (DOT):
an OAuth 2.1 authorization server (authorization code + PKCE S256, refresh token);
Dynamic Client Registration (RFC 7591) so opencode mcp auth <sistema> works with no manual client setup;
Authorization Server Metadata (RFC 8414) and a helper to build Protected Resource Metadata (RFC 9728; served by the MCP package);
a DRF authentication class and read/write scope permissions that authenticate both /mcp and /api/... with the same Bearer token;
server-rendered login/consent templates that projects can override.
It is additive and opt-in: it does not replace Django auth or SimpleJWT, and requires no SPA work.
Compatibility
The base DRBS package supports Django ≥ 3.2, and some deployments are still on Django 3.2. Newer django-oauth-toolkit (3.x) requires Django ≥ 4.2, so this package pins the 2.x DOT line (django-oauth-toolkit>=2.4,<3.0), which supports Django 3.2 through 5.x. (The suite’s roadmap is to move consumer apps to Django 5, at which point the DOT pin can be relaxed.)
Pre-requisites
Django ≥ 3.2
djangorestframework
django-oauth-toolkit (2.x, installed automatically)
Quick start
Install:
pip install drf-react-by-schema-oauthAdd the DOT app and this package to INSTALLED_APPS (order matters — oauth2_provider first):
INSTALLED_APPS = [ ... 'oauth2_provider', 'drf_react_by_schema_oauth', ]Include the URLs:
from django.urls import include, path urlpatterns = [ ... path('', include('drf_react_by_schema_oauth.urls')), ]Migrate (creates DOT’s token/application tables):
python manage.py migrate
That is the whole consumer contract: two apps, one include(), one migrate.
Accepting OAuth tokens on your API
To let the same Bearer token authenticate /api/... (used by the SPA via JWT and by MCP via OAuth), add the authentication class to your DRF settings or per viewset:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# OAuth *first*: SimpleJWT raises AuthenticationFailed on a non-JWT
# Bearer (e.g. an OAuth access token) and would stop the chain before
# OAuth is tried. OAuth2Authentication returns None (not an error) for
# non-OAuth tokens, so JWT/session still resolve afterwards.
'drf_react_by_schema_oauth.authentication.OAuth2Authentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
...
}
Then add HasScope where you want OAuth tokens to be scope-limited (e.g. a read-only token cannot write). It is composable: requests authenticated by session/JWT are left to your own permissions, only OAuth tokens are scope-checked.
from rest_framework.permissions import IsAuthenticated
from drf_react_by_schema_oauth.permissions import HasScope
class MyViewSet(viewsets.ModelViewSet):
authentication_classes = [
OAuth2Authentication, # first, for the reason above
JWTAuthentication,
SessionAuthentication,
]
permission_classes = [IsAuthenticated, HasScope]
A view can demand an explicit set instead of the default method mapping:
class ReportView(APIView):
required_scopes = ['read']
permission_classes = [IsAuthenticated, HasScope]
Settings
All settings live in a single optional DRBS_OAUTH dict:
DRBS_OAUTH = {
# Canonical public base URL of the authorization server (RFC 8414
# `issuer`). Set it in production; when unset it is derived from the
# request origin.
'ISSUER': 'https://auth.example.org',
# Scopes advertised, accepted and shown on the consent page (name ->
# description). This is the single source of truth: the package feeds
# these into DOT's consent rendering, so you do not need to duplicate
# them in OAUTH2_PROVIDER['SCOPES'].
'SCOPES': {
'read': 'Read access to the API.',
'write': 'Write access to the API.',
},
# Dynamic Client Registration.
'DCR_ENABLED': True,
'DCR_ALLOWED_REDIRECTS': [ # scheme+host prefixes; loopback by default
'http://127.0.0.1',
'http://localhost',
'http://[::1]',
],
# Optional template overrides (see "Templates" below).
'LOGIN_TEMPLATE': None, # defaults to "drbs_oauth/login.html"
'CONSENT_TEMPLATE': None, # defaults to "drbs_oauth/authorize.html"
# Reserved.
'AUDIENCE': None,
'ACCESS_TOKEN_TTL': None,
'REFRESH_TOKEN_TTL': None,
}
Endpoints
/.well-known/oauth-authorization-server — Authorization Server Metadata (RFC 8414)
/o/authorize/ — authorization endpoint (login + consent)
/o/token/ — token endpoint
/o/revoke_token/ — token revocation (RFC 7009)
/o/introspect/ — token introspection (RFC 7662)
/o/register/ — Dynamic Client Registration (RFC 7591)
/o/login/ — server-rendered login page
Scopes
Two scopes are defined and advertised:
read — safe methods (GET/HEAD/OPTIONS/TRACE);
write — unsafe methods (POST/PUT/PATCH/DELETE).
The names are also exported as constants (drf_react_by_schema_oauth.SCOPE_READ / SCOPE_WRITE).
Templates
The login and consent pages are plain server-rendered HTML and require no build step. Override them either by pointing DRBS_OAUTH at your own template names (LOGIN_TEMPLATE / CONSENT_TEMPLATE) or by shadowing drbs_oauth/login.html / drbs_oauth/authorize.html from a project template directory.
Using with MCP (opencode)
Once a project is deployed with this package (and the MCP package mounts /mcp), an opencode user authorizes with:
opencode mcp auth <sistema>
opencode:
discovers the authorization server (via the resource server’s Protected Resource Metadata and/or /.well-known/oauth-authorization-server);
registers a client dynamically at /o/register/;
opens the Django-rendered login/consent page in the browser;
stores a per-user access/refresh token and calls /mcp with it.
No credential is ever centralized: each system authorizes its own users and keeps Django as the permission authority.
Security notes
Dynamic client registration creates public clients only (no client secret), and redirect URIs are restricted to loopback by default (RFC 8252); plain http is allowed only for loopback hosts.
Access and refresh tokens are stored by DOT as its own hashed records — this package introduces no additional secret store.
Set DRBS_OAUTH['ISSUER'] in production so the advertised issuer does not depend on the incoming Host header.
Introspection is protected by DOT and requires a Bearer token carrying the introspection scope.
License
GNU General Public License v3 — see LICENSE.
Metadata
Release files for drf-react-by-schema-oauth 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| drf_react_by_schema_oauth-0.1.2.tar.gz | 595.2 kB | Details |
Release files / drf_react_by_schema_oauth-0.1.2.tar.gz
| Download URL | drf_react_by_schema_oauth-0.1.2.tar.gz |
|---|---|
| Size | 595.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9b375ca141a07e4cc5096f7729fa142491905377f32aded2092a2f45108cb3ce
|
|
BLAKE2b-256 checksum How to use checksums |
acd8f2a646394939129494ff5def509fa33b5aa712bf83b4416678ade2b62db1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.0.0 CPython/3.12.3
|