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

unsupported_grant_type

unsupported_grant_type

Error message

unsupported_grant_type

What it means

In the OAuth2 token endpoint (POST /login/oauth2/token), Canvas selects a granter class based on the grant_type parameter; if grant_type is absent or not one of authorization_code, refresh_token, or client_credentials, the fallback BaseType granter has supported_type? == false and Canvas::OAuth::RequestError :unsupported_grant_type is raised. This matches the OAuth2 spec error of the same name.

Solutions

  1. Set grant_type=authorization_code (plus client_id, client_secret, and the code) for the standard code exchange
  2. Use grant_type=refresh_token with your stored refresh_token to renew access
  3. Only use grant_type=client_credentials if your key/account permits service tokens
  4. Print/inspect the actual POSTed body; fix typos and confirm the param is in the body (or Basic auth), not just the URL

Example fix

# before
curl -d 'client_id=..&client_secret=..&code=abc' https://canvas/login/oauth2/token
# after
curl -d 'grant_type=authorization_code&client_id=..&client_secret=..&code=abc' https://canvas/login/oauth2/token
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED_GRANTS = ['authorization_code', 'refresh_token', 'client_credentials'];
if (!SUPPORTED_GRANTS.includes(grantType)) throw new Error(`grant_type '${grantType}' unsupported by Canvas OAuth2 token endpoint`);

Try / catch

try { token = await exchangeToken(params); } catch (e) { if (e.body?.error === 'unsupported_grant_type') { /* fix grant_type / fall back to authorization_code flow */ } throw e; }

Prevention

When it happens

Trigger: POST /login/oauth2/token with grant_type omitted, misspelled (e.g. 'authorizationcode'), or set to an unsupported type like 'password' or 'client_credentials' when the key/account disallows it.

Common situations: Sending the code exchange without grant_type=authorization_code, trying password or client_credentials grants on a key that only supports authorization code, typos after hand-writing the curl command, or a client library defaulting to a grant Canvas does not implement.

Related errors


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

Appendix: source

Thrown at app/controllers/oauth2_provider_controller.rb:203

                if Canvas::OAuth::PKCE.use_pkce_in_token?(params)
                  Canvas::OAuth::GrantTypes::AuthorizationCodeWithPKCE.new(client_id, secret, params)
                else
                  Canvas::OAuth::GrantTypes::AuthorizationCode.new(client_id, secret, params)
                end
              when "refresh_token"
                Canvas::OAuth::GrantTypes::RefreshToken.new(client_id, secret, params)
              when "client_credentials"
                Canvas::OAuth::GrantTypes::ClientCredentials.new(
                  params,
                  request.host_with_port,
                  @domain_root_account,
                  request.protocol
                )
              else
                Canvas::OAuth::GrantTypes::BaseType.new(client_id, secret, params)
              end

    raise Canvas::OAuth::RequestError, :unsupported_grant_type unless granter.supported_type?

    token = granter.token
    # make sure locales are set up
    if token.is_a?(Canvas::OAuth::Token)
      @current_user = token.user
      assign_localizer
      I18n.set_locale_with_localizer
    end

    render json: token
  end

  def destroy
    if params[:expire_sessions]
      if session[:login_aac]
        # The AAC could have been deleted since the user logged in
        @aac = AuthenticationProvider.where(id: session[:login_aac]).first
        redirect = @aac.try(:user_logout_redirect, self, @current_user)

View on GitHub (pinned to 1c9f0bb801)