{"record":{"id":"3e3438500cd076ba","repo":"instructure/canvas-lms","slug":"must-be-logged-in-reject-enrollment-invitation","errorCode":null,"errorMessage":"Must be logged in","messagePattern":"Must be logged in","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/reject_enrollment_invitation.rb","lineNumber":30,"sourceCode":"# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\n# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n#\n\nmodule Mutations\n  class RejectEnrollmentInvitation < BaseMutation\n    argument :enrollment_uuid, String, required: true\n\n    field :enrollment, Types::EnrollmentType, null: true\n    field :success, Boolean, null: false\n\n    def resolve(input:, **)\n      user = context[:current_user]\n      raise GraphQL::ExecutionError, I18n.t(\"Must be logged in\") unless user\n\n      enrollment = Enrollment.where(uuid: input[:enrollment_uuid]).first\n      raise GraphQL::ExecutionError, I18n.t(\"Enrollment invitation not found\") unless enrollment\n\n      # Verify the enrollment belongs to the current user\n      raise GraphQL::ExecutionError, I18n.t(\"Unauthorized\") unless enrollment.user == user\n\n      # Verify the enrollment is in invited state\n      raise GraphQL::ExecutionError, I18n.t(\"Enrollment is not in invited state\") unless enrollment.invited?\n\n      begin\n        if enrollment.reject\n          {\n            enrollment:,\n            success: true\n          }\n        else\n          {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/reject_enrollment_invitation.rb#L12-L48","documentation":"Raised in the RejectEnrollmentInvitation mutation when context[:current_user] is nil, meaning the GraphQL request is unauthenticated. The mutation refuses to proceed without a logged-in user before doing any enrollment lookup.","triggerScenarios":"Calling rejectEnrollmentInvitation without a valid session or access token; expired Canvas session; API token omitted/invalid on the GraphQL endpoint.","commonSituations":"Deep-linking a user to an invitation-action page after their session expired; server-side scripts calling the GraphQL endpoint without Authorization header; cookies blocked or cleared.","solutions":["Authenticate the request: ensure a valid Canvas session cookie or Bearer access token is sent with the GraphQL call","Refresh an expired session (re-login) before retrying","For scripts, generate a developer-key/token and pass it in the Authorization header","Check the client redirects to login when current_user is nil"],"exampleFix":"// before\nfetch('/api/graphql', { method: 'POST', body }) // no auth\n// after\nfetch('/api/graphql', { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body })","handlingStrategy":"try-catch","validationCode":"if (!currentUser) redirectToLogin()","typeGuard":"function isAuthenticated(ctx) { return Boolean(ctx?.currentUser?.id) }","tryCatchPattern":"try {\n  await rejectEnrollmentInvitation({ enrollmentUuid })\n} catch (e) {\n  if (e.message.includes('Must be logged in')) { redirectToLogin(); return }\n  throw e\n}","preventionTips":["Gate invitation-action pages behind an auth check","Refresh expired sessions before mutating requests","Attach valid Bearer tokens for API/GraphQL automation","Handle 401-like GraphQL errors with a login redirect"],"tags":["graphql","authentication","enrollments"],"backgroundTag":"authentication-required","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"}