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

@provider.missing_scopes

Error message

@provider.missing_scopes

What it means

Raised as Canvas::OAuth::InvalidScopeError carrying @provider.missing_scopes when the client authenticated successfully for a client_credentials grant but requested scopes the developer key is not allowed to grant (valid_scopes? false). Canvas refuses to mint a token with scopes beyond the key's configured allowlist.

Solutions

  1. Add the missing scopes listed in the error to the developer key's allowed scopes in Canvas admin, then retry.
  2. Trim the requested scope parameter to only scopes the key already grants.
  3. Fix scope formatting (Canvas scopes look like url:GET|/api/v1/courses) — check for typos or wrong HTTP verbs.
  4. If the key was recently edited, wait for/clear the key cache so valid_scopes? sees the new config.

Example fix

// before
scope = 'url:GET|/api/v1/users url:POST|/api/v1/accounts/1/sub_accounts'
// after (only scopes granted to the key)
scope = 'url:GET|/api/v1/users'
Defensive patterns

Strategy: validation

Validate before calling

requested = opts[:scope].to_s.split
granted = developer_key.scopes
missing = requested - granted
raise 'requesting scopes the key does not grant' if missing.any?

Try / catch

begin
  token = exchange_client_credentials(opts)
rescue Canvas::OAuth::InvalidScopeError => e
  logger.error("missing scopes: #{e.message}")
end

Prevention

When it happens

Trigger: POST to /login/oauth2/token with grant_type=client_credentials and a scope parameter containing one or more scopes not enabled on the developer key (e.g. url:POST|/api/v1/accounts/1/* missing from the key's scopes, or a service-user scope the key lacks).

Common situations: Integration requests new API scopes after code changes without updating the key; scope string typos or wrong verb/path format; copying scope lists between accounts where keys differ; key audience changed so previously granted scopes no longer apply.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

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

View on GitHub (pinned to 1c9f0bb801)