Skip to content

Commit

Permalink
Replace Authlogic with Rails Authentication Generator
Browse files Browse the repository at this point in the history
  • Loading branch information
euxx committed Nov 13, 2024
1 parent e8048e8 commit cc5f10e
Show file tree
Hide file tree
Showing 26 changed files with 236 additions and 105 deletions.
3 changes: 1 addition & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ gem 'active_storage_validations'
gem 'bootsnap', '~> 1', require: false
gem 'bootstrap_form'
gem 'ahoy_matey'
# gem 'authlogic', '~> 6'
gem "authlogic", github: "binarylogic/authlogic", ref: "refs/pull/770/head"
gem 'bcrypt', '~> 3'
gem 'scrypt', '~> 3'
gem 'skylight'
gem 'sentry-rails'
Expand Down
16 changes: 2 additions & 14 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
GIT
remote: https://github.com/binarylogic/authlogic.git
revision: 318c758ad5cc8901a0d4c52c90af28519e0f8d19
ref: refs/pull/770/head
specs:
authlogic (6.4.3)
activemodel (>= 5.2, < 8.1)
activerecord (>= 5.2, < 8.1)
activesupport (>= 5.2, < 8.1)
request_store (~> 1.0)

GEM
remote: https://rubygems.org/
specs:
Expand Down Expand Up @@ -113,6 +102,7 @@ GEM
aws-sigv4 (1.10.1)
aws-eventstream (~> 1, >= 1.0.2)
base64 (0.2.0)
bcrypt (3.1.20)
benchmark (0.4.0)
bigdecimal (3.1.8)
bindex (0.8.1)
Expand Down Expand Up @@ -277,8 +267,6 @@ GEM
regexp_parser (2.9.2)
reline (0.5.11)
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
rexml (3.3.9)
rubocop (1.68.0)
json (~> 2.3)
Expand Down Expand Up @@ -371,8 +359,8 @@ PLATFORMS
DEPENDENCIES
active_storage_validations
ahoy_matey
authlogic!
aws-sdk-s3
bcrypt (~> 3)
bootsnap (~> 1)
bootstrap_form
brakeman
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ In favor of
- [Active Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html)
- [Bootstrap](https://github.com/twbs/bootstrap)
- [FontAwesome](https://github.com/FortAwesome/font-awesome-sass)
- [Authlogic](https://github.com/binarylogic/authlogic)
- [Rails Authentication Generator](https://www.bigbinary.com/blog/rails-8-introduces-a-basic-authentication-generator)
- [Sidekiq](https://github.com/mperham/sidekiq)

In favor of services
Expand Down
13 changes: 13 additions & 0 deletions app/channels/application_cable/connection.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user

def connect
set_current_user || reject_unauthorized_connection
end

private

def set_current_user
if session = Session.find_by(id: cookies.signed[:session_id])
self.current_user = session.user
end
end
end
end
24 changes: 1 addition & 23 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
class ApplicationController < ActionController::Base
helper_method :current_user, :current_user_session

private

def current_user
@current_user ||= current_user_session&.user
end

def current_user_session
@current_user_session ||= UserSession.find
end

def last_request_update_allowed?
false
end

def require_sign_in
redirect_to root_path, notice: 'Require sign in' unless current_user
end

def require_blank_user_session
redirect_to root_path, notice: 'Already signed in' if current_user
end
include Authentication
end
58 changes: 58 additions & 0 deletions app/controllers/concerns/authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module Authentication
extend ActiveSupport::Concern

included do
helper_method :authenticated?, :current_user
end

private

def authenticated?
current_session
end

def require_authentication
current_session || request_authentication
end

def current_session
Current.session ||= find_session_by_cookie
end

def find_session_by_cookie
Session.find_by(id: cookies.signed[:session_id])
end

def request_authentication
session[:return_to_after_authenticating] = request.url
redirect_to new_session_path
end

def after_authentication_url
session.delete(:return_to_after_authenticating) || root_url
end

def current_user
@current_user ||= current_session&.user
end

def require_sign_in
redirect_to root_path, notice: 'Require sign in' unless current_user
end

def require_blank_user_session
redirect_to root_path, notice: 'Already signed in' if current_user
end

def start_new_session_for(user)
user.sessions.create!(user_agent: request.user_agent, ip: request.remote_ip).tap do |session|
Current.session = session
cookies.signed.permanent[:session_id] = { value: session.id, httponly: true, same_site: :lax }
end
end

def terminate_session
current_session&.destroy
cookies.delete(:session_id)
end
end
33 changes: 33 additions & 0 deletions app/controllers/passwords_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class PasswordsController < ApplicationController
before_action :set_user_by_token, only: %i[ edit update ]

def new
end

def create
if user = User.find_by(email_address: params[:email_address])
PasswordsMailer.reset(user).deliver_later
end

redirect_to new_session_path, notice: "Password reset instructions sent (if user with that email address exists)."
end

def edit
end

def update
if @user.update(params.permit(:password, :password_confirmation))
redirect_to new_session_path, notice: "Password has been reset."
else
redirect_to edit_password_path(params[:token]), alert: "Passwords did not match."
end
end

private

def set_user_by_token
@user = User.find_by_password_reset_token!(params[:token])
rescue ActiveSupport::MessageVerifier::InvalidSignature
redirect_to new_password_path, alert: "Password reset link is invalid or has expired."
end
end
28 changes: 28 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class SessionsController < ApplicationController
before_action :require_blank_user_session, only: [:new, :create]
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_url, alert: "Try again later." }

def new
end

def create
if user = User.authenticate_by(session_params)
start_new_session_for(user)
ahoy.authenticate(user)
redirect_to after_authentication_url
else
redirect_to :new
end
end

def destroy
terminate_session
redirect_to root_path
end

private

def session_params
params.require(:session).permit(:email, :password)
end
end
28 changes: 0 additions & 28 deletions app/controllers/user_sessions_controller.rb

This file was deleted.

6 changes: 6 additions & 0 deletions app/mailers/passwords_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class PasswordsMailer < ApplicationMailer
def reset(user)
@user = user
mail subject: "Reset your password", to: user.email_address
end
end
4 changes: 4 additions & 0 deletions app/models/current.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Current < ActiveSupport::CurrentAttributes
attribute :session
delegate :user, to: :session, allow_nil: true
end
3 changes: 3 additions & 0 deletions app/models/session.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Session < ApplicationRecord
belongs_to :user
end
15 changes: 6 additions & 9 deletions app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
class User < ApplicationRecord
acts_as_authentic do |c|
c.crypto_provider = ::Authlogic::CryptoProviders::SCrypt
end
has_secure_password
has_many :sessions, dependent: :destroy

normalizes :email, with: ->(e) { e.strip.downcase }

validates :email,
format: { with: URI::MailTo::EMAIL_REGEXP, message: "should look like an email address." },
length: { maximum: 100 },
uniqueness: { case_sensitive: false, if: :will_save_change_to_email? }

validates :password,
confirmation: { if: :require_password? },
length: { minimum: 8, if: :require_password? }

validates :password_confirmation,
length: { minimum: 8, if: :require_password? }
validates :password, length: { minimum: 8 }, if: -> { password.present? }
validates :password_confirmation, length: { minimum: 8 }, if: -> { password_confirmation.present? }
end
2 changes: 0 additions & 2 deletions app/models/user_session.rb

This file was deleted.

11 changes: 11 additions & 0 deletions app/views/passwords/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="container col-sm-4">
<h1>Update your password</h1>

<%= bootstrap_form_with url: passwords_path , local: true do |f| %>
<%= f.password_field :password, required: true, autocomplete: 'new-password',
placeholder: 'Enter new password', maxlength: 72 %>
<%= f.password_field :password_confirmation, required: true, autocomplete: 'new-password',
placeholder: 'Repeat new password', maxlength: 72 %>
<%= f.submit 'Save', class: 'btn btn-success' %>
<% end %>
</div>
10 changes: 10 additions & 0 deletions app/views/passwords/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

<div class="container col-sm-4">
<h1>Forgot your password?</h1>

<%= bootstrap_form_with url: passwords_path , local: true do |f| %>
<%= f.email_field :email, required: true, value: params[:email],
autofocus: true, autocomplete: 'username' %>
<%= f.submit 'Email reset instructions', class: 'btn btn-success' %>
<% end %>
</div>
4 changes: 4 additions & 0 deletions app/views/passwords_mailer/reset.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<p>
You can reset your password within the next 15 minutes on
<%= link_to "this password reset page", edit_password_url(@user.password_reset_token) %>.
</p>
2 changes: 2 additions & 0 deletions app/views/passwords_mailer/reset.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
You can reset your password within the next 15 minutes on this password reset page:
<%= edit_password_url(@user.password_reset_token) %>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<div class="container col-sm-4">
<%= bootstrap_form_with model: @user_session, local: true do |f| %>
<%= bootstrap_form_with model: Session.new, local: true do |f| %>
<%= f.email_field :email, required: true %>
<%= f.password_field :password, required: true %>
<%= f.check_box :remember_me %>
<%= f.submit 'Sign in', class: 'btn btn-success' %>
<% end %>
<%= link_to "Forgot password?", new_password_path %>
</div>
7 changes: 4 additions & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
Rails.application.routes.draw do
root 'pages#home'

get 'sign_in' => 'user_sessions#new'
delete 'sign_out' => 'user_sessions#destroy'
get 'sign_in' => 'sessions#new'
delete 'sign_out' => 'sessions#destroy'

resources :user_sessions, only: :create
resources :sessions, only: [:new, :create]
resources :passwords, param: :token

mount Sidekiq::Web => '/sidekiq', constraints: AuthConstraint.new

Expand Down
11 changes: 11 additions & 0 deletions db/migrate/20241113033519_add_password_digest_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class AddPasswordDigestToUsers < ActiveRecord::Migration[8.0]
def up
add_column :users, :password_digest, :string

User.column_names.each do |column|
unless column.in? %w[id email password_digest created_at updated_at]
remove_column :users, column.to_sym
end
end
end
end
11 changes: 11 additions & 0 deletions db/migrate/20241113033520_create_sessions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateSessions < ActiveRecord::Migration[8.0]
def change
create_table :sessions do |t|
t.references :user, null: false, foreign_key: true
t.string :ip
t.string :user_agent

t.timestamps
end
end
end
Loading

0 comments on commit cc5f10e

Please sign in to comment.