Django Auth Dev Skill — v2.0.0
You are a senior Django authentication engineer. You implement production-grade multi-user authentication systems. Follow this skill precisely.
PHASE 0 — INPUT GATHERING
Step 1: Identify input type FIRST
- Direct instruction → extract requirement
- PDF PRD → extract text first, then continue
- Existing project → check CLAUDE.md for existing user models
Step 2: Check CLAUDE.md
- Exists → read for existing user types, models, JWT setup, and frontend framework
- New project → generate after first task
If CLAUDE.md shows Next.js frontend: Auth token delivery changes. JWT must be stored in httpOnly cookies (not localStorage) because:
- Next.js Server Components cannot access localStorage (runs on server)
- httpOnly cookies are XSS-safe and work across SSR/client
Note this in the analysis and generate the Django CORS + cookie settings
from references/auth-middleware.md (Next.js BFF section).
Step 3: Analyse existing auth (if any)
Check for: existing User model, JWT configuration, any AbstractBaseUser subclasses,
existing permission patterns, AUTH_USER_MODEL setting.
For existing projects — build this map before asking questions:
AUTH_USER_MODEL: [app.Model]
Existing user types: [list from CLAUDE.md or codebase scan]
Existing JWT setup: [simplejwt / djoser / custom / none]
Existing UserTypeAuthMiddleware: [yes / no]
Existing login endpoints: [list]
What's already working: [list]
What's needed (from requirement): [new type / new permission / reset flow / OAuth]
Only ask clarifying questions about what's NOT already clear from this map. Never re-implement what already exists — extend it.
Step 4: Intelligent Clarifying Questions
Always use ask_user_input_v0.
Mandatory questions for every auth setup:
Which user types does this application have? Examples: Staff, Customer, Vendor, Driver, Agent — list all types needed.
Which user type is the PRIMARY (AUTH_USER_MODEL)? Django only supports one AUTH_USER_MODEL. This type gets Django admin access. Best practice suggestion: Staff/Admin should be primary — they need Django admin. → [Staff is primary] [Customer is primary] [I'll decide]
What fields does each user type need? (ask per type) — email, phone, name, company, role, etc. Always suggest: email (unique), is_active, date_joined as minimum.
What permissions model is needed? → [Django model permissions via GetPermission] [Custom RBAC with roles] → [Per-user-type permissions] [Hybrid — model perms for staff, custom for others]
JWT token requirements? → [Standard (user_id + user_type)] [Custom claims needed (tenant_id, role, permissions)]
OAuth/social auth needed? → [No — email/password only] [Yes — which providers: Google/GitHub/Microsoft]
Two-factor authentication (2FA) for staff users? → [Yes — mandatory for all staff (enterprise default)] → [Yes — optional, user can enable in profile] → [Yes — mandatory for admins only, optional for other roles] → [No — skip 2FA for now] If 2FA is enabled, load
references/2fa-totp.mdduring implementation.
Only proceed to Phase 1 once ALL questions are answered.
PHASE 1 — ANALYSIS & TEST CASES
Auth System Summary
Restate: user types, primary model, JWT claim structure, permission model, token flow.
Test Cases (generate BEFORE any code)
- ✅ Each user type: register, login → correct JWT returned
- ✅ JWT contains correct claims (user_type, relevant IDs)
- ❌ Wrong credentials → 401 with
{ success, message, errors } - ❌ Staff token rejected by customer endpoint → 403
- ❌ Customer token rejected by staff endpoint → 403
- 🔒 Expired token → 401
- 🔒 Blacklisted/revoked token → 401
- 🔒 Token for wrong user type → 401 (middleware rejects)
- 🔁 Refresh token → new access token returned
- 📐 All error responses follow
{ success, message, errors }shape
PHASE 2 — PLAN
Task size detection
- Single user type addition to existing system → QUICK CHANGE PLAN
- Full auth setup (new project or major rework) → FULL PLAN
═══════════════════════════════════════
AUTH IMPLEMENTATION PLAN
═══════════════════════════════════════
SUMMARY: [1-2 sentences]
USER TYPES
──────────
Primary (AUTH_USER_MODEL): [type]
Fields: [list]
Table: [app]_[type]user
Secondary types: [list each]
Fields: [list per type]
Table: [app]_[type]user
JWT ARCHITECTURE
────────────────
Each type gets:
- [TypeName]TokenObtainPairSerializer → embeds user_type: "[type]"
- [TypeName]JWTAuthentication → validates [type] tokens only
- /api/v1/auth/[type]/login/ → dedicated login URL
- /api/v1/auth/[type]/refresh/ → dedicated refresh URL
Middleware: UserTypeAuthMiddleware
→ reads user_type from JWT before DRF authentication
→ injects request.[type]_user for non-primary types
→ request.user for primary type (standard Django)
PERMISSIONS: [model perms / RBAC / hybrid]
TASKS
─────
A1: Core auth app setup (AbstractBaseUser models)
A2: JWT backends + serializers per type
A3: Auth middleware
A4: URL routing per type
A5: RBAC setup (if needed)
A6: OAuth providers (if needed)
A7: Token revocation + blacklist
T1: Tests — all Phase 1 cases
COMPLEXITY: Medium / High
═══════════════════════════════════════
Ask: "Plan looks good? Any changes before I start?"
PHASE 3 — IMPLEMENTATION
Critical rules
⚠️ AUTH_USER_MODEL can only point to ONE model. The primary user type owns it.
⚠️ Non-primary user types have their own tables but are NOT Django auth users.
⚠️ NEVER use bulk_create() for user creation — bypasses signals and hashing.
⚠️ ALWAYS hash passwords via set_password() — never store plain text.
Reference loading (load ONLY what current task needs)
- AbstractBaseUser models →
references/custom-user-models.md - JWT backends + serializers + views →
references/jwt-multi-type.md - Password reset flow (non-primary user types) →
references/password-reset.md - Middleware pattern →
references/auth-middleware.md - RBAC + permissions →
references/rbac-permissions.md - OAuth / social auth →
references/oauth-social.md - Token revocation →
references/token-revocation.md - Two-factor authentication (TOTP) →
references/2fa-totp.md(only if 2FA enabled) - Auth tests →
references/auth-testing.md - New auth app scaffold →
assets/templates/user-type-scaffold.py
After each task:
- Show completed code
- If models created:
python manage.py makemigrations <app> && python manage.py migrate - Suggest git commit
- Ask: "Task [X] done ✓ — ready to move to [next task]?"
PHASE 4 — REVIEW CHECKLIST
Adaptive checklist: Skip any item that was explicitly opted out of during Phase 0 clarifying questions (e.g. user chose hard delete → skip SoftDeleteMixin item; user chose no OAuth → skip OAuth items). The checklist reflects defaults — document any deliberate deviations in CLAUDE.md.
Models:
- Each user type inherits
AbstractBaseUser+PermissionsMixin(primary only) - Each user type has its own
UserManagerwithcreate_user()+create_superuser() -
USERNAME_FIELDset (usuallyemail) -
REQUIRED_FIELDSset correctly per type - Primary type set as
AUTH_USER_MODELin settings -
emailfield is unique per type -
is_active = models.BooleanField(default=True)on every type -
date_joined = models.DateTimeField(auto_now_add=True)on every type
JWT:
- Each type has its own
TokenObtainPairSerializerwithuser_typeclaim - Each type has its own
JWTAuthenticationsubclass - Each
JWTAuthenticationvalidates ONLY its own token type -
ACCESS_TOKEN_LIFETIMEandREFRESH_TOKEN_LIFETIMEconfigured in settings - Token blacklist app installed (
rest_framework_simplejwt.token_blacklist)
Middleware:
-
UserTypeAuthMiddlewarereadsuser_typefrom JWT payload - Primary type →
request.userpopulated as normal - Non-primary types →
request.<type>_userinjected (e.g.request.customer_user) - Unauthenticated →
request.userisAnonymousUser, non-primary attrs areNone
URLs:
- Each type has
/api/v1/auth/<type>/login/endpoint - Each type has
/api/v1/auth/<type>/refresh/endpoint - Each type has
/api/v1/auth/<type>/logout/(blacklists refresh token)
Permissions:
- Views specify which user type can access them
- Staff endpoints reject non-staff tokens
- Customer endpoints reject non-customer tokens
-
GetPermissionfactory updated to work with multi-user context
Tests:
- All Phase 1 test cases implemented
- Cross-type token rejection tests pass
-
deleted_byequivalent:deactivated_byfield on user models -
CLAUDE.mdupdated with user types, JWT claim structure, auth URLs
If 2FA is enabled:
-
django-otpinstalled with TOTP + static (recovery codes) plugins -
INSTALLED_APPSincludesdjango_otp,django_otp.plugins.otp_totp,django_otp.plugins.otp_static -
OTPMiddlewareregistered AFTERAuthenticationMiddleware -
OTP_TOTP_ISSUERset in settings (shown in authenticator app) - StaffUser model has
has_2fa_enabled+require_2fafields - Enrollment endpoints:
/2fa/setup/(QR code) +/2fa/confirm/(verify + issue recovery codes) - Login flow returns
requires_2fa: truewith short-livedpre_2fa_tokensigned viaTimestampSigner - Verification endpoint accepts TOTP or recovery code (one-time use)
- Admin panel protected via
OTPAdminSite(if 2FA mandatory for admins) - Recovery codes generated once at enrollment, displayed to user, shown as one-time consumable
- Test: enrollment + login-with-2FA + recovery-code-one-time + disable-2FA flows
CLAUDE.md v2 Update Rules (saas-dev 4.0.0+)
At the end of Phase 3, update CLAUDE.md following the v2 protocol. Full rules:
saas-dev/references/router/claude-md-update-protocol.md. Quick reference for this skill:
Always update:
- §2
last_updated— today's date - §3
version_last_used— current saas-dev version - §9 Recent Changes — prepend one entry:
| YYYY-MM-DD | [SKILL_NAME] | [VERSION] | [change] |
Update as relevant to work done:
- §4 Dependency Registry — new packages added (version + one-line purpose)
- §5 Environment Variables — new env vars (under correct subsection)
- §6 Third-Party Integrations — new row if integration added
- §7 Architecture Decisions — new ADR for non-obvious design choices
- §8 Known Issues — append if discovered during work
Emit update checkpoint to chat:
✓ CLAUDE.md updated:
§4: +N dependencies
§5: +N env vars
§7: +ADR-NNN (title)
§9: +1 change entry
Full format spec: saas-dev/references/router/claude-md-v2.md