instructure/canvas-lms · error · BasicLTI::Errors::InvalidSourceId

signature_invalid

signature_invalid

Error message

Invalid signature

What it means

Sourcedid.load_from_legacy_sourcedid! raises Errors::InvalidSourceId with :signature_invalid when the legacy sourcedid's HMAC-SHA1 signature does not verify against the tool shard's encryption_key. The token was tampered with, constructed with a different key, or the key changed.

Solutions

  1. Re-launch the tool in the correct environment to get a validly signed sourcedid.
  2. Confirm the sourcedid is being verified on the same shard that signed it (check tool.shard.settings[:encryption_key]).
  3. Do not modify sourcedid segments in transit; treat them as opaque strings.
  4. If keys were rotated, existing legacy sourcedids are invalid — migrate to JWT-based sourcedids.

Example fix

// before
sd = sourcedid_from_other_env # signed with different key
BasicLti::Sourcedid.load!(sd) # InvalidSourceId :signature_invalid
// after
sd = fresh_sourcedid_from_launch # signed by this environment's key
BasicLti::Sourcedid.load!(sd)
Defensive patterns

Strategy: try-catch

Try / catch

begin
  BasicLti::Sourcedid.load!(sourcedid)
rescue BasicLti::Sourcedid::Errors::InvalidSourceId => e
  request_fresh_launch if e.error_code == :signature_invalid
end

Prevention

When it happens

Trigger: Verifying md[5] via Canvas::Security.verify_hmac_sha1(md[5], new_encoding, key: tool.shard.settings[:encryption_key]) and it returns false — e.g. the sourcedid was signed on a shard/account with a different encryption key, or the id segments (md[1..4]) were altered.

Common situations: Copying sourcedids between Canvas environments (prod/test) with different encryption keys; shard settings[:encryption_key] regenerated; hand-crafted sourcedids in scripts; truncation that changes the signed payload.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at lib/basic_lti/sourcedid.rb:90

      if course
        user = course.student_enrollments.active.find_by(user_id: token[:user_id])&.user
        assignment = course.assignments.active.find_by(id: token[:assignment_id])
      end

      sourcedid = new(tool, course, assignment, user)
      sourcedid.validate!
      sourcedid
    end

    def self.load_from_legacy_sourcedid!(sourcedid)
      token = nil
      md = sourcedid.match(SOURCE_ID_REGEX)
      if md
        tool = Lti::ToolFinder.find_by(id: md[1])
        raise Errors::InvalidSourceId.new("Tool is invalid", :tool_invalid) unless tool

        new_encoding = [md[1], md[2], md[3], md[4]].join("-")
        raise Errors::InvalidSourceId.new("Invalid signature", :signature_invalid) unless Canvas::Security
                                                                                          .verify_hmac_sha1(md[5], new_encoding, key: tool.shard.settings[:encryption_key])

        token = { tool_id: md[1].to_i, course_id: md[2], assignment_id: md[3], user_id: md[4] }
      end
      token
    end

    def self.token_from_sourcedid!(sourcedid)
      Canvas::Security.decrypt_encrypted_jwt(
        Canvas::Security.base64_decode(sourcedid),
        signing_secret,
        encryption_secret
      )
    rescue JSON::JWT::InvalidFormat
      raise Errors::InvalidSourceId.new("Invalid sourcedid", :sourcedid_invalid)
    end

    def self.signing_secret

View on GitHub (pinned to 1c9f0bb801)