Skip to content

Testing auth

How to test authentication in your Bluefox app.

Test setup

bluefox-auth uses bluefox-test for its own tests. Your app's tests can follow the same pattern.

conftest.py

import pytest
import pytest_asyncio
from bluefox_core.database import BluefoxBase, get_session
from bluefox_test import bluefox_test_setup
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient

from bluefox_auth import BluefoxAuth
from bluefox_auth.settings import AuthSettings

# Set up test database with SAVEPOINT isolation
globals().update(
    bluefox_test_setup(
        base=BluefoxBase,
        app_factory=None,
        session_dependency=None,
    )
)

TEST_SECRET = "test-secret-key-that-is-long-enough-for-validation"


@pytest.fixture
def auth_app(db):
    """FastAPI app with auth configured."""
    from types import SimpleNamespace

    settings = SimpleNamespace(SECRET_KEY=TEST_SECRET)
    app = FastAPI()

    # Override the DB session
    async def _override_session():
        yield db

    app.dependency_overrides[get_session] = _override_session

    BluefoxAuth(app, settings, cookie_secure=False)

    yield app
    app.dependency_overrides.clear()


@pytest_asyncio.fixture
async def client(auth_app):
    transport = ASGITransport(app=auth_app)
    async with AsyncClient(transport=transport, base_url="http://testserver") as ac:
        yield ac

cookie_secure=False

Set cookie_secure=False in tests — httpx's test transport doesn't use HTTPS.

Testing Bearer auth

async def test_protected_route(client):
    # Register and login
    await client.post(
        "/auth/register",
        json={"email": "test@example.com", "password": "test-password"},
    )
    login = await client.post(
        "/auth/login",
        json={"email": "test@example.com", "password": "test-password"},
    )
    token = login.json()["access_token"]

    # Access protected route
    resp = await client.get(
        "/your-protected-route",
        headers={"Authorization": f"Bearer {token}"},
    )
    assert resp.status_code == 200
async def test_cookie_auth(client):
    # Login
    login = await client.post(
        "/auth/login",
        json={"email": "test@example.com", "password": "test-password"},
    )

    # Set cookies on client
    access = login.cookies.get("bf_access_token")
    csrf = login.cookies.get("bf_csrf_token")
    client.cookies.set("bf_access_token", access)

    # GET works without CSRF
    resp = await client.get("/your-protected-route")
    assert resp.status_code == 200

    # POST requires CSRF header
    client.cookies.set("bf_csrf_token", csrf)
    resp = await client.post(
        "/your-protected-route",
        headers={"X-CSRF-Token": csrf},
        json={"data": "..."},
    )
    assert resp.status_code == 200

Creating test users directly

For tests that don't need to go through the registration flow:

from bluefox_auth.models import BluefoxUser
from bluefox_auth.passwords import hash_password
from bluefox_auth.tokens import create_access_token
from bluefox_auth.settings import AuthSettings


async def test_with_direct_user(db, auth_app, client):
    # Create user directly in DB
    user = BluefoxUser(
        email="test@example.com",
        password_hash=hash_password("test-password"),
        is_active=True,
    )
    db.add(user)
    await db.flush()
    await db.refresh(user)

    # Create token directly
    settings = auth_app.state.auth_settings
    token = create_access_token(user.id, settings)

    resp = await client.get(
        "/auth/me",
        headers={"Authorization": f"Bearer {token}"},
    )
    assert resp.status_code == 200

Testing with pytest-asyncio

bluefox-auth uses asyncio_mode = "auto" in pyproject.toml, so async test functions are automatically detected:

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"

No @pytest.mark.asyncio decorators needed.