Skip to content

m3thom/rails-api-with-auth-template

Repository files navigation

This Rails 6 API only. Created with devise, devise-jwt and based on
https://medium.com/@nandhae/2019-how-i-set-up-authentication-with-jwt-in-just-a-few-lines-of-code-with-rails-5-api-devise-9db7d3cee2c0

At your custom


To Run the project

rails db:setup

rails db:migrate

rails s

Step to create from scratch.

STEP 1: Configure Rack Middleware

/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource(
        '*',
        headers: :any,
        expose: ["Authorization"],
        methods: [:get, :patch, :put, :delete, :post, :options, :show]
    )
  end
end

STEP 2: Add the needed Gems

gem 'devise', '~> 4.8.0'

gem 'devise-jwt', '~> 0.9.0'

gem 'dotenv-rails'

Then

bundle install

STEP 3: Install and configure devise

rails generate devise:install

At /config/environments/development.rb

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

STEP 4: Create User model

rails generate devise User

rails db:create

rails db:migrate

STEP 5: Create devise controllers and routes

For controllers

Look at

app/controllers/concerns/users/users_authenticable.rb

app/controllers/users/registrations_controller.rb

app/controllers/users/sessions_controller.rb

for more information.

For routes

At /routes.rb

Rails.application.routes.draw do
  devise_for :users,
             controllers: {
                 sessions: 'users/sessions',
                 registrations: 'users/registrations'
             }
end

For STEP 6: Configure devise-jwt

Create .env.development at your project root directory

bundle exec rake secret

Then add it to .env.development

DEVISE_SECRET_KEY=SECRET_FROM_BUNDLE_EXEC_RAKE_SECRET

Prevent credentials push at .gitignore

# Credentials with dot-env
.env*
!.env.template

At /devise.rb

class CustomDeviseFailure < Devise::FailureApp
  def respond
    http_auth
  end
end

Devise.setup do |config|
  # .
  # .
  # .
  
  config.warden do |manager|
    # Response with status code(401, 404, etc.) instead of redirect the user.
    manager.failure_app = CustomDeviseFailure
  end
  
  config.jwt do |jwt|
    jwt.secret = ENV['DEVISE_SECRET_KEY']
    jwt.dispatch_requests = [
        ['POST', %r{^/users$}], # Default registration path from devise.
        ['POST', %r{^/users/sign_in$}], # Default sign_in path from devise.
    ]
    jwt.revocation_requests = [
        ['DELETE', %r{^/sign_out}] # Default sign_out path from devise.
    ]
    jwt.expiration_time = 5.minutes.to_i
  end
  # .
  # .
  # .
end

STEP 7: Set up a revocation strategy

For more info https://github.com/waiting-for-dev/devise-jwt

Option 1 :

rails g model jwt_denylist jti:string:index exp:datetime

At /app/models/jwt_denylist.rb add

class JwtDenylist < ApplicationRecord
  include Devise::JWT::RevocationStrategies::Denylist

  self.table_name = 'jwt_denylist'
end

At app/models/user.rb

class User < ApplicationRecord
  devise :database_authenticatable,
         :jwt_authenticatable, 
         jwt_revocation_strategy: JwtDenylist
end

Option 2 :

rails g migration create_allowlisted_jwts

At generated _create_allowlisted_jwts.rb migration file

def change
  create_table :allowlisted_jwts do |t|
    t.string :jti, null: false
    t.string :aud
    # If you want to leverage the `aud` claim, add to it a `NOT NULL` constraint:
    # t.string :aud, null: false
    t.datetime :exp, null: false
    t.references :user, foreign_key: { on_delete: :cascade }, null: false
  end

  add_index :allowlisted_jwts, :jti, unique: true
end

Create allowlisted_jwt.rb model

class AllowlistedJwt < ApplicationRecord
end

At user.rb model

class User < ApplicationRecord
  include Devise::JWT::RevocationStrategies::Allowlist

  devise :database_authenticatable,
         :jwt_authenticatable, 
         jwt_revocation_strategy: self # Use created strategy
end

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published