Authentication: token-based API auth
Every request so far has worked with no login at all — request.user in perform_create() has quietly been AnonymousUser this entire series unless tested from a browser with an active Django admin session. This part fixes that properly, with authentication built for an API client rather than a browser.
Why session auth (the Django default) doesn't fit an API
Django's default authentication relies on a session cookie, set when logging in through a browser-rendered form. A mobile app, a script, or a separate frontend calling this API has no browser session to carry — API authentication instead expects the client to send proof of identity with every single request, typically as a header, which is exactly what token authentication provides.
Setting up TokenAuthentication
# taskapi/settings.py
INSTALLED_APPS = [
# ...
"rest_framework.authtoken",
]
REST_FRAMEWORK = {
# ... existing settings ...
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
}python manage.py migraterest_framework.authtoken is its own app with its own model (Token, one per user) and needs a migration the same as any other. Keeping SessionAuthentication alongside TokenAuthentication is deliberate — it's what lets the browsable API (part 18) keep working with your existing Django admin login, while TokenAuthentication handles everything else.
Issuing a token
# tasks/urls.py
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = router.urls + [
path("token/", obtain_auth_token, name="api-token"),
]curl -X POST http://127.0.0.1:8000/api/token/ -d "username=alice&password=testpass123"{"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4"}obtain_auth_token is a built-in DRF view — it validates a username/password and returns the associated Token in one call, no view code of your own needed. A real client calls this once, stores the token, and sends it on every subsequent request from then on.
Sending it on every request
curl http://127.0.0.1:8000/api/tasks/ -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4"The Authorization: Token <value> header format — Token, not Bearer — is specific to DRF's TokenAuthentication; other schemes (JWT among them) use Bearer instead, worth knowing since copying the wrong prefix from a different tutorial is a common source of a mysterious 401.
Authentication is not permissions
request.user # the User if a valid token was sent, AnonymousUser otherwise
request.user.is_authenticatedTokenAuthentication only identifies who is making the request — it doesn't, by itself, block anyone. Right now, an anonymous request to TaskViewSet still succeeds; the crucial distinction is that authentication answers "who is this?" while permissions (next part) answer "what are they allowed to do?" — two separate, composable layers.
Sending the token over plain HTTP instead of HTTPS. A token is a long-lived credential — unlike a short-lived session cookie's typical lifecycle, a leaked API token often keeps working until manually revoked. Never send one anywhere except over HTTPS in production.
Next: actually restricting what an authenticated (or anonymous) user is allowed to do, with DRF's permission classes.