From ec5fee3b103feac4beba83bb89a2471cf927f2fd Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Tue, 2 May 2023 10:25:24 -0400 Subject: [PATCH 1/8] setup jwt --- Gemfile | 2 ++ Gemfile.lock | 21 +++++++++++++++++++ app/models/jwt_denylist.rb | 18 ++++++++++++++++ app/models/user.rb | 2 +- config/initializers/cors.rb | 10 +++++++++ config/initializers/devise.rb | 5 +++++ .../20230308041745_create_jwt_denylist.rb | 9 ++++++++ db/schema.rb | 8 ++++++- 8 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 app/models/jwt_denylist.rb create mode 100644 config/initializers/cors.rb create mode 100644 db/migrate/20230308041745_create_jwt_denylist.rb diff --git a/Gemfile b/Gemfile index ee80af0fbc..630e26a5a7 100644 --- a/Gemfile +++ b/Gemfile @@ -15,6 +15,8 @@ gem "cssbundling-rails", "~> 1.1" # compiles css gem "delayed_job_active_record" gem "devise" # for authentication gem "devise_invitable" +gem 'devise-jwt' +gem 'rack-cors' gem "httparty" # for making HTTP network requests 🥳 gem "twilio-ruby" # twilio helper functions gem "draper" # adds decorators for cleaner presentation logic diff --git a/Gemfile.lock b/Gemfile.lock index cc6ad49633..189c050848 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -148,6 +148,9 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) + devise-jwt (0.10.0) + devise (~> 4.0) + warden-jwt_auth (~> 0.6) devise_invitable (2.0.7) actionmailer (>= 5.0) devise (>= 4.6) @@ -166,6 +169,15 @@ GEM activesupport (>= 5.0) request_store (>= 1.0) ruby2_keywords + dry-auto_inject (1.0.1) + dry-core (~> 1.0) + zeitwerk (~> 2.6) + dry-configurable (1.0.1) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-core (1.0.0) + concurrent-ruby (~> 1.0) + zeitwerk (~> 2.6) erb_lint (0.3.1) activesupport better_html (>= 2.0.1) @@ -316,6 +328,8 @@ GEM rack (2.2.6.2) rack-attack (6.6.1) rack (>= 1.0, < 3) + rack-cors (2.0.0) + rack (>= 2.0.0) rack-test (2.0.2) rack (>= 1.3) rails (7.0.4.2) @@ -477,6 +491,11 @@ GEM parser (>= 3.2.0) warden (1.2.9) rack (>= 2.0.9) + warden-jwt_auth (0.8.0) + dry-auto_inject (>= 0.8, < 2) + dry-configurable (>= 0.13, < 2) + jwt (~> 2.1) + warden (~> 1.2) web-console (4.2.0) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -531,6 +550,7 @@ DEPENDENCIES database_cleaner-active_record delayed_job_active_record devise + devise-jwt devise_invitable dotenv-rails draper @@ -558,6 +578,7 @@ DEPENDENCIES puma pundit rack-attack + rack-cors rails (~> 7.0.4) rails-controller-testing rake diff --git a/app/models/jwt_denylist.rb b/app/models/jwt_denylist.rb new file mode 100644 index 0000000000..053fb469ee --- /dev/null +++ b/app/models/jwt_denylist.rb @@ -0,0 +1,18 @@ +class JwtDenylist < ApplicationRecord + include Devise::JWT::RevocationStrategies::Denylist + + self.table_name = 'jwt_denylist' +end + +# == Schema Information +# +# Table name: jwt_denylist +# +# id :bigint not null, primary key +# exp :datetime not null +# jti :string not null +# +# Indexes +# +# index_jwt_denylist_on_jti (jti) +# diff --git a/app/models/user.rb b/app/models/user.rb index 8f62de0f61..764045dbad 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,7 +8,7 @@ class User < ApplicationRecord validates_with UserValidator - devise :database_authenticatable, :invitable, :recoverable, :validatable, :timeoutable, :trackable + devise :database_authenticatable, :invitable, :recoverable, :validatable, :timeoutable, :trackable, :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist belongs_to :casa_org diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb new file mode 100644 index 0000000000..ceacf34732 --- /dev/null +++ b/config/initializers/cors.rb @@ -0,0 +1,10 @@ +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' # make sure to change to domain name of frontend + + resource '*', + headers: :any, + methods: [:get, :post, :put, :patch, :delete, :options, :head], + expose: %w(Authorization) + end +end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 608d6c203b..1f091e4c05 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -345,4 +345,9 @@ # 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 + + # ==> Configuration for devise-jwt secret key generation + config.jwt do |jwt| + jwt.secret = "wtfffff" + end end diff --git a/db/migrate/20230308041745_create_jwt_denylist.rb b/db/migrate/20230308041745_create_jwt_denylist.rb new file mode 100644 index 0000000000..6a96c3dd0a --- /dev/null +++ b/db/migrate/20230308041745_create_jwt_denylist.rb @@ -0,0 +1,9 @@ +class CreateJwtDenylist < ActiveRecord::Migration[7.0] + def change + create_table :jwt_denylist do |t| + t.string :jti, null: false + t.datetime :exp, null: false + end + add_index :jwt_denylist, :jti + end +end diff --git a/db/schema.rb b/db/schema.rb index 903527e7df..17bc17bf5c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_01_21_174227) do +ActiveRecord::Schema[7.0].define(version: 2023_03_08_041745) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -325,6 +325,12 @@ t.index ["casa_org_id"], name: "index_judges_on_casa_org_id" end + create_table "jwt_denylist", force: :cascade do |t| + t.string "jti", null: false + t.datetime "exp", null: false + t.index ["jti"], name: "index_jwt_denylist_on_jti" + end + create_table "languages", force: :cascade do |t| t.string "name" t.bigint "casa_org_id", null: false From 1066acba3c7848486b33860e43477c9ade7499f7 Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Thu, 4 May 2023 19:06:14 -0400 Subject: [PATCH 2/8] extend sessions controller create action to respond to json requests --- app/controllers/concerns/accessible.rb | 2 +- app/controllers/users/sessions_controller.rb | 14 ++++++ config/application.rb | 3 ++ config/initializers/cors.rb | 6 +-- config/initializers/devise.rb | 2 +- spec/requests/users/sessions_spec.rb | 45 ++++++++++++++++++++ 6 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 spec/requests/users/sessions_spec.rb diff --git a/app/controllers/concerns/accessible.rb b/app/controllers/concerns/accessible.rb index 269abe0476..be37df1d75 100644 --- a/app/controllers/concerns/accessible.rb +++ b/app/controllers/concerns/accessible.rb @@ -12,7 +12,7 @@ def check_user flash.clear redirect_to(authenticated_all_casa_admin_root_path) and return # override "after_sign_in_path_for" and redirect user to root path if no target URL is stored in session - elsif current_user && session[:user_return_to].nil? + elsif request.format.html? && current_user && session[:user_return_to].nil? flash.clear # The authenticated root path can be defined in your routes.rb in: devise_scope :user do... redirect_to(authenticated_user_root_path) and return diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index a1afeb575d..0ddf219c6c 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -3,4 +3,18 @@ class Users::SessionsController < Devise::SessionsController include Accessible skip_before_action :check_user, only: :destroy + def create + respond_to do |format| + format.html { super } + format.json { + warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#faliure") + render json: "Successfully authenticated", status: 200 + } + end + end + + # API authentication error + def faliure + render json: "Something went wrong with API sign in :(", status: 401 + end end diff --git a/config/application.rb b/config/application.rb index 9e59e54853..53f7a5bd74 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,5 +25,8 @@ class Application < Rails::Application config.active_storage.variant_processor = :mini_magick config.active_storage.content_types_to_serve_as_binary.delete("image/svg+xml") config.serve_static_assets = true + config.to_prepare do + DeviseController.respond_to :html, :json + end end end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index ceacf34732..12f803a262 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -3,8 +3,8 @@ origins '*' # make sure to change to domain name of frontend resource '*', - headers: :any, - methods: [:get, :post, :put, :patch, :delete, :options, :head], - expose: %w(Authorization) + headers: %w(Authorization), + methods: [:get, :post, :put, :patch, :delete, :options, :head], + expose: %w(Authorization) end end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 1f091e4c05..695b065946 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -300,7 +300,7 @@ # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. - # config.navigational_formats = ['*/*', :html] + config.navigational_formats = ['*/*', :html, :json] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :get diff --git a/spec/requests/users/sessions_spec.rb b/spec/requests/users/sessions_spec.rb new file mode 100644 index 0000000000..1580d00b5f --- /dev/null +++ b/spec/requests/users/sessions_spec.rb @@ -0,0 +1,45 @@ +require "rails_helper" + +RSpec.describe "Users::SessionsController", type: :request do + let(:casa_org) { create(:casa_org) } + let(:volunteer) { create(:volunteer, casa_org: casa_org)} + + describe "POST create" do + #before { @request.env["devise.mapping"] = Devise.mappings[:user] } + + context "when a user signs in" do + context "when request format is json" do + it "respond with jwt in response header" do + params = { + "user": { + "email": volunteer.email, + "password": volunteer.password + } + } + #header "Content-Type", "application/json; charset=utf-8" + post "/users/sign_in", as: :json, params: params + #print request.headers + expect(response.content_type).to eq("application/json; charset=utf-8") + expect(response.headers).to have_key "Authorization" + expect(response.headers['Authorization']).to be_starts_with('Bearer') + expect(response.body).to eq "Successfully authenticated" + expect(response).not_to have_http_status(:redirect) + end + + it "respond with status code 401 for bad request" do + params = { + "user": { + "email": "", + "password": "" + } + } + post "/users/sign_in", as: :json, params: params + expect(response.content_type).to eq("application/json; charset=utf-8") + expect(response.headers).not_to have_key "Authorization" + expect(response.body).to eq "Something went wrong with API sign in :(" + expect(response).not_to have_http_status(:redirect) + end + end + end + end +end From 46f4c1e959e9e599379ff26128bd9fc367646cb9 Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Tue, 9 May 2023 17:21:29 -0400 Subject: [PATCH 3/8] config dev environment to allow ngrok requests --- app/controllers/users/sessions_controller.rb | 2 ++ config/environments/development.rb | 3 +++ config/initializers/cors.rb | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 0ddf219c6c..7f67e2fe9b 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true class Users::SessionsController < Devise::SessionsController + #respond_to :json include Accessible skip_before_action :check_user, only: :destroy + def create respond_to do |format| format.html { super } diff --git a/config/environments/development.rb b/config/environments/development.rb index 7a63c3c4e3..c73f706fce 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -79,4 +79,7 @@ # config.action_cable.disable_request_forgery_protection = true config.assets.digest = false + + # allow requests from ngrok + config.hosts << '.ngrok-free.app' end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 12f803a262..a5170af0a8 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -3,8 +3,8 @@ origins '*' # make sure to change to domain name of frontend resource '*', - headers: %w(Authorization), - methods: [:get, :post, :put, :patch, :delete, :options, :head], - expose: %w(Authorization) + headers: %w(Authorization), + methods: :any, + expose: %w(Authorization) end end From 025a89695e1b2582be804e3b3f7a0d085c46f6a8 Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Wed, 10 May 2023 18:36:06 -0400 Subject: [PATCH 4/8] extend devise session controller to respond in json for log out; also wrote tests --- app/controllers/users/sessions_controller.rb | 14 +++++ spec/requests/users/sessions_spec.rb | 57 +++++++++++++------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 7f67e2fe9b..43b9512d58 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -19,4 +19,18 @@ def create def faliure render json: "Something went wrong with API sign in :(", status: 401 end + + private + + def respond_to_on_destroy + # We actually need to hardcode this as Rails default responder doesn't + # support returning empty response on GET request + respond_to do |format| + format.json { + render json: "Successfully Signed Out", status: 200 + } + format.all { head :no_content } + format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name), status: Devise.responder.redirect_status } + end + end end diff --git a/spec/requests/users/sessions_spec.rb b/spec/requests/users/sessions_spec.rb index 1580d00b5f..bafc6fd09a 100644 --- a/spec/requests/users/sessions_spec.rb +++ b/spec/requests/users/sessions_spec.rb @@ -5,39 +5,58 @@ let(:volunteer) { create(:volunteer, casa_org: casa_org)} describe "POST create" do - #before { @request.env["devise.mapping"] = Devise.mappings[:user] } + before { + @params = { + "user": { + "email": volunteer.email, + "password": volunteer.password + } + } + } context "when a user signs in" do context "when request format is json" do + after { + expect(response).not_to have_http_status(:redirect) + expect(response.content_type).to eq("application/json; charset=utf-8") + } + it "respond with jwt in response header" do - params = { - "user": { - "email": volunteer.email, - "password": volunteer.password - } - } #header "Content-Type", "application/json; charset=utf-8" - post "/users/sign_in", as: :json, params: params + post "/users/sign_in", as: :json, params: @params #print request.headers - expect(response.content_type).to eq("application/json; charset=utf-8") expect(response.headers).to have_key "Authorization" expect(response.headers['Authorization']).to be_starts_with('Bearer') expect(response.body).to eq "Successfully authenticated" - expect(response).not_to have_http_status(:redirect) end it "respond with status code 401 for bad request" do - params = { - "user": { - "email": "", - "password": "" - } - } - post "/users/sign_in", as: :json, params: params - expect(response.content_type).to eq("application/json; charset=utf-8") + post "/users/sign_in", as: :json, params: {"user": {"email": "suzume@tojimari.jp", "password": ""}} expect(response.headers).not_to have_key "Authorization" expect(response.body).to eq "Something went wrong with API sign in :(" - expect(response).not_to have_http_status(:redirect) + end + end + end + + context "when a user signs out" do + context "when request format is json" do + after { expect(response).not_to have_http_status(:redirect) } + + it "adds JWT to denylist" do + #header "Content-Type", "application/json; charset=utf-8" + post "/users/sign_in", as: :json, params: @params + # get JWT from response header + auth_bearer = response.headers['Authorization'] + #print auth_bearer + expect { + get "/users/sign_out", as: :json, headers: {"Authorization" => auth_bearer} + }.to change(JwtDenylist, :count).by(1) + end + + it "responds with status code 200" do + #header "Content-Type", "application/json; charset=utf-8" + post "/users/sign_in", as: :json, params: @params + get "/users/sign_out", as: :json end end end From cc75d855075fe182e7c04dbb5c73b2e1c54fa3ba Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Wed, 21 Jun 2023 14:49:48 -0400 Subject: [PATCH 5/8] add secret keys to credentials file --- Gemfile.lock | 2 +- config/credentials/development.yml.enc | 2 +- config/credentials/test.yml.enc | 2 +- config/initializers/devise.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 165aec0a9a..513612e1ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -550,7 +550,7 @@ DEPENDENCIES pundit rack-attack rack-cors - rails (~> 7.0.4) + rails (~> 7.0.5) rails-controller-testing rake request_store diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc index 4c0cc7ab67..6762972412 100644 --- a/config/credentials/development.yml.enc +++ b/config/credentials/development.yml.enc @@ -1 +1 @@ -svCtLWmi6TUWfy4jhsNxZgGKdzBrjq5JjKkGUaDA5tlP2XFn6XY8lJDVhF+T82kGjwT4EgsBheMZqPMbytlJ6iSDBIq/bHfjl1E5Zx3DqCkd4gDYgVK0roJffesKQPuWUSQUzvJV9pZ9VQEKbh+YA/I/N6aWGbkYlKXTOPHMY7F+rfiKXb8vHodUGWxCTycsWLpe/ohBvF7zzSwxkG7sEmbnRnqYd2Tmn0ASf6vNKXOzPamQ21rrgUss427/zjCjzWHCk4iUaHnhQQYwC2zJ+m1/0Uu+sM5CkYJhddsPbeeQkd7vgPjHBylgkT6L86XTz8sBrQDZB51TbmNouygu96NzQwE472c0csFEWwjz7fepy7sZkHN5KqQ=--dx6D/QqFOeacGYGg--+r3ffqcg8wONL9oMId9u5g== \ No newline at end of file +kCD5s9jBDZZfXdKirYJD95LfwF3xeWi2E+OGS9x9/8EsI6WMzl+jNUjuBQt/fUdO5R5pCHXXPCYPzsGnDyTKJJczqszDXDsG2xS5eSImw9O7BGmNDh7W1sQ5kMmjZPUUu9GaCcAjmnvh0ImXM2X+Lo/5X3NeAZ7FllFmUd3CaBD5J3N3mbXlJgVGRylabaKABqrZqbRnZK5QDOcjwxYspsnu3m9M2qx5B7m2RnpKdytDpxbRtGSj34cZmH0C+rGOCQUm9rSR5wrQVmmJ5DdElYAnM7lo6bUTf2W5wbd51TsrEATPCHx1iK89ot/alUIjmHcoMlUstLnSX8VQzQeIluqa7a7o2IN/p++vknWELVlYROn67wiRTrLPXeDy+NkhiYkhQCBl3AIL8qs92tKX9xwvM75q5pGwsxQneGM5IjOSHUnEmw6j0f6B0R1oksOGt/xg9VP73ANP3zxqNTnbzRNoqO9wVVju0jJVt8KGoXVzdQx2qQSU5ZqUulJ7v88Xjro7Ew6mSY5gh4itzzsVIF7kAEJM20eh2WWxNEDMZyzvKDD34XWxOb8nks9MF0yCJzWBIog=--m9KJMwI1N3qSf0ry--d64JLH6L2BJNNIW2DdMR6w== \ No newline at end of file diff --git a/config/credentials/test.yml.enc b/config/credentials/test.yml.enc index 51801db2a1..3fa70806dc 100644 --- a/config/credentials/test.yml.enc +++ b/config/credentials/test.yml.enc @@ -1 +1 @@ -zsvjeQzFV0vM7h3jsx7RUyA3WTQsEYWvEMreEvVbi4L0HN7sDNrP7kay2FZS5VEwrW5mJZdnu63BXa8fK/h1agvaaiOO9FhDXfyK+VT86TAfsLa1gsBK9mHjWSdXCJE9TZj9OjOtR3R/qHOI+2uPUnysh7Om4a0ckiu4Jwex3OcbgCYj2+G2JQtwHkWhlyBthGxLjuDDFfx+qxkJWkN7V9FhN0FkkPaflyj4FjR9BUf3/CB8pvHXqJ1lmxVScYsyhh50mc+CKyVptpqbLi9Jou3SiUePREX03ynV0KPR+7mT3FH9gCj4QyzzS1t3JOUfrgqeVFAzdV1TW01olinOyG2aMrZn1aA7GWfDeIr/GwnaPfUMmZNj4RQ=--KmPWCR7xHr6jUSx5--1L2S0bUzD2Bc+JFAHX7xKg== \ No newline at end of file +Z9z6La5/jrIcjvqDXZLmtw2hMblsJ4e+LfUCe3sOPdhdkk7iqSNlEpMe5/HSPubVjpTPuciGifG3W8SCfrESdK3yEYS8VRY1pImPrxBRzE8VjJ6/tyNZ6U/FWfveK2uisnxDeiM1j2PfbM1nhlggMWW+Ll4+LJba7HFQtJn4oyES4dK9syrgcB1zMZeD/Tdqub9eNIUBCqj/ehOnvUnTop6O5k5BI3Z7jXH/iNqNYK+Ks2rq0XdP5C3rLaDaotJkdcWjIZSGraovO6MrDl4YfNdcsWIBWsq2VNgRzkz2zrcaGCXaY9drd4r8OMQiULWplCO6ZAXHeuwubkisVdcqUZMhQ52Pklky36xmri76Tt5ztN30PGiD1PYYceDcHeuqnYsTVEonbJp1o4I7Z0ZvIVSR0dy5TeIne7A=--/Gk/JLOC9ouxL6CR--N9wv5rGQmObNmSe5FsRyjA== \ No newline at end of file diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index cd552ed7df..1bda029c93 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -348,6 +348,6 @@ # ==> Configuration for devise-jwt secret key generation config.jwt do |jwt| - jwt.secret = "wtfffff" + jwt.secret = Rails.application.credentials.devise.jwt_secret_key end end From 00de59977bdf95fc536659b8d2f8e65b9577ac63 Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Wed, 21 Jun 2023 14:59:45 -0400 Subject: [PATCH 6/8] lint and schema migrations --- Gemfile | 4 ++-- app/controllers/users/sessions_controller.rb | 4 ++-- app/models/jwt_denylist.rb | 2 +- config/environments/development.rb | 2 +- config/initializers/cors.rb | 10 ++++---- config/initializers/devise.rb | 2 +- db/schema.rb | 2 +- spec/requests/users/sessions_spec.rb | 24 ++++++++++---------- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Gemfile b/Gemfile index bb8954fd01..2734cf129c 100644 --- a/Gemfile +++ b/Gemfile @@ -15,8 +15,8 @@ gem "cssbundling-rails", "~> 1.1" # compiles css gem "delayed_job_active_record" gem "devise" # for authentication gem "devise_invitable" -gem 'devise-jwt' -gem 'rack-cors' +gem "devise-jwt" +gem "rack-cors" gem "httparty" # for making HTTP network requests 🥳 gem "twilio-ruby" # twilio helper functions gem "draper" # adds decorators for cleaner presentation logic diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 43b9512d58..8d667535ec 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Users::SessionsController < Devise::SessionsController - #respond_to :json + # respond_to :json include Accessible skip_before_action :check_user, only: :destroy @@ -9,7 +9,7 @@ def create respond_to do |format| format.html { super } format.json { - warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#faliure") + warden.authenticate!(scope: resource_name, recall: "#{controller_path}#faliure") render json: "Successfully authenticated", status: 200 } end diff --git a/app/models/jwt_denylist.rb b/app/models/jwt_denylist.rb index 053fb469ee..2eb57f14e2 100644 --- a/app/models/jwt_denylist.rb +++ b/app/models/jwt_denylist.rb @@ -1,7 +1,7 @@ class JwtDenylist < ApplicationRecord include Devise::JWT::RevocationStrategies::Denylist - self.table_name = 'jwt_denylist' + self.table_name = "jwt_denylist" end # == Schema Information diff --git a/config/environments/development.rb b/config/environments/development.rb index c73f706fce..beba8efc71 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -81,5 +81,5 @@ config.assets.digest = false # allow requests from ngrok - config.hosts << '.ngrok-free.app' + config.hosts << ".ngrok-free.app" end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index a5170af0a8..4884b6ca3b 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -1,10 +1,10 @@ Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do - origins '*' # make sure to change to domain name of frontend + origins "*" # make sure to change to domain name of frontend - resource '*', - headers: %w(Authorization), - methods: :any, - expose: %w(Authorization) + resource "*", + headers: %w[Authorization], + methods: :any, + expose: %w[Authorization] end end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 1bda029c93..1ae98686ce 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -300,7 +300,7 @@ # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. - config.navigational_formats = ['*/*', :html, :json] + config.navigational_formats = ["*/*", :html, :json] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :get diff --git a/db/schema.rb b/db/schema.rb index 197bc563b3..f38b23441b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_03_08_041745) do +ActiveRecord::Schema[7.0].define(version: 2023_06_15_155223) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/requests/users/sessions_spec.rb b/spec/requests/users/sessions_spec.rb index bafc6fd09a..d504fb58f1 100644 --- a/spec/requests/users/sessions_spec.rb +++ b/spec/requests/users/sessions_spec.rb @@ -2,14 +2,14 @@ RSpec.describe "Users::SessionsController", type: :request do let(:casa_org) { create(:casa_org) } - let(:volunteer) { create(:volunteer, casa_org: casa_org)} + let(:volunteer) { create(:volunteer, casa_org: casa_org) } describe "POST create" do before { @params = { - "user": { - "email": volunteer.email, - "password": volunteer.password + user: { + email: volunteer.email, + password: volunteer.password } } } @@ -22,16 +22,16 @@ } it "respond with jwt in response header" do - #header "Content-Type", "application/json; charset=utf-8" + # header "Content-Type", "application/json; charset=utf-8" post "/users/sign_in", as: :json, params: @params - #print request.headers + # print request.headers expect(response.headers).to have_key "Authorization" - expect(response.headers['Authorization']).to be_starts_with('Bearer') + expect(response.headers["Authorization"]).to be_starts_with("Bearer") expect(response.body).to eq "Successfully authenticated" end it "respond with status code 401 for bad request" do - post "/users/sign_in", as: :json, params: {"user": {"email": "suzume@tojimari.jp", "password": ""}} + post "/users/sign_in", as: :json, params: {user: {email: "suzume@tojimari.jp", password: ""}} expect(response.headers).not_to have_key "Authorization" expect(response.body).to eq "Something went wrong with API sign in :(" end @@ -43,18 +43,18 @@ after { expect(response).not_to have_http_status(:redirect) } it "adds JWT to denylist" do - #header "Content-Type", "application/json; charset=utf-8" + # header "Content-Type", "application/json; charset=utf-8" post "/users/sign_in", as: :json, params: @params # get JWT from response header - auth_bearer = response.headers['Authorization'] - #print auth_bearer + auth_bearer = response.headers["Authorization"] + # print auth_bearer expect { get "/users/sign_out", as: :json, headers: {"Authorization" => auth_bearer} }.to change(JwtDenylist, :count).by(1) end it "responds with status code 200" do - #header "Content-Type", "application/json; charset=utf-8" + # header "Content-Type", "application/json; charset=utf-8" post "/users/sign_in", as: :json, params: @params get "/users/sign_out", as: :json end From 38604c2aa35fd13808a891acff44d49b37962c83 Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Wed, 21 Jun 2023 15:07:46 -0400 Subject: [PATCH 7/8] run migrations to update db --- .allow_skipping_tests | 1 + 1 file changed, 1 insertion(+) diff --git a/.allow_skipping_tests b/.allow_skipping_tests index b8ba3680e2..fb3988957b 100644 --- a/.allow_skipping_tests +++ b/.allow_skipping_tests @@ -65,6 +65,7 @@ models/concerns/by_organization_scope.rb models/concerns/roles.rb models/fund_request.rb models/notification.rb +models/jwt_denylist.rb notifications/base_notification.rb notifications/delivery_methods/sms.rb notifications/emancipation_checklist_reminder_notification.rb From 884ef466743ba443438cae5b95f2e5985bd6fb92 Mon Sep 17 00:00:00 2001 From: Xihai Luo Date: Wed, 21 Jun 2023 15:59:25 -0400 Subject: [PATCH 8/8] add back confirmable module to user model --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index caaae61498..9694d4ad98 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -12,7 +12,7 @@ class User < ApplicationRecord validates_with UserValidator - devise :database_authenticatable, :invitable, :recoverable, :validatable, :timeoutable, :trackable, :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist + devise :database_authenticatable, :invitable, :recoverable, :validatable, :timeoutable, :trackable, :confirmable, :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist belongs_to :casa_org