{"record":{"id":"ced28bd7da8d2a9c","repo":"instructure/canvas-lms","slug":"signature-invalid","errorCode":"signature_invalid","errorMessage":"Invalid signature","messagePattern":"Invalid signature","errorType":"error_code","errorClass":"BasicLTI::Errors::InvalidSourceId","httpStatus":null,"severity":"error","filePath":"lib/basic_lti/sourcedid.rb","lineNumber":90,"sourceCode":"      if course\n        user = course.student_enrollments.active.find_by(user_id: token[:user_id])&.user\n        assignment = course.assignments.active.find_by(id: token[:assignment_id])\n      end\n\n      sourcedid = new(tool, course, assignment, user)\n      sourcedid.validate!\n      sourcedid\n    end\n\n    def self.load_from_legacy_sourcedid!(sourcedid)\n      token = nil\n      md = sourcedid.match(SOURCE_ID_REGEX)\n      if md\n        tool = Lti::ToolFinder.find_by(id: md[1])\n        raise Errors::InvalidSourceId.new(\"Tool is invalid\", :tool_invalid) unless tool\n\n        new_encoding = [md[1], md[2], md[3], md[4]].join(\"-\")\n        raise Errors::InvalidSourceId.new(\"Invalid signature\", :signature_invalid) unless Canvas::Security\n                                                                                          .verify_hmac_sha1(md[5], new_encoding, key: tool.shard.settings[:encryption_key])\n\n        token = { tool_id: md[1].to_i, course_id: md[2], assignment_id: md[3], user_id: md[4] }\n      end\n      token\n    end\n\n    def self.token_from_sourcedid!(sourcedid)\n      Canvas::Security.decrypt_encrypted_jwt(\n        Canvas::Security.base64_decode(sourcedid),\n        signing_secret,\n        encryption_secret\n      )\n    rescue JSON::JWT::InvalidFormat\n      raise Errors::InvalidSourceId.new(\"Invalid sourcedid\", :sourcedid_invalid)\n    end\n\n    def self.signing_secret","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/basic_lti/sourcedid.rb#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-launch the tool in the correct environment to get a validly signed sourcedid.","Confirm the sourcedid is being verified on the same shard that signed it (check tool.shard.settings[:encryption_key]).","Do not modify sourcedid segments in transit; treat them as opaque strings.","If keys were rotated, existing legacy sourcedids are invalid — migrate to JWT-based sourcedids."],"exampleFix":"// before\nsd = sourcedid_from_other_env # signed with different key\nBasicLti::Sourcedid.load!(sd) # InvalidSourceId :signature_invalid\n// after\nsd = fresh_sourcedid_from_launch # signed by this environment's key\nBasicLti::Sourcedid.load!(sd)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  BasicLti::Sourcedid.load!(sourcedid)\nrescue BasicLti::Sourcedid::Errors::InvalidSourceId => e\n  request_fresh_launch if e.error_code == :signature_invalid\nend","preventionTips":["Never move sourcedids between Canvas environments with different encryption keys.","Treat sourcedid segments as immutable; sign once at launch.","Coordinate shard encryption_key rotations with a sourcedid re-issue plan."],"tags":["lti","hmac","signature-verification"],"backgroundTag":"checksum-mismatch","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"}