Open-source project
django-oauth/django-oauth-toolkit avatar
django-oauth/django-oauth-toolkit

Django OAuth Toolkit turned on PKCE by default, and its Docker image allows every host

OAuth2 goodies for the Djangonauts!

3,340 stars862 forksPythonNOASSERTION

At a glance

What is it?
Django OAuth Toolkit is an OAuth 2.0 authorization server you run inside a Django project. Version 3.4.0 added the MCP authorization server role with PKCE required by default, and the container image it publishes sets a wildcard allowed-hosts list.
Who is it for?
Django OAuth Toolkit is the right shape for a team that already runs Django and wants to issue and manage tokens from its own application rather than run a separate authorization service, and version 3.4.0 is the version to read about, since it is where PKCE became the default and the metadata discovery endpoints entered the default URLconf. Three checks before you deploy it.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

PKCE became the default in 3.4.0, two features stayed opt-in

The most recent series of releases is where the interesting behaviour change sits. From 3.4.0 the package supports the authorization server role that the Model Context Protocol authorization specification requires, and it does so with PKCE required by default rather than available on request. Two discovery endpoints are included in the default URLconf as part of that work: authorization server metadata under RFC 8414, and protected resource metadata under RFC 9728. Two related capabilities are not on by default. Dynamic Client Registration, named with the RFC 7591 and 7592 references, is enabled by a setting called DCR_ENABLED, and Client ID Metadata Documents by CIMD_ENABLED. The README points at the 3.4.0 release discussion as the place that lists which specifications are supported and where the current gaps are, which is the honest place to look before promising compliance with a client's expectations.

The README's oauthlib floor is lower than the manifest's

The requirements section of the README lists Python 3.10 through 3.14, Django 4.2 through 6.0 in its individual releases, and oauthlib 3.2.2 or newer. The packaging metadata asks for more. Its dependency list pins django at 4.2 or newer, requests at 2.13.0 or newer, urllib3 at 1.26 or newer, jwcrypto at 1.5.0 or newer, and oauthlib at 3.3.0 or newer. So a resolver reading the metadata will refuse an environment that satisfies the README, and anyone pinning oauthlib to 3.2.2 on the strength of the prose will find the install does not behave as documented. The library's own claim is that it makes extensive use of OAuthLib so that the implementation stays compliant with RFC 6749, which makes the floor more than a formality.

The image allows every host, and that default ships

The Dockerfile that builds the published identity provider image sets its defaults in the runtime stage. Debug mode is off, and ALLOWED_HOSTS is the single character wildcard, which is Django's way of accepting any Host header. The database is a SQLite file at a path inside a mounted volume, templates are read from a directory that can be overridden by mounting over it, and static files are collected during the build and served from inside the image. Those are reasonable defaults for a container meant to be run locally or behind your own proxy, and unreasonable ones for a container exposed to a network, so the setting to change first is the host list. The image also carries an ARG for a git commit, which is used both in the org.opencontainers.image labels and in a Sentry release environment variable.

The image is built from the repository on purpose

A comment at the top of the Dockerfile explains why that file sits at the repository root rather than in a subdirectory: the build context has to include the oauth2_provider package, because the test identity provider application depends on it, so the images are built with the source from the repository in order to validate it before packages are published. The build is two stages on a slim Python base. The first installs compilers, headers, git and a couple of development libraries, upgrades pip and setuptools for the PEP 517 support that gevent needs, copies the tree into a code directory, moves the working directory into the test application, installs that application's requirements plus gunicorn, and runs collectstatic. The second stage copies only the virtual environment out of the builder, so the build tools never reach the final image.

Six compose files, one per database, plus an ordered startup

The repository root carries six compose files: a default one, and variants for MySQL, Postgres and Oracle, each with a second file whose name adds a -pr suffix. That pairing is the test matrix: the same services run against each supported database engine and against the pull request variant of it. The default compose file wires two applications. The identity provider is built from the root Dockerfile, and two helper services run before it starts: one executes the migrate command, the other loads a seed fixture, and both are expressed as dependencies that must complete successfully before the identity provider is allowed to start. The resource server is built from a directory inside the tests folder and published on a different port. The comments there record a trap worth knowing about, that the working directory inside the image must stay where it is because changing it breaks the import of the identity provider package.

A .env file is committed at the repository root

The top level of the repository contains thirty-three entries, and one of them is a .env file sitting next to a .gitignore. A committed environment file and an ignore file are contradictory signals in the same directory listing, and because the file's contents are not part of what this repository's page shows, the safest reading is that it holds values for local development rather than secrets, and the safest practice is to read it before assuming anything about the defaults the project ships. The rest of the root is ordinary and unusually complete for a library: an rfcs directory that holds the specifications the project tracks, an AUTHORS file, a changelog, a code of conduct, a contribution guide, a documentation build with its own configuration, tox for the test matrix, a coverage configuration, an editor configuration, a pre-commit configuration, and two agent-related directories.

Documentation extras pin Sphinx while everything else floats

The manifest declares three optional dependency groups and the difference between them is instructive. The dev group is the docs group plus the test group plus a formatter and linter, and the test group is the same set of testing tools: two web frameworks for integration tests, coverage, pytest with its Django plugin, a distribution plugin and a mocking plugin. The docs group contains three entries, and two of them are exact pins rather than lower bounds: the documentation builder at one specific version and its theme at another. Everything else in the manifest, including the runtime dependencies, uses lower bounds. The result is a documentation toolchain that will not move on its own, which for a project whose documentation is its main user-facing artefact is a defensible choice and a common source of build failures when a new Sphinx release lands.

Security reports have a private route and a fallback address

The README gives security issues their own section, and it is unusually specific. Report through GitHub's private vulnerability reporting form and follow the repository's security policy, and do not file a public issue or a pull request for an undisclosed vulnerability. If private reporting is not available, the fallback is an email address for the Django OAuth security team, hosted on a Google Groups list rather than on a project domain. The rest of the project's contribution guidance follows the same pattern of being explicit about process: anyone can open an issue, a pull request or a review, the project asks for help and points at issues labelled for it, and any pull request requires an independent review before it can be merged, which is the stated reason second pairs of eyes are valued.

Editorial conclusion

Django OAuth Toolkit is the right shape for a team that already runs Django and wants to issue and manage tokens from its own application rather than run a separate authorization service, and version 3.4.0 is the version to read about, since it is where PKCE became the default and the metadata discovery endpoints entered the default URLconf. Three checks before you deploy it. The two newer client registration features are off unless you set DCR_ENABLED and CIMD_ENABLED, so confirm which of them you actually need. The published container image sets ALLOWED_HOSTS to a wildcard, so do not run it with its defaults on anything reachable from a network. And the stated oauthlib floor differs between the README and the manifest, so let the resolver decide rather than pinning to the lower number.

Frequently asked questions

What does django-oauth-toolkit do inside a Django project?

It provides the endpoints, models and logic to issue and manage OAuth2 tokens from an existing project, as an authorization server rather than a separate service. You add oauth2_provider to INSTALLED_APPS and include its urls under a prefix of your choice. It can also act as a resource server for a Django or DRF API.

Which Python and Django versions does django-oauth-toolkit support?

Python 3.10 to 3.14 and Django 4.2, 5.0, 5.1, 5.2 and 6.0, stated in both the requirements list and the classifiers. Note that the README asks for oauthlib 3.2.2 or newer while the packaging metadata requires 3.3.0 or newer.

What did django-oauth-toolkit 3.4.0 add?

Support for the authorization server role required by the Model Context Protocol authorization specification, with PKCE required by default and the RFC 8414 authorization server metadata and RFC 9728 protected resource metadata discovery endpoints included in the default URLconf. Dynamic client registration and client ID metadata documents remain behind the DCR_ENABLED and CIMD_ENABLED settings.

How do I report a security issue in django-oauth-toolkit?

Use GitHub's private vulnerability reporting form and follow the repository security policy, and do not open a public issue or pull request for an undisclosed vulnerability. The stated fallback is an email address for the Django OAuth security team.

What does the django-oauth-toolkit test setup run against?

Compose files for MySQL, Postgres and Oracle alongside the default configuration, each with a second variant whose name ends in -pr. The identity provider runs migrations and loads a seed fixture as ordered dependencies before it starts, and a resource server application is built from inside the tests directory.

Official sources

  1. django-oauth/django-oauth-toolkit on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/django-oauth-django-oauth-toolkit.svg)](https://hysenlabs.com/projects/django-oauth-django-oauth-toolkit)