Adding Next-Auth Authentication to Django, FastAPI, and Next.js Stack
Wire Next-Auth to FastAPI with JWT tokens. Get login, session refresh, and type-safe auth across your full-stack app—no enterprise complexity.
This tutorial builds upon the Creating a Full Stack Application with Django, FastAPI, and Next.js guide. We'll add authentication by integrating Next-Auth (Auth.js v5 beta) into our existing full-stack application — wiring a Credentials provider in Next.js to a custom JWT login endpoint on the Django/FastAPI backend.
By the end you'll have:
- A sign-in/sign-out flow protected by Next-Auth middleware
- A Django FastAPI
/users/login/endpoint that issues short-lived JWTs - A
/users/session/endpoint that refreshes tokens transparently on every Next.js server-side render - Type-safe session data shared across the whole Next.js app
Why this architecture? Next-Auth stores the access token in an encrypted server-side JWT cookie. The
sessioncallback re-validates and silently rotates the token on every request, so your users never hit a mid-session 401 without being shown an error page.
1. Initial Setup
If you haven't completed the first tutorial, start by cloning the repository and setting up the initial project.
1.1 Clone the Repository
1.2 Start the Docker Stack
1.3 Run Database Migrations
1.4 Create a Superuser
Follow the prompts to create your superuser account. Use an email address as the username — the Django backend authenticates by email.
2. Install and Configure Next-Auth
2.1 Install Next-Auth
We install the @beta tag because Auth.js v5 (the App Router-native version) is still in beta. It ships the auth() helper used in Server Components and the new NextAuth() initialiser.
2.2 Create Next-Auth Configuration
Create src/auth.ts. This is the single source of truth for all Next-Auth options:
Why a separate
auth.ts? Auth.js v5 exports named helpers (auth,handlers,signIn,signOut) from the single initialisation call. Putting them in one file lets you importauthinto any Server Component without re-instantiating the library.
2.3 Set Up Next-Auth API Routes
Create src/app/api/auth/[...nextauth]/route.ts. Auth.js v5 uses a catch-all route to handle sign-in, sign-out, CSRF, and callback traffic:
2.4 Add Next-Auth Middleware
Edit src/middleware.ts. The middleware runs on every matched request and redirects unauthenticated users before the page ever renders:
Edge case: Without the
matcherexclusion, Next-Auth middleware intercepts its own/api/auth/*callbacks, causing an infinite redirect loop. Always excludeapi/authfrom the matcher.
3. Create Sign In and Sign Out Components
3.1 Create Sign In Button Component
Create src/app/components/sign-in.tsx:
3.2 Create Sign Out Button Component
Create src/app/components/sign-out.tsx:
Why
"use client"?signIn()andsignOut()fromnext-auth/reactrely on browser APIs and React context. Server Components must use thesignIn/signOuthelpers exported fromsrc/auth.ts(via a Server Action) instead.
3.3 Update Home Page
Edit src/app/page.tsx:
4. Configure Next-Auth for Django Credentials
4.1 Set Environment Variables
Create frontend/.env.local:
NEXTAUTH_SECRETmust be a long random string in production. Generate one withopenssl rand -base64 32.INTERNAL_API_URLuses the Docker Compose service name (api) so the Next.js container talks directly to Django inside the Docker network — bypassing Nginx and avoiding an extra round-trip.
4.2 Create Next-Auth Type Declarations
Create frontend/src/types/next-auth.d.ts to extend Auth.js's built-in types with your Django user fields:
Why extend
DefaultSession? Auth.js ships a minimalSessiontype ({ user?: { name, email, image }, expires }). Extending it — rather than replacing it — preserves the built-inexpiresfield and keeps TypeScript happy across the whole app.
4.3 Update Next-Auth Configuration
Update frontend/src/auth.ts with the Credentials provider and the two key callbacks:
Why call the API inside the session callback and not the jwt callback?
The jwt callback runs even when no session is needed (e.g. middleware token checks). The session callback only runs when a component actually reads the session, reducing unnecessary backend calls. It also means every rendered page gets a fresh token if the old one is about to expire — no user ever sees a stale access_token in their browser.
Common failure modes:
| Symptom | Likely cause | Fix |
|---|---|---|
| Redirect loop on sign-in | NEXTAUTH_SECRET missing or mismatched between restarts | Set a stable secret in .env.local |
| 401 on every page load | INTERNAL_API_URL resolves to localhost inside Docker | Use the Docker service name (http://api:8000) |
| Session immediately expires after sign-in | authorize response shape doesn't match what jwt callback reads | Log user in the jwt callback and compare to the /users/login/ JSON |
TypeScript errors on session.access_token | Type declarations not picked up | Restart the TS server; ensure tsconfig.json includes src/types |
4.4 Update Docker Compose Configuration
Edit docker-compose.yml to inject the env file into the frontend service:
4.5 Restart Frontend Container
5. Create FastAPI Routes for Django Authentication
5.1 Update Backend Requirements
Edit backend/requirements.txt to add JWT support. We use python-jose (latest stable: 3.5.0) with the cryptography extra for full algorithm support:
Note: Pin to
3.3.0if your project requires it, or upgrade to the latest3.5.0. The[cryptography]extra replaces the olderpycryptobackend and is the recommended install for new projects.
5.2 Create User Schemas
Create backend/users/schemas.py:
5.3 Create Authentication Utilities
Create backend/users/utils.py:
Why HS256 and Django's SECRET_KEY?
Using Django's existing SECRET_KEY means you don't need to manage a separate signing secret. HS256 is a symmetric algorithm — the same key signs and verifies, which is fine for a single-service backend. If you ever split auth into a microservice, switch to RS256 and a dedicated key pair.
5.4 Create Authentication Routers
Create backend/users/routers.py:
5.5 Update ASGI Configuration
Update backend/mysaas/asgi.py to register the users router with FastAPI:
6. Verify the Setup
6.1 Rebuild the Backend
After adding python-jose to requirements.txt, rebuild the API container:
6.2 Check the FastAPI Health Endpoint
Confirm FastAPI is responding before testing auth:
6.3 Test the Login Endpoint Directly
Use the superuser credentials you created in step 1.4:
A successful response looks like:
6.4 Test the Sign-In Flow in the Browser
- Navigate to
http://localhost:3000. - Click Sign in — you should be taken to the Next-Auth built-in credentials form.
- Enter the superuser email and password.
- On success, the home page should display the full session JSON and a Sign out button.
6.5 Verify Token Rotation
Open the Network tab in DevTools and watch requests to /api/auth/session. After you sign in, each hard-reload calls the Next-Auth session callback, which in turn calls /users/session/ on Django. If the token is still valid you'll see the same access_token echoed back; when it reaches the ACCESS_TOKEN_VALID_MINUTES threshold a fresh token is returned.
7. Common Issues & Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
CredentialsSignin error on the form | Wrong email/password, or Django's authenticate() isn't finding the user | Verify the superuser was created with an email, not a username |
fetch failed in the session callback | INTERNAL_API_URL is not reachable from the Next.js container | Confirm the api service name in docker-compose.yml matches INTERNAL_API_URL |
| Infinite redirect after sign-in | Middleware matcher catching the auth callback route | Ensure api/auth is excluded from the matcher in middleware.ts |
JWTError / jose not found | python-jose not installed in the API container | Run docker compose up api --build -d to rebuild |
TypeScript: Property 'access_token' does not exist on type 'Session' | Module augmentation not applied | Check tsconfig.json includes "include": ["src"] and restart TS server |
| Session expires immediately after sign-in | authorize response shape mismatch | Add console.log(user, account) in the jwt callback to inspect payloads |
Damian Hodgkiss
Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.