{"id":13416354,"url":"https://github.com/doorkeeper-gem/doorkeeper","last_synced_at":"2026-10-18T23:47:20.466Z","repository":{"id":37336566,"uuid":"2802565","full_name":"doorkeeper-gem/doorkeeper","owner":"doorkeeper-gem","description":"Doorkeeper is an OAuth 2 provider for Ruby on Rails / Grape.","archived":false,"fork":false,"pushed_at":"2026-10-12T09:06:37.000Z","size":5526,"stargazers_count":5523,"open_issues_count":24,"forks_count":1078,"subscribers_count":99,"default_branch":"main","last_synced_at":"2026-10-13T13:56:59.102Z","etag":null,"topics":["authentication","authorization","doorkeeper","grape","identity","oauth","oauth2","oauth2-provider","oauth2-server","ruby-on-rails"],"latest_commit_sha":null,"homepage":"https://doorkeeper.gitbook.io/guides/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doorkeeper-gem.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"MIT-LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","claude":null,"gemini":null,"cursor":null,"copilot":null,"dco":null,"cla":null,"disclosure":null},"funding":{"open_collective":"doorkeeper-gem"}},"created_at":"2011-11-18T13:02:01.000Z","updated_at":"2026-10-08T08:15:36.000Z","dependencies_parsed_at":"2026-10-06T11:39:50.961Z","dependency_job_id":null,"html_url":"https://github.com/doorkeeper-gem/doorkeeper","commit_stats":{"total_commits":1849,"total_committers":327,"mean_commits":5.654434250764526,"dds":0.7928610059491616,"last_synced_commit":"8b85512e8daa787bf875965f58d86d315b219b5e"},"previous_names":["applicake/doorkeeper"],"tags_count":135,"template":false,"template_full_name":null,"purl":"pkg:github/doorkeeper-gem/doorkeeper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doorkeeper-gem%2Fdoorkeeper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doorkeeper-gem%2Fdoorkeeper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doorkeeper-gem%2Fdoorkeeper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doorkeeper-gem%2Fdoorkeeper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doorkeeper-gem","download_url":"https://codeload.github.com/doorkeeper-gem/doorkeeper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doorkeeper-gem%2Fdoorkeeper/sbom","scorecard":{"id":352279,"data":{"date":"2025-08-11","repo":{"name":"github.com/doorkeeper-gem/doorkeeper","commit":"89b05fbf914e6b7bf65e627db8a6ff200bf095f8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.3,"checks":[{"name":"Maintained","score":4,"reason":"5 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":7,"reason":"Found 10/13 approved changesets -- score normalized to 7","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/check_orm_changes.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/ci.yml:6","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":4,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Warn: no linked content found","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: MIT-LICENSE:0","Info: FSF or OSI recognized license: MIT License: MIT-LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"CII-Best-Practices","score":2,"reason":"badge detected: InProgress","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/check_orm_changes.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/check_orm_changes.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/check_orm_changes.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/check_orm_changes.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/check_orm_changes.yml:53: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/check_orm_changes.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:56: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:76: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/ci.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:78: update your workflow using https://app.stepsecurity.io/secureworkflow/doorkeeper-gem/doorkeeper/ci.yml/main?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating ruby:3.3.4-alpine to ruby:3.3.4-alpine@sha256:9fc2d9dd146a47fddcf2b69e3174d92ee1a654fbbe73f97b858505394748ac6e","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   6 third-party GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T08:38:34.649Z","repository_id":37336566,"created_at":"2025-08-18T08:38:34.649Z","updated_at":"2025-08-18T08:38:34.649Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":343843146,"owners_count":38275644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-10-03T21:59:58.778Z","status":"online","status_checked_at":"2026-10-13T02:00:04.984Z","response_time":52,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","authorization","doorkeeper","grape","identity","oauth","oauth2","oauth2-provider","oauth2-server","ruby-on-rails"],"created_at":"2024-07-30T21:00:57.564Z","updated_at":"2026-10-18T23:47:20.460Z","avatar_url":"https://github.com/doorkeeper-gem.png","language":"Ruby","funding_links":["https://opencollective.com/doorkeeper-gem"],"categories":["Ruby","Authentication","Authentication and OAuth","others","API","OAuth","Uncategorized","Client Library","Gems"],"sub_categories":["Uncategorized","Ruby","Articles"],"readme":"# Doorkeeper — awesome OAuth 2 provider for your Rails / Grape app.\n\n[![Gem Version](https://badge.fury.io/rb/doorkeeper.svg)](https://rubygems.org/gems/doorkeeper)\n[![CI](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml/badge.svg)](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml)\n[![Maintainability](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper/maintainability.svg)](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper)\n[![Coverage Status](https://coveralls.io/repos/github/doorkeeper-gem/doorkeeper/badge.svg?branch=main)](https://coveralls.io/github/doorkeeper-gem/doorkeeper?branch=main)\n[![GuardRails badge](https://api.guardrails.io/v2/badges/21183?token=66768ce8f6995814df81f65a2cff40f739f688492704f973e62809e15599bb62)](https://dashboard.guardrails.io/gh/doorkeeper-gem/repos/21183)\n[![Dependabot](https://img.shields.io/badge/dependabot-enabled-success.svg)](https://dependabot.com)\n\nDoorkeeper is a gem (Rails engine) that makes it easy to introduce OAuth 2 provider\nfunctionality to your Ruby on Rails or Grape application.\n\nSupported features:\n\n- [The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)\n  - [Authorization Code Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1)\n  - [Access Token Scopes](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3)\n  - [Refresh token](https://datatracker.ietf.org/doc/html/rfc6749#section-1.5)\n  - [Implicit grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.2)\n  - [Resource Owner Password Credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3)\n  - [Client Credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)\n- [OAuth 2.0 Token Revocation](https://datatracker.ietf.org/doc/html/rfc7009)\n- [OAuth 2.0 Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662)\n- [OAuth 2.0 Threat Model and Security Considerations](https://datatracker.ietf.org/doc/html/rfc6819)\n- [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252)\n- [Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636)\n- [OAuth 2.0 Authorization Server Issuer Identification](https://datatracker.ietf.org/doc/html/rfc9207) — opt-in by setting `issuer`; adds the `iss` parameter to authorization redirects returned to the client\n- [Resource Indicators for OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc8707)\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Documentation](#documentation)\n- [Installation](#installation)\n  - [Ruby on Rails](#ruby-on-rails)\n  - [Grape](#grape)\n- [ORMs](#orms)\n- [Extensions](#extensions)\n- [Database maintenance](#database-maintenance)\n- [Resource Indicators](#resource-indicators)\n- [Refresh Token Scopes](#refresh-token-scopes)\n- [Custom Grant Flows](#custom-grant-flows)\n- [Custom Client Authentication Methods](#custom-client-authentication-methods)\n- [Example Applications](#example-applications)\n- [Sponsors](#sponsors)\n- [Development](#development)\n- [Contributing](#contributing)\n- [Contributors](#contributors)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Documentation\n\nThis documentation is valid for `main` branch. Please check the documentation for the version of doorkeeper you are using in:\nhttps://github.com/doorkeeper-gem/doorkeeper/releases.\n\nAdditionally, other resources can be found on:\n\n- [Guides](https://doorkeeper.gitbook.io/guides/) with how-to get started and configuration documentation\n- See the [Wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki) for articles on how to integrate with other solutions\n- Screencast from [railscasts.com](http://railscasts.com/): [#353\nOAuth with\nDoorkeeper](http://railscasts.com/episodes/353-oauth-with-doorkeeper)\n- See [upgrade guides](https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions)\n- For general questions, please post on [Stack Overflow](http://stackoverflow.com/questions/tagged/doorkeeper)\n- See [SECURITY.md](SECURITY.md) for this project's security disclose\n  policy\n\n## Installation\n\nInstallation depends on the framework you're using. The first step is to add the following to your Gemfile:\n\n```ruby\ngem 'doorkeeper'\n```\n\nAnd run `bundle install`. After this, check out the guide related to the framework you're using.\n\n### Ruby on Rails\n\nDoorkeeper currently supports Ruby on Rails \u003e= 5.0. See the guide [here](https://doorkeeper.gitbook.io/guides/ruby-on-rails/getting-started).\n\n### Grape\n\nGuide for integration with Grape framework can be found [here](https://doorkeeper.gitbook.io/guides/grape/grape).\n\n## ORMs\n\nDoorkeeper supports Active Record by default, but can be configured to work with the following ORMs:\n\n| ORM | Support via |\n| :--- | :--- |\n| Active Record | by default |\n| MongoDB | [doorkeeper-gem/doorkeeper-mongodb](https://github.com/doorkeeper-gem/doorkeeper-mongodb) |\n| Sequel | [nbulaj/doorkeeper-sequel](https://github.com/nbulaj/doorkeeper-sequel) |\n| Couchbase | [acaprojects/doorkeeper-couchbase](https://github.com/acaprojects/doorkeeper-couchbase) |\n| RethinkDB | [aca-labs/doorkeeper-rethinkdb](https://github.com/aca-labs/doorkeeper-rethinkdb) |\n\n## Extensions\n\nExtensions that are not included by default and can be installed separately.\n\n|  | Link |\n| :--- | :--- |\n| OpenID Connect extension | [doorkeeper-gem/doorkeeper-openid\\_connect](https://github.com/doorkeeper-gem/doorkeeper-openid_connect) |\n| JWT Token support | [doorkeeper-gem/doorkeeper-jwt](https://github.com/doorkeeper-gem/doorkeeper-jwt) |\n| Assertion grant extension | [doorkeeper-gem/doorkeeper-grants\\_assertion](https://github.com/doorkeeper-gem/doorkeeper-grants_assertion) |\n| I18n translations | [doorkeeper-gem/doorkeeper-i18n](https://github.com/doorkeeper-gem/doorkeeper-i18n) |\n| CIBA - Client Initiated Backchannel Authentication Flow extension | [doorkeeper-ciba](https://github.com/autoseg/doorkeeper-ciba) |\n| Device Authorization Grant | [doorkeeper-device_authorization_grant](https://github.com/exop-group/doorkeeper-device_authorization_grant) |\n\n## Database maintenance\n\nDoorkeeper does **not** automatically remove expired or revoked tokens and grants. The `oauth_access_tokens` and `oauth_access_grants` tables grow indefinitely and can reach millions of rows if left unmanaged.\n\nPrune them periodically with the bundled rake task:\n\n```bash\nbundle exec rake doorkeeper:db:cleanup\n```\n\nThis deletes expired and revoked access tokens and grants. See the [Rake tasks guide](https://doorkeeper.gitbook.io/guides/internals/rake) for details.\n\n## Resource Indicators\n\nDoorkeeper supports [Resource Indicators for OAuth 2.0 (RFC 8707)](https://datatracker.ietf.org/doc/html/rfc8707), allowing clients to signal which protected resource(s) they intend to access. Tokens are then audience-restricted to those resources.\n\n### Setup\n\n1. Run the generator to add the required `resource` column:\n\n```bash\nrails generate doorkeeper:resource_indicators\nrails db:migrate\n```\n\n2. Configure a validator in your initializer:\n\n```ruby\n# config/initializers/doorkeeper.rb\nDoorkeeper.configure do\n  resource_indicator_validator -\u003e(resource_indicators, client) {\n    allowed = %w[https://api.example.com/ https://calendar.example.com/]\n    resource_indicators.all? { |r| allowed.include?(r) }\n  }\nend\n```\n\nThe callable receives an array of resource URIs and the OAuth client. Return `true` to accept or `false` to reject with `invalid_target`.\n\n### Behavior\n\n- Resource URIs must be absolute and must not contain a fragment component.\n- Resource indicators are stored on grants and tokens.\n- Token and refresh requests enforce subset restrictions against the original grant.\n- Token introspection responses include `aud` when resource indicators are present.\n- Grants issued with resource indicators retain their audience restriction even if the validator is later removed from configuration.\n\n### Multiple resources\n\nRFC 8707 uses repeated query parameters (`?resource=…\u0026resource=…`) for multiple values, but Rack collapses repeated keys to the last value. Clients must use the Rails bracket syntax for multiple resource indicators:\n\n```\n?resource[]=https://api.example.com/\u0026resource[]=https://calendar.example.com/\n```\n\nA single `resource=…` works as-is.\n\n## Refresh Token Scopes\n\nA client may narrow the access token it gets back from the refresh token grant by sending a `scope` parameter. Per [RFC 6749 §6](https://datatracker.ietf.org/doc/html/rfc6749#section-6) the refresh token itself keeps the scope originally granted by the resource owner: a later refresh that omits `scope` returns to the granted scope, and one that asks for the granted scope again is accepted.\n\nDoorkeeper tracks the granted scope in the `refresh_token_scopes` column of `oauth_access_tokens`. New installs get it from the install migration; existing installs add it with:\n\n```bash\nrails generate doorkeeper:refresh_token_scopes\nrails db:migrate\n```\n\nWith the column in place, [token introspection](https://datatracker.ietf.org/doc/html/rfc7662#section-2.2) of a refresh token reports the granted scope rather than the scope of the access token it was issued with, and `reuse_access_token` does not hand a grant of a narrower scope an existing token whose refresh token was granted a wider one.\n\nWithout the column, a narrowed refresh narrows the refresh token as well, so the chain can never return to the granted scope (the behavior of Doorkeeper before the column existed), and a `refresh_token_scopes` assigned to an access token is ignored. Rows created before the migration keep that behavior until they are rotated, and so do the [ORM extensions](#extensions) (Sequel, MongoDB) until they add the field.\n\n## Custom Grant Flows\n\nBesides the built-in OAuth 2 flows, Doorkeeper can recognize and process any custom grant type through its grant flow registry — including grant types whose names are URNs or URIs, such as the SAML 2.0 bearer assertion grant defined by [RFC 7522](https://www.rfc-editor.org/rfc/rfc7522).\n\nA grant flow bundles a matcher for the `grant_type` parameter with a strategy class that processes the token request. Register it before `Doorkeeper.configure` and enable it by adding its registered name to `grant_flows`:\n\n```ruby\n# config/initializers/doorkeeper.rb\nDoorkeeper::GrantFlow.register(\n  :saml2_bearer,\n  grant_type_matches: \"urn:ietf:params:oauth:grant-type:saml2-bearer\",\n  grant_type_strategy: SamlBearer::Strategy,\n)\n\nDoorkeeper.configure do\n  grant_flows %w[authorization_code saml2_bearer]\n  # ...\nend\n```\n\nNote that `grant_flows` lists the *registered flow name* (`saml2_bearer`), while `grant_type_matches` — a `String` or a `Regexp` — is what the request's `grant_type` parameter is matched against.\n\nThe strategy class receives the authorization server as `server` and builds the request object handling the grant:\n\n```ruby\nmodule SamlBearer\n  class Strategy \u003c Doorkeeper::Request::Strategy\n    delegate :client, :parameters, to: :server\n\n    def request\n      @request ||= TokenRequest.new(Doorkeeper.config, client, parameters)\n    end\n  end\nend\n```\n\nThe request object validates the grant and issues the token. Subclassing `Doorkeeper::OAuth::BaseRequest` provides the response handling, scope calculation and token creation, so only the grant-specific parts remain (per RFC 7522 §2.1 the `assertion` parameter carries a single SAML assertion, base64url-encoded without padding):\n\n```ruby\nmodule SamlBearer\n  class TokenRequest \u003c Doorkeeper::OAuth::BaseRequest\n    validate :client, error: Doorkeeper::Errors::InvalidClient\n    validate :client_supports_grant_flow, error: Doorkeeper::Errors::UnauthorizedClient\n    validate :assertion, error: Doorkeeper::Errors::InvalidGrant\n    validate :scopes, error: Doorkeeper::Errors::InvalidScope\n\n    attr_reader :client, :parameters, :access_token\n\n    def initialize(server, client, parameters = {})\n      @server          = server\n      @client          = client\n      @parameters      = parameters\n      @original_scopes = parameters[:scope]\n      @grant_type      = \"urn:ietf:params:oauth:grant-type:saml2-bearer\"\n    end\n\n    private\n\n    def before_successful_response\n      find_or_create_access_token(client, resource_owner, scopes, {}, server)\n      super\n    end\n\n    def assertion\n      # Decode and verify the SAML assertion — signature, audience, validity\n      # window, etc. — e.g. with the ruby-saml gem. Skipping verification\n      # turns the endpoint into a token vending machine for anyone.\n      @assertion ||= decode_and_verify_saml(parameters[:assertion])\n    end\n\n    def resource_owner\n      # Map the assertion's subject to a resource owner.\n      @resource_owner ||= User.find_by(email: assertion.name_id)\n    end\n\n    def validate_client\n      client.present?\n    end\n\n    def validate_client_supports_grant_flow\n      Doorkeeper.config.allow_grant_flow_for_client?(grant_type, client\u0026.application)\n    end\n\n    def validate_assertion\n      assertion.present? \u0026\u0026 resource_owner.present?\n    end\n\n    def validate_scopes\n      return true if scopes.blank?\n\n      Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(\n        scope_str: scopes.to_s,\n        server_scopes: server.scopes,\n        app_scopes: client\u0026.scopes,\n        grant_type: grant_type,\n      )\n    end\n  end\nend\n```\n\nThe `client_supports_grant_flow` validation keeps the custom grant subject to the `allow_grant_flow_for_client` configuration option (per-client grant restrictions), just like the built-in flows.\n\nFlows can also handle custom `response_type` values on the authorization endpoint via the `response_type_matches` / `response_type_strategy` options — see the built-in registrations in [`lib/doorkeeper/grant_flow.rb`](lib/doorkeeper/grant_flow.rb) for reference. An extension can also group several flows under one configuration name with `Doorkeeper::GrantFlow.register_alias` (e.g. the OpenID Connect extension registers `implicit_oidc` to expand to multiple response types).\n\n## Custom Client Authentication Methods\n\nDoorkeeper authenticates clients (RFC 6749 §2.3) through a registry of named methods. `client_secret_basic`, `client_secret_post` and `none` are built in, and an application or extension can register additional ones — for instance to keep accepting credentials that a partner integration sends in its own headers.\n\nA method is any object that responds to `matches_request?` and `authenticate`. Register it before `Doorkeeper.configure` and enable it by listing its registered name in `client_authentication`:\n\n```ruby\n# config/initializers/doorkeeper.rb\nDoorkeeper::ClientAuthentication.register(\n  :partner_headers,\n  PartnerHeaders::Authentication,\n)\n\nDoorkeeper.configure do\n  client_authentication %i[client_secret_basic client_secret_post partner_headers none]\n  # ...\nend\n```\n\nThe order of `client_authentication` is the order the methods are tried in: the first one whose `matches_request?` returns true handles the request.\n\n`matches_request?` decides whether the request carries this method's credentials, and `authenticate` extracts them into a `Doorkeeper::ClientAuthentication::Credentials` pair (or `nil`):\n\n```ruby\nmodule PartnerHeaders\n  class Authentication\n    def self.matches_request?(request)\n      request.get_header(\"HTTP_X_CLIENT_ID\").present? \u0026\u0026\n        request.get_header(\"HTTP_X_CLIENT_SECRET\").present?\n    end\n\n    def self.authenticate(request)\n      Doorkeeper::ClientAuthentication::Credentials.new(\n        request.get_header(\"HTTP_X_CLIENT_ID\"),\n        request.get_header(\"HTTP_X_CLIENT_SECRET\"),\n      )\n    end\n  end\nend\n```\n\nTwo things are worth keeping in mind when writing one.\n\n**Keep `matches_request?` as narrow as possible.** RFC 6749 §2.3 forbids a client from using more than one authentication method in a single request, and Doorkeeper enforces that across the whole registry rather than only the enabled methods. A method that matches too broadly therefore collides with a built-in one and the request is answered with `invalid_request`.\n\n**The returned credentials are resolved with `by_uid_and_secret`.** A blank secret resolves only a public (non-confidential) client — that is what the built-in `none` method relies on — while a confidential client is resolved only when the secret matches the registered one. A method that establishes the client's identity by some other proof, such as a client certificate or a signed assertion, therefore still has to produce the registered secret for a confidential client.\n\nEnabled methods are advertised in the authorization server metadata, so a registered method appears in `token_endpoint_auth_methods_supported` at `/.well-known/oauth-authorization-server` once `client_authentication` lists it.\n\n## Example Applications\n\nThese applications show how Doorkeeper works and how to integrate with it. Start with the oAuth2 server and use the clients to connect with the server.\n\n| Application | Link |\n| :--- | :--- |\n| OAuth2 Server with Doorkeeper | [doorkeeper-gem/doorkeeper-provider-app](https://github.com/doorkeeper-gem/doorkeeper-provider-app) |\n| Sinatra Client connected to Provider App | [doorkeeper-gem/doorkeeper-sinatra-client](https://github.com/doorkeeper-gem/doorkeeper-sinatra-client) |\n| Devise + Omniauth Client | [doorkeeper-gem/doorkeeper-devise-client](https://github.com/doorkeeper-gem/doorkeeper-devise-client) |\n\nYou may want to create a client application to\ntest the integration. Check out these [client\nexamples](https://github.com/doorkeeper-gem/doorkeeper/wiki/Example-Applications)\nin our wiki or follow this [tutorial\nhere](https://github.com/doorkeeper-gem/doorkeeper/wiki/Testing-your-provider-with-OAuth2-gem).\n\n## Sponsors\n\n[![OpenCollective](https://opencollective.com/doorkeeper-gem/backers/badge.svg)](#backers) \n[![OpenCollective](https://opencollective.com/doorkeeper-gem/sponsors/badge.svg)](#sponsors)\n\nSupport this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/doorkeeper-gem#sponsor)]\n\n\u003ca href=\"https://codecademy.com/about/careers?utm_source=doorkeeper-gem\" target=\"_blank\"\u003e\u003cimg src=\"https://static-assets.codecademy.com/marketing/codecademy_logo_padded.png\"/\u003e\u003c/a\u003e\n\n\u003e Codecademy supports open source as part of its mission to democratize tech. Come help us build the education the world deserves: [https://codecademy.com/about/careers](https://codecademy.com/about/careers?utm_source=doorkeeper-gem)\n\n\u003cbr\u003e\n\n\u003ca href=\"https://oauth.io/?utm_source=doorkeeper-gem\" target=\"_blank\"\u003e\u003cimg src=\"https://oauth.io/img/logo_text.png\"/\u003e\u003c/a\u003e\n\n\u003e If you prefer not to deal with the gory details of OAuth 2, need dedicated customer support \u0026 consulting, try the cloud-based SaaS version: [https://oauth.io](https://oauth.io/?utm_source=doorkeeper-gem)\n\n\u003cbr\u003e\n\n\u003ca href=\"https://www.wealthsimple.com/?utm_source=doorkeeper-gem\" target=\"_blank\"\u003e\u003cimg src=\"https://wealthsimple.s3.amazonaws.com/branding/medium-black.svg\"/\u003e\u003c/a\u003e\n\n\u003e Wealthsimple is a financial company on a mission to help everyone achieve financial freedom by providing products and advice that are accessible and affordable. Using smart technology, Wealthsimple takes financial services that are often confusing, opaque and expensive and makes them simple, transparent, and low-cost. See what Investing on Autopilot is all about: [https://www.wealthsimple.com](https://www.wealthsimple.com/?utm_source=doorkeeper-gem)\n\n## Development\n\nTo run the local engine server:\n\n```\nbundle install\nbundle exec rake doorkeeper:server\n````\n\nBy default, it uses the latest Rails version with ActiveRecord. To run the\ntests with a specific Rails version:\n\n```\nBUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle exec rake\n```\n\nYou can also experiment with the changes using `bin/console`. It uses in-memory SQLite database and default\nDoorkeeper config, but you can reestablish connection or reconfigure the gem if you need.\n\n## Contributing\n\nWant to contribute and don't know where to start? Check out [features we're\nmissing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Supported-Features),\ncreate [example\napps](https://github.com/doorkeeper-gem/doorkeeper/wiki/Example-Applications),\nintegrate the gem with your app and let us know!\n\nAlso, check out our [contributing guidelines page](CONTRIBUTING.md).\n\n## Contributors\n\nThanks to all our [awesome\ncontributors](https://github.com/doorkeeper-gem/doorkeeper/graphs/contributors)!\n\n\u003ca href=\"https://github.com/doorkeeper-gem/doorkeeper/graphs/contributors\"\u003e\u003cimg src=\"https://opencollective.com/doorkeeper-gem/contributors.svg?width=890\u0026button=false\" /\u003e\u003c/a\u003e\n\n## License\n\nMIT License. Created in Applicake. Maintained by the community.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoorkeeper-gem%2Fdoorkeeper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoorkeeper-gem%2Fdoorkeeper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoorkeeper-gem%2Fdoorkeeper/lists"}