instructure/canvas-lms · error · InvalidTokenError
the following assertions are missing: #
Error message
the following assertions are missing: #{missing_assertions.join(",")} What it means
Lti::OAuth2::AccessToken#check_required_assertions (invoked from validate!) compares the JWT's claim keys against the required set [iss, sub, exp, aud, iat, nbf, jti] and raises InvalidTokenError listing whichever claims are missing. Canvas-minted tokens always include all of these, so absence signals a token not built by this class or a hand-rolled/incomplete JWT.
Solutions
- Include all required claims (iss, sub, exp, aud, iat, nbf, jti) when minting the token
- Prefer Lti::OAuth2::AccessToken.create_jwt / its jwt builder over hand-rolled JWT construction
- Decode the offending token and diff its claim keys against the required list to see exactly what to add
Example fix
// before
const payload = {iss: 'Canvas', sub, exp, aud};
// after
const payload = {iss: 'Canvas', sub, exp, aud, iat: Math.floor(Date.now()/1000), nbf: Math.floor(Date.now()/1000)-30, jti: crypto.randomUUID()}; Defensive patterns
Strategy: validation
Validate before calling
required = %w[iss sub exp aud iat nbf jti]
payload = JSON.parse(Base64.urlsafe_decode64(jwt.split('.')[1]))
missing = required - payload.keys
raise "missing claims: #{missing.join(',')}" if missing.any? Type guard
def complete_lti_jwt?(jwt)
payload = JSON.parse(Base64.urlsafe_decode64(jwt.split('.')[1]))
(%w[iss sub exp aud iat nbf jti] - payload.keys).empty?
rescue JSON::ParserError, ArgumentError
false
end Prevention
- Always mint via Lti::OAuth2::AccessToken.create_jwt so all claims are present
- Diff token claims against the required list in tests before validate!
- Update custom builders whenever Canvas adds required claims
When it happens
Trigger: Calling validate! on a JWT lacking one or more of iss/sub/exp/aud/iat/nbf/jti - the error message enumerates the missing keys.
Common situations: Hand-built or third-party JWTs fed to the Canvas-internal validator, SDKs emitting minimal claim sets, tests with truncated payloads, or tokens from a different Canvas version with a different claim set.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Access token expired
- Access token invalid - signature likely incorrect
- either the tool proxy or developer key were not found
- iat must be in the past
- Invalid access token field/s: #
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/a767e7209a001cad.
Report an issue: GitHub.
Appendix: source
Thrown at lib/lti/oauth2/access_token.rb:96
body = {
iss: ISS,
sub:,
exp: 1.hour.from_now,
aud:,
iat: Time.zone.now.to_i,
nbf: 30.seconds.ago,
jti: SecureRandom.uuid,
shard_id:
}
body[:reg_key] = @reg_key if @reg_key
Canvas::Security.create_jwt(body)
end
end
def check_required_assertions(assertion_keys)
missing_assertions = (%w[iss sub exp aud iat nbf jti] - assertion_keys)
if missing_assertions.present?
raise InvalidTokenError, "the following assertions are missing: #{missing_assertions.join(",")}"
end
end
end
end
end
View on GitHub (pinned to 1c9f0bb801)