Repository: goauthentik/authentik

Issue:
User is asked for OpenID scopes that can't be requested by the client / application
I created a new OpenID scope mapping for `profile`. Then i replaced the default `profile` scope in the provider with the new one. If the user logs in and sees the consent all scopes that are beeing requested by the OpenID client / application are displayed even if they are not selected in the authentik provider. Is this intended?

Here is the mapping i created:
<img width="788" alt="image" src="https://github.com/goauthentik/authentik/assets/14923000/6c84bd13-8af9-4453-ab46-f1eca91b6747">

Add this scope to a provider and remove `authentik default OAuth Mapping: OpenID 'profile'`.

Remove the consent for the app from the user (if already consented) and login to the application.

This is the explicit consent the user sees:
<img width="508" alt="image" src="https://github.com/goauthentik/authentik/assets/14923000/41d83be8-6a67-48a5-b43c-d914843aad57">

After testing a bit more it seems like the user sees all scopes beeing requested even if there is no scope selected in the provider. This seems confusing since the user is accepting scopes the OpenID client / application doesn't get access to.

I know that i could create a new scope `custom_profile` and add it to all applications but i wanted to improve the default `profile` scope without touching all applications (as described in the comment of `authentik default OAuth Mapping: OpenID 'profile'`: `You can override this behaviour in custom mappings...`).

Should i have deleted the default authentik mapping of overwritten it instead of creating my own (will this be reverted by authentik updates)?

-   authentik version: 2023.8.1
-   deployment: docker-compose


Discussion:

Resolving patch:
diff --git a/authentik/providers/oauth2/views/authorize.py b/authentik/providers/oauth2/views/authorize.py
--- a/authentik/providers/oauth2/views/authorize.py
+++ b/authentik/providers/oauth2/views/authorize.py
@@ -375,7 +375,9 @@ def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
             ):
                 self.request.session[SESSION_KEY_LAST_LOGIN_UID] = login_uid
                 return self.handle_no_permission()
-        scope_descriptions = UserInfoView().get_scope_descriptions(self.params.scope)
+        scope_descriptions = UserInfoView().get_scope_descriptions(
+            self.params.scope, self.params.provider
+        )
         # Regardless, we start the planner and return to it
         planner = FlowPlanner(self.provider.authorization_flow)
         planner.allow_empty_flows = True
diff --git a/authentik/providers/oauth2/views/device_init.py b/authentik/providers/oauth2/views/device_init.py
--- a/authentik/providers/oauth2/views/device_init.py
+++ b/authentik/providers/oauth2/views/device_init.py
@@ -55,7 +55,7 @@ def validate_code(code: int, request: HttpRequest) -> Optional[HttpResponse]:
     if not app:
         return None
 
-    scope_descriptions = UserInfoView().get_scope_descriptions(token.scope)
+    scope_descriptions = UserInfoView().get_scope_descriptions(token.scope, token.provider)
     planner = FlowPlanner(token.provider.authorization_flow)
     planner.allow_empty_flows = True
     try:
diff --git a/authentik/providers/oauth2/views/userinfo.py b/authentik/providers/oauth2/views/userinfo.py
--- a/authentik/providers/oauth2/views/userinfo.py
+++ b/authentik/providers/oauth2/views/userinfo.py
@@ -40,10 +40,14 @@ class UserInfoView(View):
 
     token: Optional[RefreshToken]
 
-    def get_scope_descriptions(self, scopes: list[str]) -> list[PermissionDict]:
+    def get_scope_descriptions(
+        self, scopes: list[str], provider: OAuth2Provider
+    ) -> list[PermissionDict]:
         """Get a list of all Scopes's descriptions"""
         scope_descriptions = []
-        for scope in ScopeMapping.objects.filter(scope_name__in=scopes).order_by("scope_name"):
+        for scope in ScopeMapping.objects.filter(scope_name__in=scopes, provider=provider).order_by(
+            "scope_name"
+        ):
             scope_descriptions.append(PermissionDict(id=scope.scope_name, name=scope.description))
         # GitHub Compatibility Scopes are handled differently, since they required custom paths
         # Hence they don't exist as Scope objects


Test patch: