Testing authentication#
Practice shows us that most API endpoints will be using some form of authentication.
There are two ways to test it with django-modern-rest.
The correct way to test auth#
We don’t ship our own version of force_authenticate or similar functions.
Because auth is one of the most important parts of your app.
We must not ship any simple ways to bypass it easily.
In this case most users will just bypass it in their apps.
How do you test views with auth? By providing it!
1import json
2from http import HTTPStatus
3
4import pytest
5from django.contrib.auth.models import User
6from django.http import HttpResponse
7
8from dmr.security.token.app.models import Token
9from dmr.test import DMRRequestFactory
10from examples.auth.token.using_token_header import APIController
11
12
13@pytest.fixture
14def token_auth(admin_user: User) -> tuple[Token, str]:
15 return Token.issue(user=admin_user, name='test-token')
16
17
18def test_view_with_auth(
19 dmr_rf: DMRRequestFactory,
20 token_auth: tuple[Token, str],
21) -> None:
22 request = dmr_rf.get('/api/url', headers={'X-API-Token': token_auth[1]})
23
24 response = APIController.as_view()(request)
25
26 assert isinstance(response, HttpResponse)
27 assert response.status_code == HTTPStatus.OK, response.content
28 assert json.loads(response.content) == 'authed'
In this test we:
Create a real auth
TokeninstanceProvide a real request header
Use real auth logic to authenticate the request
We test that our real auth works the way we want it to.
The faster way to test auth#
Added in version 0.13.0.
But, we don’t always strictly need to test auth again and again. Only several tests might be enough. Especially, if this auth is rather slow due to 3rd party HTTP requests or a lot of crypto computations.
To fix this, we have a special function / pytest fixture
to disabled the auth for some specific endpoint.
Here’s how it can be used:
1import json
2from http import HTTPStatus
3
4from django.contrib.auth.models import User
5from django.http import HttpResponse
6
7from dmr.test import DMRRequestFactory, disabled_auth
8from examples.auth.token.using_token_header import APIController, token_auth
9
10
11def test_view_disabled_auth(
12 dmr_rf: DMRRequestFactory,
13 admin_user: User,
14) -> None:
15 request = dmr_rf.get('/api/url')
16
17 with disabled_auth(
18 APIController,
19 request=request,
20 user=admin_user,
21 auth=token_auth,
22 ):
23 # No `X-API-Token` header is passed:
24 response = APIController.as_view()(request)
25
26 # But, request is successful:
27 assert isinstance(response, HttpResponse)
28 assert response.status_code == HTTPStatus.OK, response.content
29 assert json.loads(response.content) == 'authed'
Use this approach together with our request factories.
Works for both pytest and unittest.
Our design goals here are:
Provide a utility for a non-common use-case
Make sure it does not compromise / affect / have a single source code change in the real auth flow, so no security problems can actually happen in production
Tip
Do not use django.test.Client.force_login(),
because it will use the default Django’s login / auth logic,
not django-modern-rest custom one.