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
- Fix the IdP configuration so the ID token includes aud, iss, iat, exp, and nonce (enable nonce issuance on the IdP).
- Verify the callback is decoding the id_token, not the access token (check response_type/protocol mapping in provider settings).
- Check for IdP-side claims-mapping policies that drop standard claims and re-add them.
- 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
- Verify IdP OIDC compliance (nonce, iat, exp issuance) before integration
- Decode a sample ID token at jwt.io during setup
- Ensure the callback handles id_token, not the access token
- Check IdP claims-mapping policies do not drop standard claims
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)