instructure/canvas-lms · error · Canvas::OAuth::InvalidRequestError

assertion method not supported for this grant_type

Error message

assertion method not supported for this grant_type

What it means

Canvas's OAuth2 client_credentials grant rejects the request when the selected provider says the JWT 'client_assertion' (assertion) method is not permitted for this key's client_credentials audience. Each provider defines assertion_method_permitted?; e.g. SymmetricClientCredentialsProvider permits it only when the developer key's client_credentials_audience is 'external'. The token endpoint raises Canvas::OAuth::InvalidRequestError before any token is issued.

Solutions

  1. Open the developer key in Canvas admin and set its client_credentials audience to 'external' (or, for service auth, use the site-admin service key).
  2. If using JWT assertion, send client_assertion_type exactly as urn:ietf:params:oauth:client-assertion-type:jwt-bearer along with client_assertion.
  3. Verify you are hitting the token endpoint on the correct root account/shard where the key with the right audience is cached.
  4. If you do not intend assertion auth, authenticate with client_id + client_secret instead and ensure the key permits symmetric client credentials.

Example fix

// before
curl -d 'grant_type=client_credentials&client_id=123&client_secret=xyz' https://canvas/login/oauth2/token
// after (key configured with client_credentials_audience=external)
curl -d 'grant_type=client_credentials&client_assertion=<jwt>&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' https://canvas/login/oauth2/token
Defensive patterns

Strategy: validation

Validate before calling

def client_credentials_request_valid?(key, opts)
  assertion = opts[:client_assertion_type] == 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
  if assertion
    key.client_credentials_audience == 'external'
  else
    key.client_credentials_audience != 'external'
  end
end

Type guard

def jwt_assertion?(opts)
  opts.is_a?(Hash) && opts[:client_assertion_type] == 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' && opts[:client_assertion].present?
end

Try / catch

begin
  token = exchange_client_credentials(opts)
rescue Canvas::OAuth::InvalidRequestError => e
  logger.warn("client_credentials rejected: #{e.message}")
  # fix key config or fall back to symmetric flow
end

Prevention

When it happens

Trigger: POST to /login/oauth2/token with grant_type=client_credentials where (a) a JWT client_assertion is sent but the developer key's client_credentials_audience is not set to 'external' (falls through to SymmetricClientCredentialsProvider), (b) no client_assertion_type of urn:ietf:params:oauth:client-assertion-type:jwt-bearer is sent so a symmetric provider is chosen whose key audience disallows it, or (c) a service-user key path whose provider does not permit assertion auth.

Common situations: Key created without configuring client_credentials_audience; switching an integration from client_id/client_secret to JWT assertion without updating the key; copy of a key in a different account/shard missing the audience setting; typo in client_assertion_type URI so the asymmetric provider is never selected.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at lib/canvas/oauth/grant_types/client_credentials.rb:74

          )
        end

        Canvas::OAuth::SymmetricClientCredentialsProvider.new(client_id, host, scopes: scopes_from_opts(opts), protocol:)
      end

      def secret_for(provider, opts)
        provider.try(:secret) || opts[:client_secret]
      end

      def key_for(client_id)
        DeveloperKey.find_cached(client_id)
      rescue ::ActiveRecord::RecordNotFound
        nil
      end

      def validate_type
        unless @provider.assertion_method_permitted?
          raise Canvas::OAuth::InvalidRequestError, "assertion method not supported for this grant_type"
        end

        raise Canvas::OAuth::InvalidRequestError, @provider.error_message unless @provider.valid?
        raise Canvas::OAuth::InvalidScopeError, @provider.missing_scopes unless @provider.valid_scopes?
      end

      def generate_token
        @provider.generate_token
      end

      def basic_auth?(opts)
        opts[:client_assertion_type] != "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
      end

      def scopes_from_opts(opts)
        (opts[:scope] || opts[:scopes] || "").split
      end
    end

View on GitHub (pinned to 1c9f0bb801)