From daafbaa37785389f0dc4c6be5e763ce773076e0a Mon Sep 17 00:00:00 2001 From: Taimoor Ahmed Date: Tue, 23 Jun 2026 12:03:49 +0500 Subject: [PATCH 1/6] docs(adr-0034): audit Xblock v1 against auth standardization (already compliant) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR 0034 — Unify Auth (OAuth2 DOT v2) — requires removing BearerAuthentication / BearerAuthenticationAllowInactiveUser / OAuth2Authentication / OAuth2AuthenticationAllowInactiveUser from authentication_classes per OEP-0042. XblockViewSet.authentication_classes is already: (JwtAuthentication, SessionAuthenticationAllowInactiveUser) — matches the ADR 0034 target pattern. The explicit declaration is preserved (rather than relying on platform defaults) because the endpoint needs SessionAuthenticationAllowInactiveUser rather than the default SessionAuthentication. Documented in the module docstring. No code changes required. --- cms/djangoapps/contentstore/rest_api/v1/views/xblock.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py b/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py index 025f2c4c4ca9..d00b37c10a2d 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/xblock.py @@ -8,6 +8,13 @@ * ADR 0026 - explicit authentication_classes + permission_classes * ADR 0028 - consolidated into XblockViewSet via DefaultRouter * ADR 0029 - standardized error envelope via StandardizedErrorMixin + * ADR 0034 - already compliant. ``authentication_classes`` is + ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — no + ``BearerAuthentication`` / ``OAuth2Authentication`` to remove. This view + is set explicitly (rather than relying on platform defaults) because it + needs ``SessionAuthenticationAllowInactiveUser`` instead of the default + ``SessionAuthentication`` so inactive Studio authors can still hit the + endpoint while their session is being verified. * ADR 0036 - minimal/flattened views. ``retrieve`` accepts a ``?view=minimal`` query parameter that strips the (tree-shaped) xblock response to a small set of structural fields. The full xblock response is kept as the default From cd5498c206b6b76b43b14bb0dc153d909cdd26a1 Mon Sep 17 00:00:00 2001 From: Taimoor Ahmed Date: Tue, 23 Jun 2026 12:04:24 +0500 Subject: [PATCH 2/6] docs(adr-0034): audit CourseHome v3 against auth standardization (already compliant) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HomeViewSet.authentication_classes is already ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — matches the ADR 0034 target. No Bearer or OAuth2 authentication classes to remove. Explicit declaration kept (rather than relying on platform defaults) so SessionAuthenticationAllowInactiveUser is used instead of the default SessionAuthentication. Documented in the module docstring. --- cms/djangoapps/contentstore/rest_api/v3/views/home.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cms/djangoapps/contentstore/rest_api/v3/views/home.py b/cms/djangoapps/contentstore/rest_api/v3/views/home.py index de5158c17e58..fcf09a0e617f 100644 --- a/cms/djangoapps/contentstore/rest_api/v3/views/home.py +++ b/cms/djangoapps/contentstore/rest_api/v3/views/home.py @@ -18,6 +18,12 @@ explicitly. The flat-list ``courses`` and ``libraries`` actions are out of scope (single-key dict around a list) and do not honour ``?fields=``. + * ADR 0034 – already compliant. ``authentication_classes`` is + ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — + no ``BearerAuthentication`` / ``OAuth2Authentication`` to remove. + The explicit declaration is kept (rather than relying on platform + defaults) so that ``SessionAuthenticationAllowInactiveUser`` is used + instead of the default ``SessionAuthentication``. """ import edx_api_doc_tools as apidocs From 9969811f34b7991489698c039a1a4ef851569760 Mon Sep 17 00:00:00 2001 From: Taimoor Ahmed Date: Tue, 23 Jun 2026 12:05:03 +0500 Subject: [PATCH 3/6] docs(adr-0034): audit CourseHome v4 against auth standardization (already compliant) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HomeCoursesViewSet.authentication_classes is already ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — matches the ADR 0034 target. No Bearer or OAuth2 authentication classes to remove. Documented in the class's ADR-compliance docstring. --- cms/djangoapps/contentstore/rest_api/v4/views/home.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cms/djangoapps/contentstore/rest_api/v4/views/home.py b/cms/djangoapps/contentstore/rest_api/v4/views/home.py index 28fab47b55c0..7617cd6bd850 100644 --- a/cms/djangoapps/contentstore/rest_api/v4/views/home.py +++ b/cms/djangoapps/contentstore/rest_api/v4/views/home.py @@ -128,6 +128,11 @@ class HomeCoursesViewSet(StandardizedErrorMixin, viewsets.ViewSet): a dynamic-fields serializer mixin and per-field schema documentation) but is intentionally NOT added here to keep the v4 contract stable for the existing Studio frontend. + - 0034: already compliant. ``authentication_classes`` is + ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — no + ``BearerAuthentication`` / ``OAuth2Authentication`` to remove. + Explicit declaration kept so ``SessionAuthenticationAllowInactiveUser`` + is used instead of the default ``SessionAuthentication``. """ authentication_classes = (JwtAuthentication, SessionAuthenticationAllowInactiveUser) From 1a94972ecf7950de998752aaea746e8b118315ab Mon Sep 17 00:00:00 2001 From: Taimoor Ahmed Date: Tue, 23 Jun 2026 12:06:40 +0500 Subject: [PATCH 4/6] feat(adr-0034): remove BearerAuthentication from Enrollment v2 viewsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR 0034 (Unify Auth — OEP-0042 alignment) deprecates BearerAuthenticationAllowInactiveUser. All four enrollment v2 views had the pattern (JwtAuthentication, BearerAuthenticationAllowInactiveUser, EnrollmentCrossDomainSessionAuth); per the ADR's "Standard API" target, the Bearer entry is removed and the rest of the tuple kept unchanged. Updated: * EnrollmentViewSet (list / create / unenroll / allowed) * EnrollmentRetrieveView (GET /enrollment/{course_key}) * UserRolesView (GET /roles/) * EnrollmentsAdminListView (GET /enrollments/, admin) EnrollmentCrossDomainSessionAuth is retained (not replaced by the platform-default SessionAuthentication) because these endpoints must accept cross-domain Studio/LMS CSRF-validated session cookies. Each viewset carries an inline comment explaining the deviation. Dropped the now-unused BearerAuthenticationAllowInactiveUser import. Module docstring updated to reflect ADR 0034 compliance. No new dependencies. No behaviour changes for legitimate JWT or session callers; Bearer-token callers (deprecated per OEP-0042) must migrate to JWT. --- .../core/djangoapps/enrollments/v2/views.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/openedx/core/djangoapps/enrollments/v2/views.py b/openedx/core/djangoapps/enrollments/v2/views.py index 905452f1d32c..67af3006a025 100644 --- a/openedx/core/djangoapps/enrollments/v2/views.py +++ b/openedx/core/djangoapps/enrollments/v2/views.py @@ -6,6 +6,13 @@ * ADR 0025 – ``serializer_class`` on every viewset/view * ADR 0026 – explicit ``authentication_classes`` + ``permission_classes`` + * ADR 0034 – auth standardization (OEP-0042). All four v2 viewsets/views use + ``(JwtAuthentication, EnrollmentCrossDomainSessionAuth)``; + ``BearerAuthenticationAllowInactiveUser`` has been removed per the + deprecation policy. ``EnrollmentCrossDomainSessionAuth`` is retained + (rather than relying on the platform-default ``SessionAuthentication``) + because these endpoints must accept cross-domain Studio/LMS CSRF-validated + session cookies. * ADR 0027 – ``drf_spectacular`` for OpenAPI schema generation * ADR 0028 – consolidated into ``ViewSet`` classes registered via ``DefaultRouter`` where the URL shape allows it @@ -70,7 +77,6 @@ EnrollmentUserThrottle, ) from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser -from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser from openedx.core.lib.api.mixins import StandardizedErrorMixin from openedx.core.lib.api.permissions import ApiKeyHeaderPermissionIsAuthenticated @@ -209,9 +215,12 @@ class EnrollmentViewSet(StandardizedErrorMixin, viewsets.ViewSet, ApiKeyPermissi DELETE /api/enrollment/v2/enrollment/enrollment_allowed/ → allowed (DELETE) """ + # ADR 0034 — JWT + cross-domain session (BearerAuthenticationAllowInactiveUser + # removed per OEP-0042). EnrollmentCrossDomainSessionAuth retained because the + # endpoint must accept cross-domain Studio/LMS CSRF-validated session cookies; + # the platform-default SessionAuthentication would reject those. authentication_classes = ( JwtAuthentication, - BearerAuthenticationAllowInactiveUser, EnrollmentCrossDomainSessionAuth, ) permission_classes = (ApiKeyHeaderPermissionIsAuthenticated,) @@ -417,9 +426,12 @@ def allowed(self, request): class EnrollmentRetrieveView(StandardizedErrorMixin, ApiKeyPermissionMixIn, APIView): """GET enrollment for a course (and optionally a named user).""" + # ADR 0034 — JWT + cross-domain session (BearerAuthenticationAllowInactiveUser + # removed per OEP-0042). EnrollmentCrossDomainSessionAuth retained because the + # endpoint must accept cross-domain Studio/LMS CSRF-validated session cookies; + # the platform-default SessionAuthentication would reject those. authentication_classes = ( JwtAuthentication, - BearerAuthenticationAllowInactiveUser, EnrollmentCrossDomainSessionAuth, ) permission_classes = (ApiKeyHeaderPermissionIsAuthenticated,) @@ -492,9 +504,12 @@ def get(self, request, course_id=None, username=None): class UserRolesView(StandardizedErrorMixin, APIView): """List the current user's course-level roles.""" + # ADR 0034 — JWT + cross-domain session (BearerAuthenticationAllowInactiveUser + # removed per OEP-0042). EnrollmentCrossDomainSessionAuth retained because the + # endpoint must accept cross-domain Studio/LMS CSRF-validated session cookies; + # the platform-default SessionAuthentication would reject those. authentication_classes = ( JwtAuthentication, - BearerAuthenticationAllowInactiveUser, EnrollmentCrossDomainSessionAuth, ) permission_classes = (ApiKeyHeaderPermissionIsAuthenticated,) @@ -644,9 +659,12 @@ def get(self, request, course_id=None): class EnrollmentsAdminListView(StandardizedErrorMixin, ListAPIView): """Admin-only paginated enrollment list with OEP-68 filter aliases.""" + # ADR 0034 — JWT + cross-domain session (BearerAuthenticationAllowInactiveUser + # removed per OEP-0042). EnrollmentCrossDomainSessionAuth retained because the + # endpoint must accept cross-domain Studio/LMS CSRF-validated session cookies; + # the platform-default SessionAuthentication would reject those. authentication_classes = ( JwtAuthentication, - BearerAuthenticationAllowInactiveUser, EnrollmentCrossDomainSessionAuth, ) permission_classes = (permissions.IsAdminUser,) From d72e5b16a7ad5dba7a5e82544b803ba79c025074 Mon Sep 17 00:00:00 2001 From: Taimoor Ahmed Date: Tue, 23 Jun 2026 12:07:48 +0500 Subject: [PATCH 5/6] docs(adr-0034): audit Course Detail v3 against auth standardization (already compliant) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CourseDetailsViewSet.authentication_classes is already ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — matches the ADR 0034 target. No Bearer or OAuth2 authentication classes to remove. Documented in the module docstring. --- .../contentstore/rest_api/v3/views/course_details.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cms/djangoapps/contentstore/rest_api/v3/views/course_details.py b/cms/djangoapps/contentstore/rest_api/v3/views/course_details.py index 79a1f0ed1cba..ae31a1f7d0e1 100644 --- a/cms/djangoapps/contentstore/rest_api/v3/views/course_details.py +++ b/cms/djangoapps/contentstore/rest_api/v3/views/course_details.py @@ -12,6 +12,12 @@ * ADR 0029 – standardized error envelope via :class:`StandardizedErrorMixin` (v3-scoped — does not change the project-wide DRF ``EXCEPTION_HANDLER`` setting) + * ADR 0034 – already compliant. ``authentication_classes`` is + ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)`` — no + ``BearerAuthentication`` / ``OAuth2Authentication`` to remove. + Explicit declaration kept (rather than relying on platform defaults) + so that ``SessionAuthenticationAllowInactiveUser`` is used instead of + the default ``SessionAuthentication``. Permission model note: PR #38365 proposed a class-level ``HasStudioReadAccess`` permission. The From ba938e3e3a7bf59c8a07e58d0bd77b79885bdd76 Mon Sep 17 00:00:00 2001 From: Taimoor Ahmed Date: Tue, 23 Jun 2026 12:10:42 +0500 Subject: [PATCH 6/6] feat(adr-0034): remove BearerAuthentication from AuthorGrading v3 viewset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR 0034 (Unify Auth — OEP-0042 alignment) deprecates BearerAuthenticationAllowInactiveUser. AuthoringGradingViewSet had the pattern (JwtAuthentication, BearerAuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser); per the ADR's "Standard API" target the Bearer entry is removed and the rest of the tuple kept unchanged. SessionAuthenticationAllowInactiveUser is retained (rather than relying on the platform-default SessionAuthentication) so Studio authors whose accounts are temporarily inactive can still update grading; an inline comment records the deviation. Dropped the now-unused BearerAuthenticationAllowInactiveUser import. Module docstring updated to reflect ADR 0034 compliance. No new dependencies. No behaviour changes for JWT or session callers; Bearer-token callers (deprecated per OEP-0042) must migrate to JWT. --- .../rest_api/v3/views/authoring_grading.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cms/djangoapps/contentstore/rest_api/v3/views/authoring_grading.py b/cms/djangoapps/contentstore/rest_api/v3/views/authoring_grading.py index 72a4039c9ff3..e92272e6f8bd 100644 --- a/cms/djangoapps/contentstore/rest_api/v3/views/authoring_grading.py +++ b/cms/djangoapps/contentstore/rest_api/v3/views/authoring_grading.py @@ -30,6 +30,13 @@ real-world payload is always small. A hard cap is enforced upstream of this endpoint by :func:`CourseGradingModel.update_from_json`; we surface that as a documentation note rather than re-implement the bound here. + * ADR 0034 – auth standardization (OEP-0042). + ``authentication_classes`` is ``(JwtAuthentication, SessionAuthenticationAllowInactiveUser)``; + ``BearerAuthenticationAllowInactiveUser`` has been removed per the + deprecation policy. ``SessionAuthenticationAllowInactiveUser`` is + retained (rather than relying on the platform-default + ``SessionAuthentication``) so Studio authors whose accounts are + temporarily inactive can still update grading. Permission model note: PR #38363 proposed a class-level ``HasStudioReadAccess`` permission. The @@ -63,7 +70,6 @@ from openedx.core.djangoapps.authz.constants import LegacyAuthoringPermission from openedx.core.djangoapps.authz.decorators import user_has_course_permission from openedx.core.djangoapps.credit.tasks import update_credit_course_requirements -from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser from openedx.core.lib.api.mixins import StandardizedErrorMixin _COURSE_KEY_PARAMETER = OpenApiParameter( @@ -87,9 +93,12 @@ class AuthoringGradingViewSet(StandardizedErrorMixin, viewsets.ViewSet): Supersedes ``AuthoringGradingView`` at ``POST /api/contentstore/v0/grading/{course_id}``. """ + # ADR 0034 — JWT + session-with-inactive-user (BearerAuthenticationAllowInactiveUser + # removed per OEP-0042). SessionAuthenticationAllowInactiveUser is retained + # (instead of relying on the platform-default SessionAuthentication) so Studio + # authors whose accounts are temporarily inactive can still update grading. authentication_classes = ( JwtAuthentication, - BearerAuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser, ) permission_classes = (IsAuthenticated,)