instructure/canvas-lms · error · OAuthValidationError

Invalid JWT audience

Error message

Invalid JWT audience: %{audience}

What it means

Raised in claims() when the ID token's aud (audience) claim does not include this provider's configured client_id. Canvas requires that the ID token was issued specifically for its OAuth client; otherwise the token may belong to another app and is rejected as an audience mismatch.

Solutions

  1. Confirm the client_id configured on the Canvas OpenID Connect provider exactly matches the aud claim in the rejected token.
  2. Re-issue credentials on the IdP and update client_id/client_secret in Canvas, then retry login.
  3. On the IdP, ensure the token is issued to the correct application (check azp/authorized party settings).
  4. Decode the failing ID token and compare aud against the provider settings before changing anything.

Example fix

# before (Canvas provider settings)
client_id: "old-client-id"
# after
client_id: "the-client-id-matching-aud-claim"
Defensive patterns

Strategy: validation

Validate before calling

payload = JSON.parse(Base64.urlsafe_decode64(id_token.split(".")[1]))
aud = Array(payload["aud"])
raise "aud mismatch: #{payload['aud'].inspect} vs #{CLIENT_ID}" unless aud.include?(CLIENT_ID)

Try / catch

begin
  provider.claims(token)
rescue OAuthValidationError => e
  Rails.logger.error("OIDC audience validation failed: #{e.message}")
  head :unauthorized
end

Prevention

When it happens

Trigger: claims(token) is invoked from unique_id/persist_to_session/provider_attributes; Array(id_token['aud']).include?(client_id) is false — the aud claim holds a different client ID, or the provider's client_id was changed/rotated after the token was issued.

Common situations: Admin regenerated OAuth credentials and the Canvas provider still has the old client_id (or vice versa); IdP issues tokens with azp/aud pointing to another registered app; copy-paste of client_id with whitespace or from the wrong app registration; multiple Canvas OpenID Connect providers sharing one IdP app.

Related errors


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

Appendix: source

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

        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")
        end

        if (signature_error = validate_signature(id_token))
          raise OAuthValidationError, t("Invalid signature: %{signature_error}", signature_error:)
        end

View on GitHub (pinned to 1c9f0bb801)