{"record":{"id":"9cccfcb00f55ab37","repo":"instructure/canvas-lms","slug":"the-current-user-has-changed","errorCode":null,"errorMessage":"The current user has changed","messagePattern":"The current user has changed","errorType":"exception","errorClass":"OembedAuthorizationError","httpStatus":null,"severity":"error","filePath":"app/controllers/lti/concerns/oembed.rb","lineNumber":38,"sourceCode":"module Lti::Concerns\n  module Oembed\n    class OembedAuthorizationError < StandardError; end\n\n    # Does standard JWT validation and also verifies\n    # the current user is the same user that the\n    # tool issued a token for\n    #\n    # Validating this token helps Canvas ensure that\n    # an authorized tool is requesting oembed object\n    # embedding.\n    def validate_oembed_token!\n      error_message ||= jwt_validator.error_message unless jwt_validator.valid?\n      error_message = \"The current user has changed\" unless same_user?\n\n      return if error_message.blank?\n\n      log_error(error_message)\n      raise OembedAuthorizationError, error_message\n    end\n\n    def log_error(message)\n      logger.warn \"[OEmbed] #{message}\"\n    end\n\n    def jwt_validator\n      @jwt_validator ||= Canvas::Security::JwtValidator.new(\n        jwt: verified_jwt,\n        expected_aud: Canvas::Security.config[\"lti_iss\"],\n        require_iss: true\n      )\n    end\n\n    def oembed_endpoint\n      uri_source[:endpoint]\n    end\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/lti/concerns/oembed.rb#L20-L56","documentation":"LTI OEmbed controllers validate the signed request token via JwtValidator; even with a cryptographically valid token, validate_oembed_token! additionally checks that the token's user matches the currently logged-in user (same_user?). If they differ, the error message is set to \"The current user has changed\", logged, and OembedAuthorizationError is raised to reject the request.","triggerScenarios":"validate_oembed_token! runs when a user's session user differs from the user embedded in the OEmbed JWT — e.g., the token was minted for user A but the request arrives under user B's session (impersonation started mid-flow, user switched accounts in another tab, or session changed between token issuance and request).","commonSituations":"Admin masquerading as a user while an LTI tool iframe holds the original user's token; a user logging out and back in as another account while an OEmbed request is in flight; tools caching tokens across user sessions.","solutions":["Have the LTI tool re-request/re-launch to mint a fresh token for the current user","Verify same_user? comparison uses consistent identifiers (LTI user id vs Canvas id) — a mismatch in id types can falsely trigger it","Avoid initiating impersonation in a session with an active LTI OEmbed flow, or relaunch the tool after starting impersonation","Check that the JWT validator populated the correct user claim (sub/custom user id) at token generation"],"exampleFix":"// before\n# tool reuses a cached JWT minted for the previous session user\nfetchOembed(cachedToken) // -> OembedAuthorizationError\n// after\n# mint a token for the current user at request time\nconst token = await launchLti({ user: currentUser })\nfetchOembed(token)","handlingStrategy":"try-catch","validationCode":"// before making the OEmbed request, confirm the session user still matches the token\nif (tokenUserId !== currentUser.ltiUserId) {\n  await relaunchTool(); // mint a fresh token for the current user\n  return;\n}","typeGuard":"function tokenMatchesCurrentUser(token, currentUser) {\n  const claims = decodeJwt(token);\n  return claims.sub === currentUser.ltiUserId;\n}","tryCatchPattern":"begin\n  validate_oembed_token!\nrescue Lti::OembedAuthorizationError => e\n  render json: { error: e.message }, status: :unauthorized\nend","preventionTips":["Relaunch LTI tools after impersonation starts or account switches","Never cache OEmbed JWTs across user sessions","Use consistent user identifier types between token claims and session"],"tags":["lti","oembed","jwt","authorization"],"backgroundTag":"jwt-token-expired","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"}