{"record":{"id":"47ae775f2740156c","repo":"instructure/canvas-lms","slug":"provider-missing-scopes","errorCode":null,"errorMessage":"@provider.missing_scopes","messagePattern":"@provider\\.missing_scopes","errorType":"exception","errorClass":"Canvas::OAuth::InvalidScopeError","httpStatus":null,"severity":"error","filePath":"lib/canvas/oauth/grant_types/client_credentials.rb","lineNumber":78,"sourceCode":"      end\n\n      def secret_for(provider, opts)\n        provider.try(:secret) || opts[:client_secret]\n      end\n\n      def key_for(client_id)\n        DeveloperKey.find_cached(client_id)\n      rescue ::ActiveRecord::RecordNotFound\n        nil\n      end\n\n      def validate_type\n        unless @provider.assertion_method_permitted?\n          raise Canvas::OAuth::InvalidRequestError, \"assertion method not supported for this grant_type\"\n        end\n\n        raise Canvas::OAuth::InvalidRequestError, @provider.error_message unless @provider.valid?\n        raise Canvas::OAuth::InvalidScopeError, @provider.missing_scopes unless @provider.valid_scopes?\n      end\n\n      def generate_token\n        @provider.generate_token\n      end\n\n      def basic_auth?(opts)\n        opts[:client_assertion_type] != \"urn:ietf:params:oauth:client-assertion-type:jwt-bearer\"\n      end\n\n      def scopes_from_opts(opts)\n        (opts[:scope] || opts[:scopes] || \"\").split\n      end\n    end\n  end\nend\n","sourceCodeStart":60,"sourceCodeEnd":95,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/canvas/oauth/grant_types/client_credentials.rb#L60-L95","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Add the missing scopes listed in the error to the developer key's allowed scopes in Canvas admin, then retry.","Trim the requested scope parameter to only scopes the key already grants.","Fix scope formatting (Canvas scopes look like url:GET|/api/v1/courses) — check for typos or wrong HTTP verbs.","If the key was recently edited, wait for/clear the key cache so valid_scopes? sees the new config."],"exampleFix":"// before\nscope = 'url:GET|/api/v1/users url:POST|/api/v1/accounts/1/sub_accounts'\n// after (only scopes granted to the key)\nscope = 'url:GET|/api/v1/users'","handlingStrategy":"validation","validationCode":"requested = opts[:scope].to_s.split\ngranted = developer_key.scopes\nmissing = requested - granted\nraise 'requesting scopes the key does not grant' if missing.any?","typeGuard":null,"tryCatchPattern":"begin\n  token = exchange_client_credentials(opts)\nrescue Canvas::OAuth::InvalidScopeError => e\n  logger.error(\"missing scopes: #{e.message}\")\nend","preventionTips":["Keep the key's scope allowlist in sync with the scopes your code requests","Use exact Canvas scope format url:VERB|/api/v1/path","Diff requested vs granted scopes in CI before deploying scope changes","Request the minimal scope set so drift is less likely"],"tags":["oauth2","canvas-lms","scopes","permissions"],"backgroundTag":"insufficient-permissions","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}