diff --git a/lms/envs/common.py b/lms/envs/common.py
index 46e2e47c5bd52f4ca72fc410ffc16007ba5669a3..66e95a0d5345428fdf3a569ede7d69fdbf861fb3 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -477,6 +477,7 @@ OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS = 30
 # Scope description strings are presented to the user
 # on the application authorization page. See
 # lms/templates/oauth2_provider/authorize.html for details.
+# Non-default scopes should be added directly to OAUTH2_PROVIDER['SCOPES'] below.
 OAUTH2_DEFAULT_SCOPES = {
     'read': _('Read access'),
     'write': _('Write access'),
@@ -490,6 +491,7 @@ OAUTH2_PROVIDER = {
     'REFRESH_TOKEN_EXPIRE_SECONDS': 7776000,
     'SCOPES_BACKEND_CLASS': 'openedx.core.djangoapps.oauth_dispatch.scopes.ApplicationModelScopes',
     'SCOPES': dict(OAUTH2_DEFAULT_SCOPES, **{
+        'user_id': _('Retrieve your user identifier'),
         'grades:read': _('Retrieve your grades for your enrolled courses'),
         'certificates:read': _('Retrieve your course certificates'),
     }),
diff --git a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py
index ba238b8a0bd47e2fa4e8e4c80b0544bbf2424f6c..04329e2b647db8f8364a11adec18b5b9981a8037 100644
--- a/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py
+++ b/openedx/core/djangoapps/oauth_dispatch/dot_overrides/views.py
@@ -27,6 +27,7 @@ class EdxOAuth2AuthorizationView(AuthorizationView):
     def get(self, request, *args, **kwargs):
         # Note: This code is copied from https://github.com/evonove/django-oauth-toolkit/blob/34f3b7b3511c15686039079026165feaadb1b87d/oauth2_provider/views/base.py#L111
         # Places that we have changed are noted with ***.
+        application = None
         try:
             # *** Moved code to get the require_approval value earlier on so we can
             # circumvent our custom code in the case when auto_even_if_expired
@@ -94,4 +95,4 @@ class EdxOAuth2AuthorizationView(AuthorizationView):
             return self.render_to_response(self.get_context_data(**kwargs))
 
         except OAuthToolkitError as error:
-            return self.error_response(error)
+            return self.error_response(error, application)
diff --git a/openedx/core/djangoapps/oauth_dispatch/jwt.py b/openedx/core/djangoapps/oauth_dispatch/jwt.py
index f63cb88bf4d72cd284c802fac7b0dc6512d38582..f379c7d636049f7161519c9002e6f904b2263f42 100644
--- a/openedx/core/djangoapps/oauth_dispatch/jwt.py
+++ b/openedx/core/djangoapps/oauth_dispatch/jwt.py
@@ -100,6 +100,9 @@ def _create_jwt(
         secret (string): Overrides configured JWT secret (signing) key.
     """
     use_asymmetric_key = _get_use_asymmetric_key_value(is_restricted, use_asymmetric_key)
+    # Default scopes should only contain non-privileged data.
+    # Do not be misled by the fact that `email` and `profile` are default scopes. They
+    # were included for legacy compatibility, even though they contain privileged data.
     scopes = scopes or ['email', 'profile']
     iat, exp = _compute_time_fields(expires_in)