Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
run: pylint squarelet

- name: Sort
run: isort --check-only squarelet config/urls.py config/settings
run: isort --check-only --diff squarelet config/urls.py config/settings

- name: Format
run: black --check squarelet --exclude migrations && black --check config/urls.py && black --check config/settings
Expand Down
9 changes: 9 additions & 0 deletions config/settings/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,12 @@
STRIPE_PUB_KEY = "pk_muckrock"
STRIPE_SECRET_KEY = "sk_muckrock"
STRIPE_WEBHOOK_SECRET = None

# Frontend
# ------------------------------------------------------------------------------
# Don't look for built frontend assets during tests
DJANGO_VITE = {
"default": {
"dev_mode": True,
}
}
73 changes: 73 additions & 0 deletions squarelet/core/tests/test_jwt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Third Party
import pytest
from Crypto.PublicKey import RSA
from oidc_provider.models import RSAKey
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture(autouse=True)
def jwt_rsa_key(db, settings): # pylint:disable=unused-argument

key = RSA.generate(2048)
private_key = key.export_key().decode()
public_key = key.publickey().export_key()
RSAKey.objects.create(key=private_key)
settings.SIMPLE_JWT = {
**settings.SIMPLE_JWT,
"SIGNING_KEY": private_key,
"VERIFYING_KEY": public_key,
}


@pytest.mark.django_db()
class TestJWTConfiguration:
"""
Guards against misconfiguration of simplejwt settings.
These have broken on upgrades in the past — e.g. ISSUER format
changing from a list to a string broke refresh token validation
in the upgrade to Django 5.
"""

def test_token_obtain_returns_access_and_refresh(self, user_factory):
user = user_factory(password="testpassword")
client = APIClient()
response = client.post(
"/api/token/",
{"username": user.username, "password": "testpassword"},
)
assert response.status_code == status.HTTP_200_OK, "Token obtain failed"
assert "access" in response.data, "Response missing access token"
assert "refresh" in response.data, "Response missing refresh token"

def test_refresh_token_is_accepted(self, user_factory):
user = user_factory(password="testpassword")
client = APIClient()
response = client.post(
"/api/token/",
{"username": user.username, "password": "testpassword"},
)
assert response.status_code == status.HTTP_200_OK, "Token obtain failed"
refresh_token = response.data["refresh"]
response = client.post("/api/refresh/", {"refresh": refresh_token})
assert response.status_code == status.HTTP_200_OK, (
"Refresh token was rejected — check SIMPLE_JWT settings "
"(ISSUER, ALGORITHM, SIGNING_KEY)"
)
assert "access" in response.data
assert "refresh" in response.data

def test_refresh_token_is_rotated(self, user_factory):
user = user_factory(password="testpassword")
client = APIClient()
response = client.post(
"/api/token/",
{"username": user.username, "password": "testpassword"},
)
original_refresh_token = response.data["refresh"]
response = client.post("/api/refresh/", {"refresh": original_refresh_token})
new_refresh_token = response.data["refresh"]
assert new_refresh_token != original_refresh_token, (
"Refresh token was not rotated — check ROTATE_REFRESH_TOKENS "
"in SIMPLE_JWT settings"
)
Loading