diff --git a/authentik/providers/oauth2/api/tokens.py b/authentik/providers/oauth2/api/tokens.py index 05037de6b..b65679658 100644 --- a/authentik/providers/oauth2/api/tokens.py +++ b/authentik/providers/oauth2/api/tokens.py @@ -1,5 +1,4 @@ """OAuth2Provider API Views""" -from dataclasses import asdict from json import dumps from django_filters.rest_framework import DjangoFilterBackend @@ -38,7 +37,7 @@ class RefreshTokenModelSerializer(ExpiringBaseGrantModelSerializer): def get_id_token(self, instance: RefreshToken) -> str: """Get the token's id_token as JSON String""" - return dumps(asdict(instance.id_token), indent=4) + return dumps(instance.id_token.to_dict(), indent=4) class Meta: diff --git a/authentik/providers/oauth2/models.py b/authentik/providers/oauth2/models.py index 90688f314..b0a1c6268 100644 --- a/authentik/providers/oauth2/models.py +++ b/authentik/providers/oauth2/models.py @@ -399,10 +399,13 @@ class IDToken: def to_dict(self) -> dict[str, Any]: """Convert dataclass to dict, and update with keys from `claims`""" - dic = asdict(self) - dic.pop("claims") - dic.update(self.claims) - return dic + id_dict = asdict(self) + # at_hash should be omitted when not set instead of retuning a null claim + if not self.at_hash: + id_dict.pop("at_hash") + id_dict.pop("claims") + id_dict.update(self.claims) + return id_dict class RefreshToken(SerializerModel, ExpiringModel, BaseGrantModel): @@ -432,7 +435,7 @@ class RefreshToken(SerializerModel, ExpiringModel, BaseGrantModel): @id_token.setter def id_token(self, value: IDToken): - self._id_token = json.dumps(asdict(value)) + self._id_token = json.dumps(value.to_dict()) def __str__(self): return f"Refresh Token for {self.provider} for user {self.user}" diff --git a/authentik/providers/oauth2/tests/test_introspect.py b/authentik/providers/oauth2/tests/test_introspect.py index 40dea7bc5..63e77b7db 100644 --- a/authentik/providers/oauth2/tests/test_introspect.py +++ b/authentik/providers/oauth2/tests/test_introspect.py @@ -1,7 +1,6 @@ """Test introspect view""" import json from base64 import b64encode -from dataclasses import asdict from django.urls import reverse @@ -37,9 +36,7 @@ class TesOAuth2Introspection(OAuthTestCase): refresh_token=generate_id(), _scope="openid user profile", _id_token=json.dumps( - asdict( - IDToken("foo", "bar"), - ) + IDToken("foo", "bar").to_dict(), ), ) self.auth = b64encode( diff --git a/authentik/providers/oauth2/tests/test_revoke.py b/authentik/providers/oauth2/tests/test_revoke.py index 0e474d8dc..046f1c74d 100644 --- a/authentik/providers/oauth2/tests/test_revoke.py +++ b/authentik/providers/oauth2/tests/test_revoke.py @@ -1,7 +1,6 @@ """Test revoke view""" import json from base64 import b64encode -from dataclasses import asdict from django.urls import reverse @@ -36,11 +35,7 @@ class TesOAuth2Revoke(OAuthTestCase): access_token=generate_id(), refresh_token=generate_id(), _scope="openid user profile", - _id_token=json.dumps( - asdict( - IDToken("foo", "bar"), - ) - ), + _id_token=json.dumps(IDToken("foo", "bar").to_dict()), ) self.auth = b64encode( f"{self.provider.client_id}:{self.provider.client_secret}".encode() diff --git a/authentik/providers/oauth2/tests/test_userinfo.py b/authentik/providers/oauth2/tests/test_userinfo.py index 007db5c40..e6f420f6a 100644 --- a/authentik/providers/oauth2/tests/test_userinfo.py +++ b/authentik/providers/oauth2/tests/test_userinfo.py @@ -1,6 +1,5 @@ """Test userinfo view""" import json -from dataclasses import asdict from django.urls import reverse @@ -39,11 +38,7 @@ class TestUserinfo(OAuthTestCase): access_token=generate_id(), refresh_token=generate_id(), _scope="openid user profile", - _id_token=json.dumps( - asdict( - IDToken("foo", "bar"), - ) - ), + _id_token=json.dumps(IDToken("foo", "bar").to_dict()), ) def test_userinfo_normal(self):