Skip to content
This repository has been archived by the owner on Sep 22, 2022. It is now read-only.

Auth #33

Merged
merged 4 commits into from
Nov 5, 2021
Merged

Auth #33

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@
/yarn-error.log
yarn-debug.log*
.yarn-integrity

*.DS_Store
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM ruby:2.7.4

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
&& apt-get update && apt-get install -y nodejs yarn --no-install-recommends \
&& apt-get update && apt-get install -y nodejs yarn vim --no-install-recommends \
&& gem install rails

WORKDIR /app
Expand Down
11 changes: 10 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 5.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.4', require: false

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
Expand All @@ -41,3 +44,9 @@ end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'sassc', '~> 2.1.0'

gem "jwt", "~> 2.3"

gem "active_model_serializers", "~> 0.10.12"

gem "faker", "~> 2.19"
19 changes: 19 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ GEM
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.10.12)
actionpack (>= 4.1, < 6.2)
activemodel (>= 4.1, < 6.2)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (6.1.4.1)
activesupport (= 6.1.4.1)
globalid (>= 0.3.6)
Expand All @@ -60,19 +65,26 @@ GEM
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
bcrypt (3.1.16)
bindex (0.8.1)
bootsnap (1.9.1)
msgpack (~> 1.0)
builder (3.2.4)
byebug (11.1.3)
case_transform (0.2)
activesupport
concurrent-ruby (1.1.9)
crass (1.0.6)
erubi (1.10.0)
faker (2.19.0)
i18n (>= 1.6, < 2)
ffi (1.15.4)
globalid (0.5.2)
activesupport (>= 5.0)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
jsonapi-renderer (0.2.2)
jwt (2.3.0)
listen (3.7.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
Expand All @@ -96,6 +108,8 @@ GEM
nio4r (~> 2.0)
racc (1.6.0)
rack (2.2.3)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-mini-profiler (2.3.3)
rack (>= 1.2.0)
rack-proxy (0.7.0)
Expand Down Expand Up @@ -174,11 +188,16 @@ PLATFORMS
ruby

DEPENDENCIES
active_model_serializers (~> 0.10.12)
bcrypt (~> 3.1.7)
bootsnap (>= 1.4.4)
byebug
faker (~> 2.19)
jwt (~> 2.3)
listen (~> 3.3)
pg (~> 1.1)
puma (~> 5.0)
rack-cors
rack-mini-profiler (~> 2.0)
rails (~> 6.1.4, >= 6.1.4.1)
sass-rails (>= 6)
Expand Down
3 changes: 3 additions & 0 deletions app/assets/stylesheets/login.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Place all the styles related to the login controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
3 changes: 3 additions & 0 deletions app/assets/stylesheets/register.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Place all the styles related to the register controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
37 changes: 37 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,39 @@
class ApplicationController < ActionController::Base
before_action :authorized
SECRET_KEY = Rails.application.secrets.secret_key_base. to_s

def encode_token(payload, exp = 2.hours.from_now)
payload[:exp] = exp.to_i
JWT.encode(payload, SECRET_KEY)
end

def auth_header
request.headers['Authorization']
end

def decoded_token
if auth_header
token = auth_header.split(' ')[1]
begin
JWT.decode(token, SECRET_KEY, true, algorithm: 'HS256')
rescue JWT::DecodeError
nil
end
end
end

def current_user
if decoded_token
user_id = decoded_token[0]['user_id']
@user = User.find_by(id: user_id)
end
end

def logged_in?
!!current_user
end

def authorized
render json: { message: 'Please log in' }, status: :unauthorized unless logged_in?
end
end
20 changes: 20 additions & 0 deletions app/controllers/login_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class LoginController < ApplicationController
skip_before_action :verify_authenticity_token, :authorized, only: [:create]

def create
@user = User.find_by(username: user_login_params[:username])
#User#authenticate comes from BCrypt
if @user && @user.authenticate(user_login_params[:password])
@token = encode_token({ user_id: @user.id })
time = Time.now + 2.hours.to_i
render json: { user: UserSerializer.new(@user), jwt: @token, exp: time }, status: :accepted
else
render json: { message: 'Invalid username or password' }, status: :unauthorized
end
end

private
def user_login_params
params.require(:user).permit(:username, :password)
end
Comment on lines +17 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will require the frontend to send a JSON object in the following format:

{
    "user": {
        "username": "test3",
        "password": "testpw3"
    }
}

Nothing wrong with this functionality-wise, but just making sure that this is how you want it to be?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that is how I wrote it and tested it. We can just accept the username and password instead of a user json if you want.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way is fine. We just have to make sure the frontend people know the POST body should look like this for login

end
23 changes: 23 additions & 0 deletions app/controllers/register_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class RegisterController < ApplicationController
skip_before_action :verify_authenticity_token, :authorized, only: [:create]

def create
if User.exists?(username: user_params[:username])
render json: { error: 'Username taken'}, status: 409
return
end
@user = User.create(user_params)
if @user.valid?
@token = encode_token({ user_id: @user.id })
time = Time.now + 2.hours.to_i
render json: { user: UserSerializer.new(@user), jwt: @token, exp: time }, status: :created
Comment on lines +11 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify the intent here, but is the reason why you're returning a JWT token here as well because when a user registers, it should also automatically log them in?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is how the behavior is now just so you don't need to take two steps. It can easily be changed later if we don't want that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it the way you did it. Just making sure I'm understanding it correctly

else
render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity
end
end

private
def user_params
params.require(:user).permit(:username, :password, :role)
end
end
2 changes: 2 additions & 0 deletions app/helpers/login_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module LoginHelper
end
2 changes: 2 additions & 0 deletions app/helpers/register_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module RegisterHelper
end
8 changes: 4 additions & 4 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class User < ApplicationRecord
enum role: [:buyer, :seller]
validates :username, presence: true
validates :password, presence: true
validates :role, presence: true
has_secure_password
enum role: [:buyer, :seller]
validates :username, presence: true, uniqueness: { case_sensitive: false }
validates :role, presence: true
end
3 changes: 3 additions & 0 deletions app/serializers/user_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class UserSerializer < ActiveModel::Serializer
attributes :username, :role
end
7 changes: 7 additions & 0 deletions config/initializers/cors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
# Change to frontend domain
origins '*'
resource '*', headers: :any, methods: :any
end
end
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
root "doughnuts#index"
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :doughnuts
resources :register, only: [:create]
resources :login, only: [:create]
end
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class RemoveQuantityFromDoughnuts < ActiveRecord::Migration[6.1]
def change
remove_column :doughnuts, :quantity, :float
# remove_column :doughnuts, :quantity, :float
end
end
2 changes: 1 addition & 1 deletion db/migrate/20211103042845_create_users.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :username
t.string :password
t.string :password_digest
t.integer :role

t.timestamps
Expand Down
2 changes: 1 addition & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions test/controllers/login_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

class LoginControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
7 changes: 7 additions & 0 deletions test/controllers/register_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

class RegisterControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
4 changes: 2 additions & 2 deletions test/fixtures/users.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

one:
username: MyString
password: MyString
password_digest: MyString
role: 1

two:
username: MyString
password: MyString
password_digest: MyString
role: 1