{"record":{"id":"287ffc79cfe8a076","repo":"instructure/canvas-lms","slug":"not-authorized-to-view-this-user-s-module-progress","errorCode":null,"errorMessage":"Not authorized to view this user's module progress","messagePattern":"Not authorized to view this user's module progress","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/types/course_type.rb","lineNumber":741,"sourceCode":"          # For unauthenticated users, only \"incomplete\" filter returns modules\n          # All other filters return empty since they have no progress\n          case filter[:completion_status]\n          when \"incomplete\"\n            return scope # All modules are incomplete for unauthenticated users\n          else\n            return scope.none # No completed/in_progress/not_started modules\n          end\n        end\n\n        target_user = if filter[:user_id]\n                        User.find(filter[:user_id])\n                      else\n                        current_user\n                      end\n\n        # Check permissions before applying filter\n        unless can_view_user_module_progress?(target_user)\n          raise GraphQL::ExecutionError, \"Not authorized to view this user's module progress\"\n        end\n\n        scope = Modules::FilterByCompletion.new(\n          scope,\n          filter[:completion_status],\n          target_user,\n          current_user,\n          course\n        ).filter\n      end\n\n      scope\n    end\n\n    def can_view_user_module_progress?(user)\n      # Users can always view their own progress\n      return true if user.id == current_user.id\n","sourceCodeStart":723,"sourceCodeEnd":759,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/types/course_type.rb#L723-L759","documentation":"apply_module_filters determines the target user (from filter user_id or the current user) and checks can_view_user_module_progress? before applying Modules::FilterByCompletion. If the requesting user lacks permission to view that target user's progress, it raises 'Not authorized to view this user's module progress'.","triggerScenarios":"Querying course.modules with filter.completion_status for a user_id the current user is not allowed to observe (not self, not an observer/student of that user, not a teacher in the course).","commonSituations":"Observer/parent tooling querying the wrong user id; students attempting to view classmates' progress; stale or global-id mismatch causing the permission lookup to fail.","solutions":["Query only your own module progress (omit user_id) unless you are a permitted observer/teacher","Verify the user_id global ID is correct and belongs to the same course","Check the enrolling user as an observer if viewing a student's progress","Confirm course-level permissions (teacher/admin) before filtering on another user"],"exampleFix":"// before\nmodules(filter: { completionStatus: \"incomplete\", userId: classmateGid })\n// after\nmodules(filter: { completionStatus: \"incomplete\" })  // own progress only","handlingStrategy":"try-catch","validationCode":"const isSelf = targetUserId === currentUser.id\nconst isPermittedObserver = currentUser.observerOf?.includes(targetUserId)\nconst isTeacher = currentUser.enrollments?.some(e => e.type === 'TeacherEnrollment' && e.courseId === courseId)\nif (!(isSelf || isPermittedObserver || isTeacher)) omitUserIdFilter()","typeGuard":"function canViewProgress(me, target, courseId) { return me?.id === target || me?.observerOf?.includes(target) || me?.enrollments?.some(e => e.type === 'TeacherEnrollment' && e.courseId === courseId) }","tryCatchPattern":"try { await query(MODULES_QUERY) } catch (e) { if (e.message.startsWith('Not authorized')) { fallBackToOwnProgress() } }","preventionTips":["Default to querying your own progress (omit user_id)","Verify observer/teacher enrollment before filtering on others","Validate the user_id global id belongs to the same course"],"tags":["graphql","authorization","modules"],"backgroundTag":"permission-denied","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}