instructure/canvas-lms · error · OAuthValidationError

Missing claims

Error message

Missing claims %{claims}

What it means

Raised in open_id_connect.rb claims() when the provider's ID token lacks one or more of the mandatory OIDC claims: aud, iss, iat, exp, nonce. Canvas validates these before using the token, because a spec-compliant ID token must contain them for audience/issuer/time/nonce verification.

Solutions

  1. Fix the IdP configuration so the ID token includes aud, iss, iat, exp, and nonce (enable nonce issuance on the IdP).
  2. Verify the callback is decoding the id_token, not the access token (check response_type/protocol mapping in provider settings).
  3. Check for IdP-side claims-mapping policies that drop standard claims and re-add them.
  4. Test the raw ID token (e.g. jwt.io) to confirm which claims are actually present.

Example fix

// before: IdP emits ID token without nonce
{ "aud": "client-id", "iss": "https://idp", "iat": 1700000000, "exp": 1700003600 }
// after: enable nonce in IdP OIDC settings
{ "aud": "client-id", "iss": "https://idp", "iat": 1700000000, "exp": 1700003600, "nonce": "abc123" }
Defensive patterns

Strategy: validation

Validate before calling

claims = JSON.parse(Base64.urlsafe_decode64(id_token.split(".")[1]))
missing = %w[aud iss iat exp nonce] - claims.keys
raise "ID token missing claims: #{missing.join(', ')}" unless missing.empty?

Try / catch

begin
  provider.claims(token)
rescue OAuthValidationError => e
  Rails.logger.error("OIDC claims validation failed: #{e.message}")
  redirect_to login_path, alert: "Login failed: identity provider token invalid."
end

Prevention

When it happens

Trigger: unique_id, persist_to_session, or provider_attributes call claims(token); unverified_id_token decodes the JWT payload and the %w[aud iss iat exp nonce] - keys diff is non-empty (e.g. a nonstandard IdP omits nonce or iat), raising with the missing claim names joined by comma.

Common situations: Identity provider that is not fully OIDC compliant omits nonce (common with hand-rolled or older IdPs); token endpoint returns an access token where an ID token is expected; misconfigured response_type causing the callback to inspect the wrong token; IdP strips claims via token transformation policies.

Related errors


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

Appendix: source

Thrown at app/models/authentication_provider/open_id_connect.rb:277

      nil
    rescue JSON::JWK::Set::KidNotFound => e
      tries += 1
      if tries == 2
        download_jwks(force: true)
        retry
      end
      e.message
    rescue JSON::JWT::VerificationFailed => e
      e.message
    end

    def claims(token)
      token.options[:claims] ||= begin
        id_token = unverified_id_token(token)

        unless (missing_claims = %w[aud iss iat exp nonce] - id_token.keys).empty?
          raise OAuthValidationError, t({ one: "Missing claim %{claims}", other: "Missing claims %{claims}" },
                                        count: missing_claims.length,
                                        claims: missing_claims.join(", "))
        end

        unless Array(id_token["aud"]).include?(client_id)
          raise OAuthValidationError, t("Invalid JWT audience: %{audience}", audience: id_token["aud"].inspect)
        end

        if self.class.validate_issuer?
          if issuer.blank?
            raise OAuthValidationError, t("No issuer configured for OpenID Connect provider")
          end
          unless issuer === id_token["iss"] # rubocop:disable Style/CaseEquality -- may be a string or a RegEx
            raise OAuthValidationError, t("Invalid JWT issuer: %{issuer}", issuer: id_token["iss"])
          end
        end
        unless id_token["nonce"] == token.options[:nonce]
          raise OAuthValidationError, t("Invalid nonce claim in ID Token")

View on GitHub (pinned to 1c9f0bb801)