DMR from dj-rest-auth
Overview
Migrate a dj-rest-auth installation to django-modern-rest.
Required docs (always reference first):
Read This First
This migration is not shaped like $dmr-from-drf.
dj-rest-auth is a DRF layer on top of django-allauth. We do not
reimplement account management. django-allauth keeps owning registration,
email verification, password reset, social login, MFA, and passkeys through
its headless mode, and django-modern-rest owns your own API surface plus
the auth classes that read allauth's credentials.
Three consequences the user must accept before any code is written:
- Endpoint paths change. allauth headless serves its own
/_allauth/{client}/v1/... routes. Strict path parity is not a goal
and usually not achievable.
- Request and response payloads change. allauth has its own envelope
(
status, data, meta) and its own error format.
- Every API client must be updated. Treat this as a coordinated
frontend + backend migration, not a drop-in backend swap.
If the user cannot change clients, stop and say so. A compatibility shim
that re-serves dj-rest-auth paths and payloads is possible but is a
long-lived maintenance burden. Do not build one silently.
Migration Policy
- Default mode:
behavioral parity, not transport parity.
The same things must be possible; the wire format may differ.
- Preserve by default: which flows exist, who can call them, what
security properties they have, what lands in the database.
- Every path, payload, or status-code change is
approved drift and must be
listed explicitly. Never let a drift pass unlisted just because
the endpoint "still works".
- Never weaken a security property to make a client's life easier.
Call it out and let the user decide.
Workflow
1. Inventory the dj-rest-auth surface
- Find
dj_rest_auth in INSTALLED_APPS and its URL includes
(dj_rest_auth.urls, dj_rest_auth.registration.urls, dj_rest_auth.mfa.urls).
- Record every
REST_AUTH setting in use, they encode real requirements.
See the settings table in the local map.
- Record every overridden serializer
(
LOGIN_SERIALIZER, REGISTER_SERIALIZER, USER_DETAILS_SERIALIZER, ...).
These are where projects hide their custom business rules.
- Record which token strategy is active: DRF
authtoken, simplejwt
with headers, simplejwt with cookies, or session login.
- Record whether
django-allauth is already installed and configured,
which it usually is.
2. Inventory custom behavior (required gate)
dj-rest-auth is often customized by subclassing. Before mapping anything,
list for each overridden serializer or view:
- extra fields accepted or returned,
- extra validation,
- side effects (signals, analytics, provisioning, audit records).
Side effects are the part most likely to be lost silently.
Each one must be re-attached to an allauth signal or to your own controller.
3. Decide the target auth transport (required gate)
Pick one and record it. See the auth table in the local map.
- allauth headless session tokens ->
XSessionTokenSyncAuth
- jwt in
Authorization header -> HeaderJWTSyncAuth
- jwt in cookies ->
CookieJWTSyncAuth
- opaque DB tokens ->
HeaderTokenSyncAuth and the dmr token app
- Django session cookie ->
DjangoSessionSyncAuth
Match the existing security posture. A project on JWT_AUTH_HTTPONLY
cookies must not be silently moved to header tokens readable by JavaScript.
4. Wire allauth headless
- Add
allauth, allauth.account, allauth.headless to INSTALLED_APPS
and allauth.account.middleware.AccountMiddleware to MIDDLEWARE.
- Choose the
app or browser client deliberately:
app uses X-Session-Token, browser uses session cookies.
- Port
ACCOUNT_* settings so the account rules
(email verification, unique email, signup fields) stay the same.
- Expose allauth's endpoints in your OpenAPI schema with
external_path(),
allauth publishes its own specification.
5. Migrate flow by flow
Migrate one flow at a time, in this order, because later flows depend on
being able to log in:
- login and session
- logout
- user details
- password change
- password reset
- registration and email verification
- social login
- MFA and passkeys
For each flow: map the endpoint, port custom validation and side effects,
update the client, update tests, then run the repository's checks.
6. Rebuild what allauth does not serve
allauth headless has no user-details endpoint. rest_user_details becomes
your own Controller over your user model, with typed DTOs.
This is normal, and it is the right place for project-specific profile
fields that never belonged in an auth library.
7. Translate serializers into typed DTOs
TypedDict is the default for reusable controllers in this project.
- Split input and output DTOs when the response carries server-owned fields.
- Never return password fields, tokens the client should not see,
or full user objects where the old serializer returned a subset.
- Apply
@sensitive_post_parameters to anything accepting credentials.
- Apply
@sensitive_variables() to every method that holds credentials
or tokens in its local variables, so they never reach error reports.
Async methods need it on each coroutine, sync ones also cover their callees.
- Return
NO_STORE_HEADERS from dmr.security in @modify for any view
that issues or accepts credentials.
8. Port token issuing
- If tokens were issued as cookies (
JWT_AUTH_COOKIE), keep them as cookies
with the same flags: httponly, secure, samesite, and the refresh
cookie's path. Do not downgrade them.
dmr enforces CSRF automatically for cookie-based auth,
which replaces JWT_AUTH_COOKIE_USE_CSRF.
- Declare every issued cookie with
CookieSpec so response validation
and the OpenAPI schema stay honest.
9. Update tests
- Keep one test per flow asserting the same outcome, not the same payload:
a user exists, a session works, a password no longer authenticates.
- Add negative-path tests for every declared error response.
- Assert cookie flags explicitly when cookies carry credentials.
A missing
httponly is a silent security regression that no
happy-path test will catch.
10. Validate with repository-native entrypoints
- Run the same commands CI runs.
- Enable
validate_responses in development and testing.
11. Finish gate
Do not mark a flow done until linters pass, tests pass, the client is
updated, and the report is updated.
Translation Rules
- Account business logic belongs to allauth. Do not fork it into controllers.
- Your own domain logic belongs in your own controllers, not in auth hooks.
- Do not keep
dj_rest_auth or DRF imports in migrated modules.
- Do not keep serializer class names that encode DRF transport internals.
- Preserve security properties even when payload format drifts.
Reporting Format (required)
At each checkpoint and at completion, report in 4 sections:
preserved behavior
approved drift (paths, payloads, statuses, client changes)
security posture changes
unresolved gaps
security posture changes is separate on purpose. It must state explicitly
when cookie flags, CSRF behavior, token lifetime, or token visibility
to JavaScript changed, even if the user already approved it.
Migration Pitfalls
dj-rest-auth logout has two behaviors depending on SESSION_LOGIN and
token strategy. Verify what the deployment actually does before mapping it.
LOGOUT_ON_PASSWORD_CHANGE silently invalidates sessions.
allauth's behavior differs; check it rather than assuming.
OLD_PASSWORD_FIELD_ENABLED controls whether the current password is
required. Turning this off during migration is a security regression.
- Blindly copying
JWT_AUTH_* settings loses the cookie flags,
because they become arguments to NewCookie, not settings.
- Custom
REGISTER_SERIALIZER side effects (provisioning, invites, billing)
disappear unless re-attached to allauth signals.
simplejwt refresh-token rotation and blacklisting are not automatic
in dmr. Use the dmr jwt blocklist app if the project relied on it.
- Social login callback URLs are provider-registered. Changing the path
requires updating provider configuration too.
Output Checklist
- allauth headless is wired and serving the account flows.
- Auth transport chosen deliberately and matching the previous posture.
- User-details and any project-specific endpoints rebuilt as dmr controllers.
- All serializers replaced with typed DTOs; no
dj_rest_auth imports remain.
- Cookie flags and CSRF behavior preserved or explicitly drift-approved.
- Custom validation and side effects re-attached and tested.
- allauth endpoints represented in the OpenAPI schema.
- Response validation enabled and passing.
- Clients updated for every approved path or payload change.
- Final report emitted in the required 4-section format.
- After green CI, remove
dj-rest-auth, and djangorestframework if nothing
else needs it, from project dependencies.
1---2name: dmr-from-dj-rest-auth3description: Migrate an existing Django auth API from dj-rest-auth to django-modern-rest, moving account flows onto django-allauth headless and rebuilding the transport layer with dmr controllers and auth classes. Use when replacing dj_rest_auth login/logout/registration/password/MFA/social views and their DRF serializers.4---56# DMR from dj-rest-auth78## Overview910Migrate a `dj-rest-auth` installation to `django-modern-rest`.1112Required docs (always reference first):13- DMR LLM docs: https://django-modern-rest.readthedocs.io/llms-full.txt14- dj-rest-auth docs: https://dj-rest-auth.readthedocs.io15- allauth headless docs: https://docs.allauth.org/en/latest/headless/index.html16- Local map: [references/dj-rest-auth-to-dmr-map.md](references/dj-rest-auth-to-dmr-map.md)1718## Read This First1920This migration is **not** shaped like `$dmr-from-drf`.2122`dj-rest-auth` is a DRF layer on top of `django-allauth`. We do not23reimplement account management. `django-allauth` keeps owning registration,24email verification, password reset, social login, MFA, and passkeys through25its `headless` mode, and `django-modern-rest` owns your own API surface plus26the auth classes that read allauth's credentials.2728Three consequences the user must accept before any code is written:29301. **Endpoint paths change.** allauth headless serves its own31 `/_allauth/{client}/v1/...` routes. Strict path parity is not a goal32 and usually not achievable.332. **Request and response payloads change.** allauth has its own envelope34 (`status`, `data`, `meta`) and its own error format.353. **Every API client must be updated.** Treat this as a coordinated36 frontend + backend migration, not a drop-in backend swap.3738If the user cannot change clients, stop and say so. A compatibility shim39that re-serves `dj-rest-auth` paths and payloads is possible but is a40long-lived maintenance burden. Do not build one silently.4142## Migration Policy4344- Default mode: `behavioral parity`, not `transport parity`.45 The same things must be possible; the wire format may differ.46- Preserve by default: which flows exist, who can call them, what47 security properties they have, what lands in the database.48- Every path, payload, or status-code change is `approved drift` and must be49 listed explicitly. Never let a drift pass unlisted just because50 the endpoint "still works".51- Never weaken a security property to make a client's life easier.52 Call it out and let the user decide.5354## Workflow5556### 1. Inventory the dj-rest-auth surface5758- Find `dj_rest_auth` in `INSTALLED_APPS` and its URL includes59 (`dj_rest_auth.urls`, `dj_rest_auth.registration.urls`, `dj_rest_auth.mfa.urls`).60- Record every `REST_AUTH` setting in use, they encode real requirements.61 See the settings table in the local map.62- Record every overridden serializer63 (`LOGIN_SERIALIZER`, `REGISTER_SERIALIZER`, `USER_DETAILS_SERIALIZER`, ...).64 These are where projects hide their custom business rules.65- Record which token strategy is active: DRF `authtoken`, `simplejwt`66 with headers, `simplejwt` with cookies, or session login.67- Record whether `django-allauth` is already installed and configured,68 which it usually is.6970### 2. Inventory custom behavior (required gate)7172`dj-rest-auth` is often customized by subclassing. Before mapping anything,73list for each overridden serializer or view:74- extra fields accepted or returned,75- extra validation,76- side effects (signals, analytics, provisioning, audit records).7778Side effects are the part most likely to be lost silently.79Each one must be re-attached to an allauth signal or to your own controller.8081### 3. Decide the target auth transport (required gate)8283Pick one and record it. See the auth table in the local map.8485- allauth headless session tokens -> `XSessionTokenSyncAuth`86- jwt in `Authorization` header -> `HeaderJWTSyncAuth`87- jwt in cookies -> `CookieJWTSyncAuth`88- opaque DB tokens -> `HeaderTokenSyncAuth` and the `dmr` token app89- Django session cookie -> `DjangoSessionSyncAuth`9091Match the existing security posture. A project on `JWT_AUTH_HTTPONLY`92cookies must not be silently moved to header tokens readable by JavaScript.9394### 4. Wire allauth headless9596- Add `allauth`, `allauth.account`, `allauth.headless` to `INSTALLED_APPS`97 and `allauth.account.middleware.AccountMiddleware` to `MIDDLEWARE`.98- Choose the `app` or `browser` client deliberately:99 `app` uses `X-Session-Token`, `browser` uses session cookies.100- Port `ACCOUNT_*` settings so the account rules101 (email verification, unique email, signup fields) stay the same.102- Expose allauth's endpoints in your OpenAPI schema with `external_path()`,103 allauth publishes its own specification.104105### 5. Migrate flow by flow106107Migrate one flow at a time, in this order, because later flows depend on108being able to log in:1091101. login and session1112. logout1123. user details1134. password change1145. password reset1156. registration and email verification1167. social login1178. MFA and passkeys118119For each flow: map the endpoint, port custom validation and side effects,120update the client, update tests, then run the repository's checks.121122### 6. Rebuild what allauth does not serve123124allauth headless has no user-details endpoint. `rest_user_details` becomes125your own `Controller` over your user model, with typed DTOs.126This is normal, and it is the right place for project-specific profile127fields that never belonged in an auth library.128129### 7. Translate serializers into typed DTOs130131- `TypedDict` is the default for reusable controllers in this project.132- Split input and output DTOs when the response carries server-owned fields.133- Never return password fields, tokens the client should not see,134 or full user objects where the old serializer returned a subset.135- Apply `@sensitive_post_parameters` to anything accepting credentials.136- Apply `@sensitive_variables()` to every method that holds credentials137 or tokens in its local variables, so they never reach error reports.138 Async methods need it on each coroutine, sync ones also cover their callees.139- Return `NO_STORE_HEADERS` from `dmr.security` in `@modify` for any view140 that issues or accepts credentials.141142### 8. Port token issuing143144- If tokens were issued as cookies (`JWT_AUTH_COOKIE`), keep them as cookies145 with the same flags: `httponly`, `secure`, `samesite`, and the refresh146 cookie's `path`. Do not downgrade them.147- `dmr` enforces CSRF automatically for cookie-based auth,148 which replaces `JWT_AUTH_COOKIE_USE_CSRF`.149- Declare every issued cookie with `CookieSpec` so response validation150 and the OpenAPI schema stay honest.151152### 9. Update tests153154- Keep one test per flow asserting the same *outcome*, not the same payload:155 a user exists, a session works, a password no longer authenticates.156- Add negative-path tests for every declared error response.157- Assert cookie flags explicitly when cookies carry credentials.158 A missing `httponly` is a silent security regression that no159 happy-path test will catch.160161### 10. Validate with repository-native entrypoints162163- Run the same commands CI runs.164- Enable `validate_responses` in development and testing.165166### 11. Finish gate167168Do not mark a flow done until linters pass, tests pass, the client is169updated, and the report is updated.170171## Translation Rules172173- Account business logic belongs to allauth. Do not fork it into controllers.174- Your own domain logic belongs in your own controllers, not in auth hooks.175- Do not keep `dj_rest_auth` or DRF imports in migrated modules.176- Do not keep serializer class names that encode DRF transport internals.177- Preserve security properties even when payload format drifts.178179## Reporting Format (required)180181At each checkpoint and at completion, report in 4 sections:1821831. `preserved behavior`1842. `approved drift` (paths, payloads, statuses, client changes)1853. `security posture changes`1864. `unresolved gaps`187188`security posture changes` is separate on purpose. It must state explicitly189when cookie flags, CSRF behavior, token lifetime, or token visibility190to JavaScript changed, even if the user already approved it.191192## Migration Pitfalls193194- `dj-rest-auth` logout has two behaviors depending on `SESSION_LOGIN` and195 token strategy. Verify what the deployment actually does before mapping it.196- `LOGOUT_ON_PASSWORD_CHANGE` silently invalidates sessions.197 allauth's behavior differs; check it rather than assuming.198- `OLD_PASSWORD_FIELD_ENABLED` controls whether the current password is199 required. Turning this off during migration is a security regression.200- Blindly copying `JWT_AUTH_*` settings loses the cookie flags,201 because they become arguments to `NewCookie`, not settings.202- Custom `REGISTER_SERIALIZER` side effects (provisioning, invites, billing)203 disappear unless re-attached to allauth signals.204- `simplejwt` refresh-token rotation and blacklisting are not automatic205 in `dmr`. Use the `dmr` jwt blocklist app if the project relied on it.206- Social login callback URLs are provider-registered. Changing the path207 requires updating provider configuration too.208209## Output Checklist210211- allauth headless is wired and serving the account flows.212- Auth transport chosen deliberately and matching the previous posture.213- User-details and any project-specific endpoints rebuilt as dmr controllers.214- All serializers replaced with typed DTOs; no `dj_rest_auth` imports remain.215- Cookie flags and CSRF behavior preserved or explicitly drift-approved.216- Custom validation and side effects re-attached and tested.217- allauth endpoints represented in the OpenAPI schema.218- Response validation enabled and passing.219- Clients updated for every approved path or payload change.220- Final report emitted in the required 4-section format.221- After green CI, remove `dj-rest-auth`, and `djangorestframework` if nothing222 else needs it, from project dependencies.