Rails api authentication with devise and Doorkeeper

Ruby on Rails web authentication with devise is easy to get started with, but how can we handle authentication with devise and doorkeeper in a rails api. This article focuses on steps to add authentication after creating a new project.

Buckle up, req…


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Code Salley

Ruby on Rails web authentication with devise is easy to get started with, but how can we handle authentication with devise and doorkeeper in a rails api. This article focuses on steps to add authentication after creating a new project.

Buckle up, required gems installation, Starting with devise.

Setup devise

# add to Gemfile
gem 'devise'

# install gem
bundle install 

# devise initialization.
# for more context on devise refer to official 
rails generate devise:install

# generate User model migration. 
rails generate devise User 

# Run rails migration 
rails db:migrate

Devise docs

Setup Doorkeeper

# add doorkeeper to Gemfile 
gem 'doorkeeper'

# install gem 
bundle install 

# initialize doorkeeper 
rails generate doorkeeper:install 

# Above command integrate doorkeeper into your 
# project and create and initializer file in 
# config/initializers/doorkeeper.rb, add doorkeeper 
# routes to your route file(config/routes.rb) and locale 
# file in config/locales/doorkeeper.en.yml 
# for more insight check official docs. 

# generate doorkeeper migration 
rails generate doorkeeper:migration

# run rails migration 
rails db:migrate

Doorkeeper Official docs

We need two(2) routes for login and signup. In config/routes.rb lets comment out use_doorkeeper and devise routes. Create new routing to look something like this.

# config/routes.rb

# comment or remove doorkeeper routes
# use_doorkeeper

# not exposing devise routes 
devise_for :users, only: [] 

# our auth routes auth/signup and auth/login
scope "auth" do 
  post "/signup",  to: "auth#signup"
  post "/login",  to: "auth#login"
end

doorkeeper configuration needs some adjustment since we're using custom routes.

# config/initializers/doorkeeper.rb

Doorkeeper.configure do
  # Change the ORM that doorkeeper will use (requires ORM extensions installed).
  # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms
  orm :active_record

  # This block will be called to check whether the resource owner is authenticated or not.
  resource_owner_authenticator do
    current_user || warden.authenticate!(scope: :user)
  end

  resource_owner_from_credentials do |_routes|
    User.authenticate!(params[:email], params[:password]) # we need to add this method in our user model
  end

  grant_flows %w[authorization_code client_credentials password]

  use_refresh_token

  client_credentials :from_basic, :from_params

  access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param

  access_token_expires_in 1.hour
end

lets adjust User model to include authenticate! method

# app/models/user.rb
class User < ApplicationRecord

# add this method to find and authenticate users
  def self.authenticate!(email, password)
    user = find_by(email: email.downcase)
    user if user&.valid_password?(password)
  end
end

One of the very capabilities of doorkeeper is the ability to manage multiple platforms, like a doorkeeper client app for android, web etc. In our database seed (db/seeds.rb) lets create an app for our IOS app like this.

# db/seeds.rb

Doorkeeper::Application.find_or_create_by(name: "IOS APP") do |app|
  app.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
  app.secret = "my_secret" 
  app.uid = "my_uid"
  app.save!
end

run rails db:seed to insert above doorkeeper app into our database.

Now we need auth controller to be able to handle requests from our signup and login routes. create a file app/controllers/auth_controller.rb

# app/controllers/auth_controller.rb

# signup method
def signup
    client_app = Doorkeeper::Application.find_by(uid: params[:client_id])

    unless client_app
      return render json: { error: I18n.t("doorkeeper.errors.messages.invalid_client") },
          status: :unauthorized
    end

    @user = User.new(user_params)
    @user.save


    unless @user
      render json: { message: "registration failed" }, status: :unprocessable_entity
    end

    @results = model_results(@user, client_app)

    @results
end

def login
    response = strategy.authorize
    @token = response.status == :ok ? response.token : nil
    if @token&.resource_owner_id
      @user ||= User.find(@token.resource_owner_id)
    end

    self.response_body =
      if response.status == :unauthorized
        render json: {error: "unauthorized" }, status: 404
      else
        user_json(@user, @token)
      end
end



private 

# for a more cleaner approach, separate this into concerns or an isolated class.

def model_results(user, client_app, token_type = "Bearer")
   access_token = Doorkeeper::AccessToken.find_or_create_for(
        resource_owner: user.id,
        application:    client_app,
        refresh_token:  generate_refresh_token,
        expires_in:     Doorkeeper.configuration.access_token_expires_in.to_i,
        scopes:         ""
      )

  return { user: user, tokens: {refresh_token: access_token.refresh_token, access_token:  access_token.token }
end


 def generate_refresh_token
    loop do
      token = SecureRandom.hex(32)

      break token unless Doorkeeper::AccessToken.exists?(refresh_token: token)
    end
end


def user_json
    {
      user:          user,
      auth: {

        access_token:  token.token,
        refresh_token: token.refresh_token
      }
    }.to_json
end

  def user_params
    params.require(:user).permit(:email ,:password, :password_confirmation)
  end

Finally how do we secure endpoints?
For any controller actions we desire to protect, we place this in our controller.

class ProfileController < ApplicationController
  # this protects profile actions. 
  before_action :doorkeeper_authorize!


  def create 
  end
end

Happy Coding 🎉


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Code Salley


Print Share Comment Cite Upload Translate Updates
APA

Code Salley | Sciencx (2022-12-30T20:25:49+00:00) Rails api authentication with devise and Doorkeeper. Retrieved from https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/

MLA
" » Rails api authentication with devise and Doorkeeper." Code Salley | Sciencx - Friday December 30, 2022, https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/
HARVARD
Code Salley | Sciencx Friday December 30, 2022 » Rails api authentication with devise and Doorkeeper., viewed ,<https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/>
VANCOUVER
Code Salley | Sciencx - » Rails api authentication with devise and Doorkeeper. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/
CHICAGO
" » Rails api authentication with devise and Doorkeeper." Code Salley | Sciencx - Accessed . https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/
IEEE
" » Rails api authentication with devise and Doorkeeper." Code Salley | Sciencx [Online]. Available: https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/. [Accessed: ]
rf:citation
» Rails api authentication with devise and Doorkeeper | Code Salley | Sciencx | https://www.scien.cx/2022/12/30/rails-api-authentication-with-devise-and-doorkeeper/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.