fix(oidc): Nonce-Pruefung selbst kontrollieren statt Authlib zu ueberlassen

Authlib wertet "Aufrufer hat eine Nonce uebergeben, das Token traegt keine"
als Mismatch. Das ist fuer den erneuerten Token der mobilen Clients falsch:
ein per Refresh-Token geholtes ID-Token laesst die nonce-Claim regulaer weg,
womit der Hintergrund-Refresh immer an einem 401 gescheitert waere.

Die Nonce wird daher nicht mehr an Authlib durchgereicht, sondern hier
geprueft: vorhandene Nonce muss passen, und der Browser-Flow verlangt sie
zusaetzlich zwingend (require_nonce) — dort erzeugen wir sie selbst, also
muss der Provider sie zurueckspiegeln. Signatur und uebrige Claims bleiben
unveraendert bei Authlib.

Zwei Tests dazu: erneuertes Token ohne Nonce wird angenommen, Browser-Flow
ohne Nonce abgelehnt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-09 18:33:19 +02:00
co-authored by Claude Opus 5
parent 07235fd4c9
commit b560ee260c
3 changed files with 41 additions and 2 deletions
+23
View File
@@ -374,6 +374,29 @@ def test_exchange_rejects_nonce_mismatch(client, authentik):
assert r.status_code == 401
def test_refreshed_token_without_nonce_is_accepted(client, authentik):
"""A token renewed via the refresh token drops the nonce claim; the app
still sends the nonce it remembers, and that must not be a mismatch."""
_seed_identity(client)
r = exchange(client, nonce="original-nonce",
id_token=make_id_token(aud=MOBILE_CLIENT, nonce=None))
assert r.status_code == 200, r.text
def test_browser_flow_requires_a_nonce_in_the_token(client, authentik):
"""We generate the nonce for the browser flow, so the provider must echo
it — a token without one is refused there even though /exchange allows it."""
import oidc_config
from oidc_client import OIDCError
provider = oidc_config.get_provider("authentik")
with pytest.raises(OIDCError) as e:
oidc_client.validate_id_token(
provider, make_id_token(nonce=None),
expected_audience=WEB_CLIENT, nonce="expected", require_nonce=True)
assert e.value.slug == "oidc_nonce_missing"
def test_exchange_requires_nonce_when_token_carries_one(client, authentik):
_seed_identity(client)
r = exchange(client, id_token=make_id_token(aud=MOBILE_CLIENT, nonce="from-appauth"))