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
- Custom mailer. https://www.truemark.dev/blog/reset-password-in-react-and-rails
- Refresh user token. waiting-for-dev/devise-jwt#7 (comment)
rails db:setup
rails db:migrate
rails s
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