Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 30, 2022 08:25 pm GMT

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, required gems installation, Starting with devise.

Setup devise

# add to Gemfilegem 'devise'# install gembundle 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/loginscope "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.rbDoorkeeper.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.hourend

lets adjust User model to include authenticate! method

# app/models/user.rbclass 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)  endend

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.rbDoorkeeper::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 methoddef 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)    @resultsenddef 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)      endendprivate # 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)    endenddef user_json    {      user:          user,      auth: {        access_token:  token.token,        refresh_token: token.refresh_token      }    }.to_jsonend  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   endend

Happy Coding


Original Link: https://dev.to/codesalley/rails-api-authentication-with-devise-and-doorkeeper-16oh

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To