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
- 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).
- If using JWT assertion, send client_assertion_type exactly as urn:ietf:params:oauth:client-assertion-type:jwt-bearer along with client_assertion.
- Verify you are hitting the token endpoint on the correct root account/shard where the key with the right audience is cached.
- 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
- Set the developer key's client_credentials audience to 'external' before using JWT assertion
- Send client_assertion_type exactly as urn:ietf:params:oauth:client-assertion-type:jwt-bearer
- Decide on one auth mode (assertion vs secret) per key and configure the key accordingly
- Test the token endpoint after any key audience change
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
- @provider.error_message
- invalid_client_id
- invalid_redirect
- invalid_refresh_token
- @provider.missing_scopes
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
endView on GitHub (pinned to 1c9f0bb801)