{"record":{"id":"aa1f5d48bee2727f","repo":"instructure/canvas-lms","slug":"masquerading-user-not-found","errorCode":null,"errorMessage":"masquerading user not found","messagePattern":"masquerading user not found","errorType":"exception","errorClass":"AccessTokenError","httpStatus":401,"severity":"error","filePath":"lib/authentication_methods/inst_access_token.rb","lineNumber":89,"sourceCode":"    def self.load_user_and_pseudonym_context(token, domain_root_account)\n      auth_context = {\n        current_user: nil,\n        current_pseudonym: nil,\n        real_current_user: nil,\n        real_current_pseudonym: nil\n      }\n      auth_context[:current_user] = find_user_by_uuid_prefer_local(token.user_uuid)\n      return auth_context unless auth_context[:current_user]\n\n      auth_context[:current_pseudonym] = SisPseudonym.for(\n        auth_context[:current_user], domain_root_account, type: :implicit, require_sis: false\n      )\n      return auth_context unless auth_context[:current_pseudonym]\n\n      if token.masquerading_user_uuid && token.masquerading_user_shard_id\n        Shard.lookup(token.masquerading_user_shard_id).activate do\n          real_user = find_user_by_uuid_prefer_local(token.masquerading_user_uuid)\n          raise AccessTokenError, \"masquerading user not found\" unless real_user\n\n          auth_context[:real_current_user] = real_user\n          auth_context[:real_current_pseudonym] = SisPseudonym.for(\n            real_user, domain_root_account, type: :implicit, require_sis: false\n          )\n        end\n      end\n      auth_context\n    end\n\n    def self.usable_developer_key?(token, domain_root_account)\n      # The token is not associated with a specific developer key\n      return true if token.client_id.blank?\n\n      DeveloperKey.find_cached(token.client_id).usable_in_context?(domain_root_account)\n    rescue ActiveRecord::RecordNotFound\n      # The developer key associated with the 'client_id' claim\n      # does not exist or was deleted.","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/authentication_methods/inst_access_token.rb#L71-L107","documentation":"When an InstAccessToken declares a masquerading (real) user via masquerading_user_uuid/shard_id, the token handler resolves that user on the specified shard. If no user with that UUID exists on that shard, AccessTokenError 'masquerading user not found' is raised and the request is rejected.","triggerScenarios":"JWT/InstAccess token containing a masquerading_user_uuid that no longer exists (deleted user), a wrong shard_id, or a UUID from a different environment; tokens minted in one environment replayed in another.","commonSituations":"Users deleted between token issuance and use; cross-environment token reuse (test token in prod); shard misconfiguration; token minted before a data migration.","solutions":["Re-mint the InstAccess token after the masquerading user's data changes or deletion","Verify masquerading_user_shard_id matches the shard where the user's UUID is stored","Check find_user_by_uuid_prefer_local resolution and confirm the user record exists in the Rails console","Reject/refresh stale tokens instead of replaying long-lived ones"],"exampleFix":"// before\nreal_user = find_user_by_uuid_prefer_local(token.masquerading_user_uuid)\n// after\nreal_user = find_user_by_uuid_prefer_local(token.masquerading_user_uuid)\nraise AccessTokenError, \"masquerading user not found\" unless real_user // add caller-side rescue\nrescue AccessTokenError => e\n  render error and force token refresh","handlingStrategy":"try-catch","validationCode":"if token.masquerading_user_uuid\n  user = User.where(uuid: token.masquerading_user_uuid).shard(token.masquerading_user_shard_id).exists?\n  raise \"stale token\" unless user\nend","typeGuard":null,"tryCatchPattern":"begin\n  auth_context = InstAccessToken.authenticate!(token)\nrescue AccessTokenError => e\n  render json: { error: e.message }, status: :unauthorized\nend","preventionTips":["Keep token lifetimes short for masqueraded access","Refresh tokens when the masquerading user is modified or deleted","Ensure shard routing is consistent between token minting and consumption","Never replay tokens across environments"],"tags":["authentication","inst-access","masquerade","jwt"],"backgroundTag":"user-not-found","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"}