Doorkeeper is a gem (Rails engine) that makes it easy to introduce OAuth 2 provider functionality to your Ruby on Rails or Grape application.
Supported features:
Please check the documentation for the version of doorkeeper you are using in: https://github.com/doorkeeper-gem/doorkeeper/releases
- See the Wiki
- See upgrade guides
- For general questions, please post in Stack Overflow
- See SECURITY.md for this project's security disclose policy
- Installation
- Configuration
- Protecting resources with OAuth (a.k.a your API endpoint)
- Other customizations
- Testing
- Upgrading
- Development
- Contributing
- Other resources
Put this in your Gemfile:
gem 'doorkeeper'
Run the installation generator with:
rails generate doorkeeper:install
This will install the doorkeeper initializer into config/initializers/doorkeeper.rb
.
By default doorkeeper is configured to use Active Record, so to start you have to generate the migration tables (supports Rails >= 5 migrations versioning):
rails generate doorkeeper:migration
You may want to add foreign keys to your migration. For example, if you plan on
using User
as the resource owner, add the following line to the migration file
for each table that includes a resource_owner_id
column:
add_foreign_key :table_name, :users, column: :resource_owner_id
If you want to enable PKCE flow for mobile apps, you need to generate another migration:
rails generate doorkeeper:pkce
Then run migrations:
rake db:migrate
Ensure to use non-confidential apps for pkce. PKCE is created, because you cannot trust its apps' secret. So whatever app needs pkce: it means, it cannot be a confidential app by design.
Remember to add associations to your model so the related records are deleted.
If you don't do this an ActiveRecord::InvalidForeignKey
-error will be raised
when you try to destroy a model with related access grants or access tokens.
class User < ApplicationRecord
has_many :access_grants, class_name: "Doorkeeper::AccessGrant",
foreign_key: :resource_owner_id,
dependent: :delete_all # or :destroy if you need callbacks
has_many :access_tokens, class_name: "Doorkeeper::AccessToken",
foreign_key: :resource_owner_id,
dependent: :delete_all # or :destroy if you need callbacks
end
See doorkeeper-mongodb project for Mongoid and MongoMapper support. Follow along the implementation in that repository to extend doorkeeper with other ORMs.
If you are using Sequel gem then you can add doorkeeper-sequel extension to your project. Follow configuration instructions for setting up the necessary Doorkeeper ORM.
Use doorkeeper-couchbase extension if you are using Couchbase database.
By default Doorkeeper uses full Rails stack to provide all the OAuth 2 functionality with additional features like administration area for managing applications. By the way, starting from Doorkeeper 5 you can use API mode for your API only Rails 5 applications. All you need is just to configure the gem to work in desired mode:
Doorkeeper.configure do
# ...
api_only
end
Keep in mind, that in this mode you will not be able to access Applications
or
Authorized Applications
controllers because they will be skipped. CSRF protections (which are otherwise enabled) will be skipped, and all the redirects will be returned as JSON response with corresponding locations.
The installation script will also automatically add the Doorkeeper routes into your app, like this:
Rails.application.routes.draw do
use_doorkeeper
# your routes
end
This will mount following routes:
GET /oauth/authorize/native?code
GET /oauth/authorize
POST /oauth/authorize
DELETE /oauth/authorize
POST /oauth/token
POST /oauth/revoke
POST /oauth/introspect
resources /oauth/applications
GET /oauth/authorized_applications
DELETE /oauth/authorized_applications/:id
GET /oauth/token/info
For more information on how to customize routes, check out this page on the wiki.
You need to configure Doorkeeper in order to provide resource_owner
model
and authentication block in config/initializers/doorkeeper.rb
:
Doorkeeper.configure do
resource_owner_authenticator do
User.find_by(id: session[:current_user_id]) || redirect_to(login_url)
end
end
This code is run in the context of your application so you have access to your
models, session or routes helpers. However, since this code is not run in the
context of your application's ApplicationController
it doesn't have access to
the methods defined over there.
You may want to check other ways of authentication here.
Doorkeeper support multiple languages. See language files in the I18n repository.
If you don't want to use default Doorkeeper error responses you can raise and rescue it's
exceptions. All you need is to set configuration option handle_auth_errors
to :raise
.
In this case Doorkeeper will raise Doorkeeper::Errors::TokenForbidden
,
Doorkeeper::Errors::TokenExpired
, Doorkeeper::Errors::TokenRevoked
or other exceptions
that you need to care about.
If you are using rake
, you can load rake tasks provided by this gem, by adding
the following line to your Rakefile
:
Doorkeeper::Rake.load_tasks
By default Doorkeeper is retaining expired and revoked access tokens and grants. This allows to keep an audit log of those records, but it also leads to the corresponding tables to grow large over the lifetime of your application.
If you are concerned about those tables growing too large, you can regularly run the following rake task to remove stale entries from the database:
rake doorkeeper:db:cleanup
Note that this will remove tokens that are expired according to the configured TTL
in Doorkeeper.configuration.access_token_expires_in
. The specific expires_in
value of each access token is not considered. The same is true for access
grants.
To protect your controllers (usual one or ActionController::API
) with OAuth,
you just need to setup before_action
s specifying the actions you want to
protect. For example:
class Api::V1::ProductsController < Api::V1::ApiController
before_action :doorkeeper_authorize! # Requires access token for all actions
# before_action -> { doorkeeper_authorize! :read, :write }
# your actions
end
You can pass any option before_action
accepts, such as if
, only
,
except
, and others.
Starting from version 2.2 Doorkeeper provides helpers for the
Grape framework >= 0.10. One of them is doorkeeper_authorize!
that
can be used in a similar way as an example above to protect your API
with OAuth. Note that you have to use require 'doorkeeper/grape/helpers'
and helpers Doorkeeper::Grape::Helpers
in your Grape API class.
For more information about integration with Grape see the Wiki.
require 'doorkeeper/grape/helpers'
module API
module V1
class Users < Grape::API
helpers Doorkeeper::Grape::Helpers
before do
doorkeeper_authorize!
end
# route_setting :scopes, ['user:email'] - for old versions of Grape
get :emails, scopes: [:user, :write] do
[{'email' => current_user.email}]
end
# ...
end
end
end
You can leverage the Doorkeeper.authenticate
facade to easily extract a
Doorkeeper::OAuth::Token
based on the current request. You can then ensure
that token is still good, find its associated #resource_owner_id
, etc.
module Constraint
class Authenticated
def matches?(request)
token = Doorkeeper.authenticate(request)
token && token.accessible?
end
end
end
For more information about integration and other integrations, check out the related wiki page.
You can also require the access token to have specific scopes in certain actions:
First configure the scopes in initializers/doorkeeper.rb
Doorkeeper.configure do
default_scopes :public # if no scope was requested, this will be the default
optional_scopes :admin, :write
end
And in your controllers:
class Api::V1::ProductsController < Api::V1::ApiController
before_action -> { doorkeeper_authorize! :public }, only: :index
before_action only: [:create, :update, :destroy] do
doorkeeper_authorize! :admin, :write
end
end
Please note that there is a logical OR between multiple required scopes. In the
above example, doorkeeper_authorize! :admin, :write
means that the access
token is required to have either :admin
scope or :write
scope, but does not
need have both of them.
If you want to require the access token to have multiple scopes at the same
time, use multiple doorkeeper_authorize!
, for example:
class Api::V1::ProductsController < Api::V1::ApiController
before_action -> { doorkeeper_authorize! :public }, only: :index
before_action only: [:create, :update, :destroy] do
doorkeeper_authorize! :admin
doorkeeper_authorize! :write
end
end
In the above example, a client can call :create
action only if its access token
has both :admin
and :write
scopes.
By default a 128 bit access token will be generated. If you require a custom
token, such as JWT, specify an object that responds to
.generate(options = {})
and returns a string to be used as the token.
Doorkeeper.configure do
access_token_generator "Doorkeeper::JWT"
end
JWT token support is available with Doorkeeper-JWT.
By default Doorkeeper's main controller Doorkeeper::ApplicationController
inherits from ActionController::Base
. You may want to use your own
controller to inherit from, to keep Doorkeeper controllers in the same
context than the rest your app:
Doorkeeper.configure do
base_controller 'ApplicationController'
end
If you want to return data based on the current resource owner, in other words, the access token owner, you may want to define a method in your controller that returns the resource owner instance:
class Api::V1::CredentialsController < Api::V1::ApiController
before_action :doorkeeper_authorize!
respond_to :json
# GET /me.json
def me
respond_with current_resource_owner
end
private
# Find the user that owns the access token
def current_resource_owner
User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end
end
In this example, we're returning the credentials (me.json
) of the access
token owner.
By default, the applications list (/oauth/applications
) is publicly available.
To protect the endpoint you should uncomment these lines:
# config/initializers/doorkeeper.rb
Doorkeeper.configure do
admin_authenticator do |routes|
Admin.find_by(id: session[:admin_id]) || redirect_to(routes.new_admin_session_url)
end
end
The logic is the same as the resource_owner_authenticator
block. Note:
since the application list is just a scaffold, it's recommended to either
customize the controller used by the list or skip the controller all together.
For more information see the page
in the wiki.
By default, everybody can create application with any scopes. However,
you can enforce users to create applications only with configured scopes
(default_scopes
and optional_scopes
from the Doorkeeper initializer):
# config/initializers/doorkeeper.rb
Doorkeeper.configure do
# ...
default_scopes :read, :write
optional_scopes :create, :update
enforce_configured_scopes
end
- Associate users to OAuth applications (ownership)
- CORS - Cross Origin Resource Sharing
- see more on Wiki page
You can use Doorkeeper models in your application test suite. Note that starting from
Doorkeeper 4.3.0 it uses ActiveSupport lazy loading hooks
to load models. There are known issue
with the factory_bot_rails
gem (it executes factories building before ActiveRecord::Base
is initialized using hooks in gem railtie, so you can catch a uninitialized constant
error).
It is recommended to use pure factory_bot
gem to solve this problem.
If you want to upgrade doorkeeper to a new version, check out the upgrading notes and take a look at the changelog.
Doorkeeper follows semantic versioning.
To run the local engine server:
bundle install
bundle exec rake doorkeeper:server
By default, it uses the latest Rails version with ActiveRecord. To run the tests with a specific ORM and Rails version:
rails=4.2.0 orm=active_record bundle exec rake
Want to contribute and don't know where to start? Check out features we're missing, create example apps, integrate the gem with your app and let us know!
Also, check out our contributing guidelines page.
You can find everything about Doorkeeper in our wiki here.
Check out this screencast from railscasts.com: #353 OAuth with Doorkeeper
After you set up the provider, you may want to create a client application to test the integration. Check out these client examples in our wiki or follow this tutorial here.
Thanks to all our awesome contributors!
- The OAuth 2.0 Authorization Framework
- OAuth 2.0 Threat Model and Security Considerations
- OAuth 2.0 Token Revocation
MIT License. Copyright 2011 Applicake.