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

  1. Include all required claims (iss, sub, exp, aud, iat, nbf, jti) when minting the token
  2. Prefer Lti::OAuth2::AccessToken.create_jwt / its jwt builder over hand-rolled JWT construction
  3. 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

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


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)