providers/oauth2: correctly fill claims_supported based on selected scopes (#4429)

* providers/oauth2: correctly fill claims_supported based on selected scopes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add nonce claim

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L 2023-01-13 14:14:25 +01:00 committed by GitHub
parent 9c9f441cff
commit 20931ccc1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 11 deletions

View File

@ -6,6 +6,7 @@ from django.shortcuts import get_object_or_404, reverse
from django.views import View from django.views import View
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.exceptions import PropertyMappingExpressionException
from authentik.core.models import Application from authentik.core.models import Application
from authentik.providers.oauth2.constants import ( from authentik.providers.oauth2.constants import (
ACR_AUTHENTIK_DEFAULT, ACR_AUTHENTIK_DEFAULT,
@ -108,9 +109,13 @@ class ProviderInfoView(View):
"scopes_supported": scopes, "scopes_supported": scopes,
# https://openid.net/specs/openid-connect-core-1_0.html#RequestObject # https://openid.net/specs/openid-connect-core-1_0.html#RequestObject
"request_parameter_supported": False, "request_parameter_supported": False,
# Because claims are dynamic and per-application, the only claims listed here "claims_supported": self.get_claims(provider),
# are ones that are always set by authentik itself on every token "claims_parameter_supported": False,
"claims_supported": [ }
def get_claims(self, provider: OAuth2Provider) -> list[str]:
"""Get a list of supported claims based on configured scope mappings"""
default_claims = [
"sub", "sub",
"iss", "iss",
"aud", "aud",
@ -118,9 +123,25 @@ class ProviderInfoView(View):
"iat", "iat",
"auth_time", "auth_time",
"acr", "acr",
], "amr",
"claims_parameter_supported": False, "nonce",
} ]
for scope in ScopeMapping.objects.filter(provider=provider).order_by("scope_name"):
value = None
try:
value = scope.evaluate(
user=self.request.user,
request=self.request,
provider=provider,
)
except PropertyMappingExpressionException:
continue
if value is None:
continue
if not isinstance(value, dict):
continue
default_claims.extend(value.keys())
return default_claims
# pylint: disable=unused-argument # pylint: disable=unused-argument
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:

View File

@ -110,6 +110,8 @@ class UserInfoView(View):
return HttpResponseBadRequest() return HttpResponseBadRequest()
claims = self.get_claims(self.token) claims = self.get_claims(self.token)
claims["sub"] = self.token.id_token.sub claims["sub"] = self.token.id_token.sub
if self.token.id_token.nonce:
claims["nonce"] = self.token.id_token.nonce
response = TokenResponse(claims) response = TokenResponse(claims)
return response return response