06 September 2020
In the previous section, we talked about how to adjust CORS settings for API-only rails applications. This week, we will introduce doorkeeper gem that makes it easy to get the OAuth 2 provider functionality.
Oauth is an authorization specification that is widely used in web applications. Programming languages have different libraries that implement the OAuth 2.0 specification and provide an abstraction to use without knowing all the implementation details. We will use the doorkeeper
gem for the bookmarker application.
To understand how OAuth works and how we’re going to use it, you need to read the blog post from Alex Bilbie. Keep in mind that we will use the resource owner credentials grant
in the bookmarker
application and make sure you understand this part before continue to read the chapter. As a difference from the blog post explanation, we will not have a specific trusted client application since we will not serve to any other external client.
Let’s start with adding doorkeeper
gem to Gemfile
and run bundle install
command.
Then we need to execute the following commands to install the doorkeeper
gem and generate corresponding migration files. (Do not forget to run rubocop --auto-correct
for generated files.)
Let’s do step by step what the doorkeeper
gem suggests to complete integration. First, we need to modify the doorkeeper initializer file to have resource_owner_from_credentials
(also called password) grant.
activerecord
as an ORM.resource_owner_authenticator
code block since we will use resource_owner_from_credentials
grant. Inside the block, we called the authenticate
class method on the User
model to authenticate our users. (We will implement the authenticate method later in the chapter.)api_only
flag because it will skip all views management and change how the doorkeeper responds to requests.grant_flows
as the password
.I would also like to introduce oauth scopes when we build premium accounts for the bookmarker app, but we don’t need it until then since there will be only one type of user.
As a second step, we will run the migration generator for the doorkeeper
gem.
Before running the migration that is created by the doorkeeper, I would like to remove ouath_applications
and oauth_access_grants
tables from the migration and also their corresponding foreign keys and indexes. We delete them because we will not expose our API to the external clients as I mentioned above, and therefore we don’t need to differentiate which application we’re using. (If you would like to publish your API to external developers and ask them to generate their oauth applications to use your API, then you need to keep those tables.)
Apart from removing those tables, we need to remove null: false
constraint from application reference because of the reason we just mentioned. We also don’t need to create foreign key for application_id
column in oauth_access_tokens
and oauth_applications
tables. So applying those changes, the migration should look like this:
Then we can run the rails db:migrate
command to create oauth_access_tokens
table in our database.
You need to run rubocop --auto-correct
again to fix rubocop offenses that are caused by the doorkeeper
migration file. It’s also better to exclude db/migrations
folder and db/schema.rb
file from rubocop rules since they are not going to be the places that we heavily write ruby code. Besides that, it’s better to be pragmatic here not to solve all the offenses from external party migrations and generated schema code.
Apart from the previous configurations, since we will not use applications and authorization flows from the doorkeeper, it’s better to disable them on the routes.
In the first step of doorkeeper suggestions, we mentioned that we would use the authenticate
method from the User
model to authenticate our users. Let’s create an authenticate method in the User
model and use it from the doorkeeper to find authenticated resources. Add authenticate method to app/models/users.rb
file as the following.
We’re basically using find_for_authentication method from devise
to authenticate user. If we can’t authenticate the user, we’re returning nil
.
We can now send a request to get an OAuth token for the user that we created with the user creation endpoint.
And the response should be like this one:
(I suggested to have the generic structure for API payloads in make API payloads generic chapter, but I would like to break this rule for doorkeeper related endpoints. We can have the same structure, but I don’t think it’s worth to do so many workarounds without getting a huge benefit.)
Now we completed the functionality of the token creation endpoint. We can continue by documenting it with the swagger. Let’s create OauthController
, OauthTokenInput
and OauthToken
classes.
Let’s add those three classes into the ApidocsController
class since we generate swagger documentation from this file.
If you enter to the http:/localhost:3000/swagger you should see the api/v1/oauth/token
endpoint documentation.
And as the last step, we need to write a test for the authenticate
method on the User
model.
Let’s run all the tests again to see everything is still working as expected as well as new changes.
UPDATE: Added one more commit after publishing the chapter to fix OauthTokenController
class and file names for swagger documentation. Make sure you also get those changes here.
This chapter, we integrated the OAuth 2.0 authorization framework into the bookmarker
application by using the doorkeeper
gem. From now on, the clients can request a new access token whenever they want to log any user in. We also provided swagger documentation for api/v1/oauth/token
endpoint. You can find the source code of bookmarker application on github. You can also find the previous chapter here.
Thank you for reading! If you want to be notified when I publish a new chapter, you can follow me on twitter.