-
Notifications
You must be signed in to change notification settings - Fork 1
Rails auth API Guide
sebastian.mihalache edited this page Mar 6, 2020
·
4 revisions
- rails API auth solution with doorkeeper
- check this example API - auth-example-api
-
create a
Users
modelrails g model User email:string:uniq password_digest:string
-
add
[doorkeeper](https://rubygems.org/gems/doorkeeper)
and[bcrypt](https://rubygems.org/gems/bcrypt)
gems and runbundle install
-
initialize doorkeeper by running
bundle exec rails generate doorkeeper:install
and follow the on screen instructions
-
- edit the following in
config/initializers/doorkeeper.rb
- edit the following in
-
- add the following:
use_refresh_token
access_token_expires_in 30.minutes
-
- run
rails generate doorkeeper:migration
- run
-
- edit the
CreateDoorKeeperTables
migration - remove theoauth_applications
,oauth_access_grants
,oauth_access_grants
tables and indexes, remove thenull: false
fromt.references :application, null: false
and add the following at the end:
- edit the
add_index :oauth_access_tokens, :token, unique: true
add_index :oauth_access_tokens, :refresh_token, unique: true
add_foreign_key(:oauth_access_tokens, :users, column: :resource_owner_id)
-
- it should look similar to this:
class CreateDoorkeeperTables < ActiveRecord::Migration[6.0]
def change
create_table :oauth_access_tokens do |t|
t.references :resource_owner, index: true
t.references :application
t.string :token, null: false
t.string :refresh_token
t.integer :expires_in
t.datetime :revoked_at
t.datetime :created_at, null: false
t.string :scopes
t.string :previous_refresh_token, null: false, default: ""
end
add_index :oauth_access_tokens, :token, unique: true
add_index :oauth_access_tokens, :refresh_token, unique: true
add_foreign_key(:oauth_access_tokens, :users, column: :resource_owner_id)
end
end
-
- run
rails db:migrate
- run
- edit
app/models/user.rb
- add the following
has_secure_password
before_save { email.downcase! }
validates :email, presence: true, uniqueness: { case_sensitive: false }, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, length: { minimum: 6 }, allow_nil: true
def tokens
access_token = Doorkeeper::AccessToken.create!(
resource_owner_id: id,
expires_in: Doorkeeper.configuration.access_token_expires_in,
use_refresh_token: Doorkeeper.configuration.refresh_token_enabled?
)
Doorkeeper::OAuth::TokenResponse.new(access_token).body
end
- create
app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :email
has_one :tokens, if: :with_auth_tokens?
def with_auth_tokens?
instance_options[:with_auth_tokens]
end
end
-
add api error handling - if you haven't already (see the guide from here)
-
create
app/controllers/api/v1/base_controller.rb
class Api::V1::BaseController < ActionController::API
include ApiErrorHandling
before_action :doorkeeper_authorize!
protected
def doorkeeper_unauthorized_render_options(error: nil)
{
json: {
code: error.state,
message: 'Not authorized',
expired: error.reason == :expired
}
}
end
def render_error_message(message = nil)
render json: {
message: 'Validation Failed',
errors: [message],
code: 'unprocessable_entity'
}, status: :unprocessable_entity
end
def current_user
@current_user ||= begin
if doorkeeper_token.present?
User.find(doorkeeper_token[:resource_owner_id])
end
end
end
end
- create
app/controllers/api/v1/sessions_controller.rb
class Api::V1::SessionsController < Api::V1::BaseController
include Api::V1::SessionsControllerDoc
skip_before_action :doorkeeper_authorize!, only: :create
def index
render json: current_user
end
def create
user = User.find_by(email: params[:email].downcase)
if user&.authenticate(params[:password])
render json: user, with_auth_tokens: true
else
render_error_message(I18n.t('errors.login'))
end
end
def destroy
doorkeeper_token.revoke
head :ok
end
end
- create
app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < Api::V1::BaseController
include Api::V1::UsersControllerDoc
skip_before_action :doorkeeper_authorize!, only: :create
def create
user = User.create!(user_params)
render json: user, with_auth_tokens: true
end
private
def user_params
params.require(:user).permit(:email, :password)
end
end
add the following under the en
key inside config/locales/en.yml
errors:
login: "Invalid email or password"
- configure routes - add the following inside
config/routes.rb
use_doorkeeper scope: 'api/v1/sessions' do
skip_controllers :applications, :authorizations, :authorized_applications, :token_info
end
namespace :api do
namespace :v1 do
resources :users, only: :create
resources :sessions, only: [:create, :index] do
delete '/', action: :destroy, on: :collection
end
end
end
- register: POST
http://localhost:3000/api/v1/users
-
- body:
{
"user": {
"email": "[email protected]",
"password": "sUpErSeCuRePass"
}
}
-
- reponse:
{
"email": "[email protected]",
"tokens": {
"access_token": "YjMCzHZLCwYwjzHbSYqKEt-AMki8w9e3ZOuRKMZYu7A",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "wtHHms3LqHVmI3RnRmQIVeyC6UWhR8IEFBwiYiUKpso",
"created_at": 1578064218
}
}
- login: POST
http://localhost:3000/api/v1/sessions
-
- body:
{
"email": "[email protected]",
"password": "sUpErSeCuRePass"
}
-
- response:
{
"email": "[email protected]",
"tokens": {
"access_token": "E8EmdWcnH-9eKrcU2qsY2WVpVi8Bm-9aDtM-7Hztz_Y",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "kn73Q_Xs5naUlj5z1Nqm2t-fzfrdlw5WEo6CN93Y8jA",
"created_at": 1578064782
}
}
- refresh token: POST
http://localhost:3000/api/v1/sessions/token
-
- body:
{
"grant_type": "refresh_token",
"refresh_token": "kn73Q_Xs5naUlj5z1Nqm2t-fzfrdlw5WEo6CN93Y8jA"
}
-
- response:
{
"access_token": "BFIVmi0Fgze4_KRDKeTxXL8RpaZ_-c1oNOVViMzD9lk",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "DbhFbAksdZaolD1NL_M07XXsMqZZV-pfeu_Yrp753rI",
"created_at": 1578065292
}
- logout: POST
http://localhost:3000/api/v1/sessions
header:
Authorization: Bearer BFIVmi0Fgze4_KRDKeTxXL8RpaZ_-c1oNOVViMzD9lk
- unauthorized response
{
"code": "unauthorized",
"message": "Not authorized",
"expired": false
}
- Aditionally - you can check this postman collection