Skip to content

Commit

Permalink
✅(api) add id token factory to improve testing flow
Browse files Browse the repository at this point in the history
We are now able to better test tokens validation in our tests.
  • Loading branch information
jmaupetit committed Apr 5, 2024
1 parent 01c252f commit d6b5e32
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 28 deletions.
31 changes: 31 additions & 0 deletions src/api/qualicharge/auth/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Authentication model factories."""

import uuid
from datetime import datetime
from typing import Any, Dict

from polyfactory import PostGenerated
from polyfactory.factories.pydantic_factory import ModelFactory
from polyfactory.pytest_plugin import register_fixture

from qualicharge.conf import settings

from .models import IDToken


def set_token_exp(name: str, values: Dict[str, int], *args: Any, **kwargs: Any) -> int:
"""Set token expiracy field base on the iat."""
return values["iat"] + 300


@register_fixture(name="id_token_factory")
class IDTokenFactory(ModelFactory[IDToken]):
"""IDToken model factory."""

iss = "http://keycloak:8080/realms/qualicharge"
sub = str(uuid.uuid4())
aud = settings.OIDC_EXPECTED_AUDIENCE
exp = PostGenerated(set_token_exp)
iat = int(datetime.now().timestamp())
scope = "email profile"
email = "john@doe.com"
4 changes: 3 additions & 1 deletion src/api/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# pylint: disable=unused-import
# ruff: noqa: F401

from .fixtures.app import client, client_auth, id_token_factory
from qualicharge.auth.factories import IDTokenFactory

from .fixtures.app import client, client_auth
from .fixtures.db import (
db_engine,
db_session,
Expand Down
25 changes: 3 additions & 22 deletions src/api/tests/fixtures/app.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
"""App HTTP client pytest fixtures."""

import uuid
from datetime import datetime

import pytest
from fastapi.testclient import TestClient

from qualicharge.api.v1 import app
from qualicharge.auth.models import IDToken
from qualicharge.auth.factories import IDTokenFactory
from qualicharge.auth.oidc import get_token
from qualicharge.conf import settings


@pytest.fixture
def id_token_factory() -> IDToken:
"""Generate a fake IDToken."""
now = datetime.now().timestamp()
return IDToken(
iss=str(settings.OIDC_PROVIDER_BASE_URL),
sub=str(uuid.uuid4()),
aud=settings.OIDC_EXPECTED_AUDIENCE,
exp=int(now) + 300,
iat=int(now),
scope="email profile",
email="john@doe.com",
)


@pytest.fixture
Expand All @@ -34,8 +15,8 @@ def client():


@pytest.fixture
def client_auth(id_token_factory):
def client_auth(id_token_factory: IDTokenFactory):
"""An authenticated test client configured for the /api/v1 application."""
app.dependency_overrides[get_token] = lambda: id_token_factory
app.dependency_overrides[get_token] = lambda: id_token_factory.build()
yield TestClient(app)
app.dependency_overrides = {}
29 changes: 24 additions & 5 deletions src/api/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"""Tests for qualicharge.auth module."""

from datetime import datetime

import httpx
import pytest
from fastapi.security import HTTPAuthorizationCredentials
from fastapi.security import HTTPAuthorizationCredentials, SecurityScopes
from jose import jwt

from qualicharge.auth.factories import IDTokenFactory
from qualicharge.auth.oidc import discover_provider, get_public_keys, get_token
from qualicharge.conf import settings
from qualicharge.exceptions import OIDCProviderException
from qualicharge.exceptions import OIDCAuthenticationError, OIDCProviderException


def test_discover_provider(httpx_mock):
Expand Down Expand Up @@ -54,7 +57,7 @@ def test__get_public_keys_with_bad_configuration(httpx_mock):
get_public_keys("http://oidc/wrong")


def test_get_token(httpx_mock, monkeypatch, id_token_factory):
def test_get_token(httpx_mock, monkeypatch, id_token_factory: IDTokenFactory):
"""Test the OIDC get token utility."""
monkeypatch.setenv("QUALICHARGE_OIDC_PROVIDER_BASE_URL", "http://oidc")
httpx_mock.add_response(
Expand All @@ -75,7 +78,23 @@ def test_get_token(httpx_mock, monkeypatch, id_token_factory):

bearer_token = HTTPAuthorizationCredentials(
scheme="Bearer",
credentials=jwt.encode(claims=id_token_factory.model_dump(), key="secret"),
credentials=jwt.encode(
claims=id_token_factory.build().model_dump(), key="secret"
),
)
token = get_token(security_scopes={}, token=bearer_token)
token = get_token(security_scopes=SecurityScopes(), token=bearer_token)
assert token.email == "john@doe.com"

# Expired token case
#
# As exp should be set to iat + 300, the token should be expired
iat = int(datetime.now().timestamp()) - 500
bearer_token = HTTPAuthorizationCredentials(
scheme="Bearer",
credentials=jwt.encode(
claims=id_token_factory.build(iat=iat).model_dump(),
key="secret",
),
)
with pytest.raises(OIDCAuthenticationError, match="Unable to decode ID token"):
get_token(security_scopes=SecurityScopes(), token=bearer_token)

0 comments on commit d6b5e32

Please sign in to comment.