instructure/canvas-lms · error · BasicLTI::BasicOutcomes::Unauthorized
Invalid logout service token
Error message
Invalid logout service token
What it means
LogoutService.parse_and_validate deserializes a signed, hyphen-joined token containing tool id, pseudonym id, timestamp, nonce and an HMAC-SHA1 signature computed with the tool shard's encryption_key. It raises BasicLTI::BasicOutcomes::Unauthorized when the token doesn't have exactly 5 parts or the recomputed HMAC does not match the final part.
Solutions
- Verify the tool shard settings[:encryption_key] is the same key used when the token was generated
- Regenerate the logout service token after rotating the encryption key
- Check the serialized token has exactly 5 hyphen-separated parts with no stray hyphens in payload values
- Rescue BasicLTI::BasicOutcomes::Unauthorized and reject the logout request with 401
Defensive patterns
Strategy: try-catch
Validate before calling
valid = token.count("-") == 4 # 5 parts expected Try / catch
begin token = Lti::LogoutService.parse_and_validate(serialized) rescue BasicLTI::BasicOutcomes::Unauthorized head :unauthorized end
Prevention
- Keep shard encryption_key stable or regenerate tokens after rotation
- Never transport tokens across environments
- Rescue BasicLTI::BasicOutcomes::Unauthorized on all token parsing paths
When it happens
Trigger: Passing a token with wrong number of segments, or a token whose signature was computed with a different/outdated encryption key, or a tampered token, to LogoutService.parse_and_validate.
Common situations: Tool shard encryption_key rotated after token issuance; tokens copied across environments/shards; hand-built or truncated tokens; different shard settings lacking :encryption_key.
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
- Invalid analytics service token
- signature_invalid
- Access token expired
- Access token invalid - signature likely incorrect
- assignment_invalid
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/3e0091d3a3bdf209.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/lti/logout_service.rb:53
end
Token = Struct.new(:tool, :pseudonym, :timestamp, :nonce) do
def self.create(tool, pseudonym)
Token.new(tool, pseudonym, Time.zone.now, SecureRandom.hex(8))
end
def serialize
key = tool.shard.settings[:encryption_key]
payload = [tool.id, pseudonym.id, timestamp.to_i, nonce].join("-")
"#{payload}-#{Canvas::Security.hmac_sha1(payload, key)}"
end
def self.parse_and_validate(serialized_token)
parts = serialized_token.split("-")
tool = Lti::ToolFinder.find(parts[0].to_i)
key = tool.shard.settings[:encryption_key]
unless parts.size == 5 && Canvas::Security.hmac_sha1(parts[0..-2].join("-"), key) == parts[-1]
raise BasicLTI::BasicOutcomes::Unauthorized, "Invalid logout service token"
end
pseudonym = Pseudonym.find(parts[1].to_i)
timestamp = parts[2].to_i
nonce = parts[3]
unless Time.now.to_i - timestamp < Lti::LogoutService::TOKEN_EXPIRATION
raise BasicLTI::BasicOutcomes::Unauthorized, "Logout service token has expired"
end
Token.new(tool, pseudonym, timestamp, nonce)
end
end
Runner = Struct.new(:callbacks) do
def perform
callbacks.each_value do |callback|
InstrumentTLSCiphers.without_tls_metrics do
CanvasHttp.get(URI.parse(callback).to_s)View on GitHub (pinned to 1c9f0bb801)