instructure/canvas-lms · error · AccessTokenError

masquerading user not found

Error message

masquerading user not found

What it means

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.

Solutions

  1. Re-mint the InstAccess token after the masquerading user's data changes or deletion
  2. Verify masquerading_user_shard_id matches the shard where the user's UUID is stored
  3. Check find_user_by_uuid_prefer_local resolution and confirm the user record exists in the Rails console
  4. Reject/refresh stale tokens instead of replaying long-lived ones

Example fix

// before
real_user = find_user_by_uuid_prefer_local(token.masquerading_user_uuid)
// after
real_user = find_user_by_uuid_prefer_local(token.masquerading_user_uuid)
raise AccessTokenError, "masquerading user not found" unless real_user // add caller-side rescue
rescue AccessTokenError => e
  render error and force token refresh
Defensive patterns

Strategy: try-catch

Validate before calling

if token.masquerading_user_uuid
  user = User.where(uuid: token.masquerading_user_uuid).shard(token.masquerading_user_shard_id).exists?
  raise "stale token" unless user
end

Try / catch

begin
  auth_context = InstAccessToken.authenticate!(token)
rescue AccessTokenError => e
  render json: { error: e.message }, status: :unauthorized
end

Prevention

When it happens

Trigger: 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.

Common situations: Users deleted between token issuance and use; cross-environment token reuse (test token in prod); shard misconfiguration; token minted before a data migration.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/aa1f5d48bee2727f. Report an issue: GitHub.

Appendix: source

Thrown at lib/authentication_methods/inst_access_token.rb:89

    def self.load_user_and_pseudonym_context(token, domain_root_account)
      auth_context = {
        current_user: nil,
        current_pseudonym: nil,
        real_current_user: nil,
        real_current_pseudonym: nil
      }
      auth_context[:current_user] = find_user_by_uuid_prefer_local(token.user_uuid)
      return auth_context unless auth_context[:current_user]

      auth_context[:current_pseudonym] = SisPseudonym.for(
        auth_context[:current_user], domain_root_account, type: :implicit, require_sis: false
      )
      return auth_context unless auth_context[:current_pseudonym]

      if token.masquerading_user_uuid && token.masquerading_user_shard_id
        Shard.lookup(token.masquerading_user_shard_id).activate do
          real_user = find_user_by_uuid_prefer_local(token.masquerading_user_uuid)
          raise AccessTokenError, "masquerading user not found" unless real_user

          auth_context[:real_current_user] = real_user
          auth_context[:real_current_pseudonym] = SisPseudonym.for(
            real_user, domain_root_account, type: :implicit, require_sis: false
          )
        end
      end
      auth_context
    end

    def self.usable_developer_key?(token, domain_root_account)
      # The token is not associated with a specific developer key
      return true if token.client_id.blank?

      DeveloperKey.find_cached(token.client_id).usable_in_context?(domain_root_account)
    rescue ActiveRecord::RecordNotFound
      # The developer key associated with the 'client_id' claim
      # does not exist or was deleted.

View on GitHub (pinned to 1c9f0bb801)