Skip to content

Commit

Permalink
Add devise auth endpoints to authenticate and revoke JWTs, also add e…
Browse files Browse the repository at this point in the history
…nvironment variable gem (#28)

* controllers and jwt setup for devise

* finish up devise setup

* lint

* add dotenv gem and seed user data

* linting fixes

* remove require auth from questions

* change revocation strategy to self

* add .env setup to readme

* add comments to session controller

* linting sessions controller comments

* add to readme how to protect a resource
  • Loading branch information
lee-ma authored Dec 4, 2019
1 parent 4f5d996 commit 9b947a6
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 5 deletions.
4 changes: 4 additions & 0 deletions .env.development.local.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CORS_ORIGIN=
SEED_USER_EMAIL=
SEED_USER_PASSWORD=
DEVISE_JWT_SECRET_KEY=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@

# Ignore master key for decrypting credentials and more.
/config/master.key
.env.development.local
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: %i[mri mingw x64_mingw]
gem 'database_cleaner'
gem 'dotenv-rails'
gem 'factory_bot_rails'
gem 'rspec-rails', '~> 3.5'
end
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ GEM
devise (~> 4.0)
warden-jwt_auth (~> 0.3.6)
diff-lcs (1.3)
dotenv (2.7.5)
dotenv-rails (2.7.5)
dotenv (= 2.7.5)
railties (>= 3.2, < 6.1)
dry-auto_inject (0.6.1)
dry-container (>= 0.3.4)
dry-configurable (0.9.0)
Expand Down Expand Up @@ -226,6 +230,7 @@ DEPENDENCIES
database_cleaner
devise
devise-jwt (~> 0.5.9)
dotenv-rails
factory_bot_rails
listen (>= 3.0.5, < 3.2)
pg (~> 1.1)
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ SELECT * FROM questions;
\d+ questions
```

**Environment Variable Setup**
```
# remove .template from .env.development.local.template and fill in .env.development.local, example values:
CORS_ORIGIN=http://localhost:3000
[email protected]
SEED_USER_PASSWORD=password
DEVISE_JWT_SECRET_KEY=super_secret_secret_key
```

**Authenticating Routes with Devise**
```
# This project includes Devise for auth, add this line before the resource to require a user to be logged in:
before_action :authenticate_user!
```

**Run the dev server**

```
Expand All @@ -95,4 +110,4 @@ If you get an error during dependency installation containing `can't find gem bu
gem update --system
bundle install
```
This worked with Ruby 2.5.1 with `rbenv` on MacOS.
This worked with Ruby 2.5.1 with `rbenv` on MacOS.
21 changes: 21 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
# frozen_string_literal: true

class ApplicationController < ActionController::API
respond_to :json
def render_resource(resource)
if resource.errors.empty?
render json: resource
else
validation_error(resource)
end
end

def validation_error(resource)
render json: {
errors: [
{
status: '400',
title: 'Bad Request',
detail: resource.errors,
code: '100'
}
]
}, status: :bad_request
end
end
19 changes: 19 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class SessionsController < Devise::SessionsController
respond_to :json

private

# this returns a response with the session info in json format
# including the jti, user, and timestamps
def respond_with(resource, _opts = {})
render json: resource
end

# upon logout, this is the handler that will handle what to send
# as response, currently just sets header to 200
def respond_to_on_destroy
head :ok
end
end
4 changes: 3 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

class User < ApplicationRecord
devise :database_authenticatable, :registerable, :validatable
include Devise::JWT::RevocationStrategies::JTIMatcher
devise :database_authenticatable, :validatable, :rememberable,
:jwt_authenticatable, jwt_revocation_strategy: self
end
2 changes: 2 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

Dotenv::Railtie.load

module SdcApi
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
Expand Down
13 changes: 12 additions & 1 deletion config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
# config.authentication_keys = [:email]
config.authentication_keys = [:email]

# Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the
Expand Down Expand Up @@ -296,4 +296,15 @@
# When set to false, does not sign a user in automatically after their password is
# changed. Defaults to true, so a user is signed in automatically after changing a password.
# config.sign_in_after_change_password = true
config.jwt do |jwt|
jwt.secret = ENV['DEVISE_JWT_SECRET_KEY']
jwt.dispatch_requests = [
['POST', %r{^/login$}]
]
jwt.revocation_requests = [
['DELETE', %r{^/logout$}]
]
jwt.expiration_time = 240.hours.to_i
jwt.request_formats = { api_user: [:json] }
end
end
11 changes: 10 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# frozen_string_literal: true

Rails.application.routes.draw do
devise_for :users
devise_for :users,
path: '',
path_names: {
sign_in: 'login',
sign_out: 'logout'
},
controllers: {
sessions: 'sessions'
},
defaults: { format: :json }
get '/questions', to: 'questions#index'

get '/flowchart/:id', to: 'flowchart#serialized_flowchart_by_id'
Expand Down
8 changes: 8 additions & 0 deletions db/migrate/20191126021858_jti_matcher_revocation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

class JtiMatcherRevocation < ActiveRecord::Migration[6.0]
def change
add_column :users, :jti, :string, null: false
add_index :users, :jti, unique: true
end
end
4 changes: 3 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2019_11_12_021705) do
ActiveRecord::Schema.define(version: 2019_11_26_021858) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -53,7 +53,9 @@
t.string "encrypted_password", default: "", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "jti", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["jti"], name: "index_users_on_jti", unique: true
end

add_foreign_key "flowchart_nodes", "flowchart_nodes", column: "child_id"
Expand Down
5 changes: 5 additions & 0 deletions db/seeds.rb
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
# frozen_string_literal: true

case Rails.env
when 'development'
User.create(email: ENV['SEED_USER_EMAIL'], password: ENV['SEED_USER_PASSWORD'], jti: SecureRandom.uuid)
end

0 comments on commit 9b947a6

Please sign in to comment.