An Interest In:
Web News this Week
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
- April 19, 2024
- April 18, 2024
- April 17, 2024
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
.
# 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
# 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
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
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To