rails-auth-eassy 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +206 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/stylesheets/rails/auth/application.css +15 -0
  6. data/app/controllers/concerns/rails/auth/authenticatable_controller.rb +114 -0
  7. data/app/controllers/rails/auth/application_controller.rb +9 -0
  8. data/app/controllers/rails/auth/confirmations_controller.rb +18 -0
  9. data/app/controllers/rails/auth/impersonations_controller.rb +46 -0
  10. data/app/controllers/rails/auth/mfa_controller.rb +26 -0
  11. data/app/controllers/rails/auth/otp_verifications_controller.rb +25 -0
  12. data/app/controllers/rails/auth/password_resets_controller.rb +51 -0
  13. data/app/controllers/rails/auth/profiles_controller.rb +24 -0
  14. data/app/controllers/rails/auth/registrations_controller.rb +27 -0
  15. data/app/controllers/rails/auth/security_controller.rb +27 -0
  16. data/app/controllers/rails/auth/sessions_controller.rb +69 -0
  17. data/app/controllers/rails/auth/unlocks_controller.rb +17 -0
  18. data/app/helpers/rails/auth/application_helper.rb +6 -0
  19. data/app/jobs/rails/auth/application_job.rb +6 -0
  20. data/app/mailers/rails/auth/application_mailer.rb +8 -0
  21. data/app/mailers/rails/auth/user_mailer.rb +20 -0
  22. data/app/models/concerns/rails/auth/authenticatable.rb +107 -0
  23. data/app/models/concerns/rails/auth/sessionable.rb +30 -0
  24. data/app/models/rails/auth/application_record.rb +7 -0
  25. data/app/models/rails/auth/current.rb +7 -0
  26. data/app/models/rails/auth/security_event.rb +25 -0
  27. data/app/views/layouts/rails/auth/application.html.erb +29 -0
  28. data/app/views/rails/auth/mfa/show.html.erb +24 -0
  29. data/app/views/rails/auth/otp_verifications/new.html.erb +13 -0
  30. data/app/views/rails/auth/password_resets/edit.html.erb +28 -0
  31. data/app/views/rails/auth/password_resets/new.html.erb +14 -0
  32. data/app/views/rails/auth/profiles/edit.html.erb +44 -0
  33. data/app/views/rails/auth/registrations/new.html.erb +40 -0
  34. data/app/views/rails/auth/security/sessions.html.erb +92 -0
  35. data/app/views/rails/auth/sessions/new.html.erb +20 -0
  36. data/app/views/rails/auth/user_mailer/confirmation_instructions.html.erb +5 -0
  37. data/app/views/rails/auth/user_mailer/password_reset.html.erb +8 -0
  38. data/app/views/rails/auth/user_mailer/password_reset.text.erb +8 -0
  39. data/app/views/rails/auth/user_mailer/unlock_instructions.html.erb +7 -0
  40. data/config/routes.rb +20 -0
  41. data/lib/generators/rails_auth/install/install_generator.rb +21 -0
  42. data/lib/generators/rails_auth/install/templates/rails_auth.rb +7 -0
  43. data/lib/generators/rails_auth/model/model_generator.rb +27 -0
  44. data/lib/generators/rails_auth/model/templates/create_rails_auth_tables.rb +60 -0
  45. data/lib/generators/rails_auth/model/templates/session.rb +3 -0
  46. data/lib/generators/rails_auth/model/templates/user.rb +3 -0
  47. data/lib/generators/rails_auth/views/views_generator.rb +13 -0
  48. data/lib/rails/auth/engine.rb +7 -0
  49. data/lib/rails/auth/version.rb +5 -0
  50. data/lib/rails/auth.rb +49 -0
  51. data/lib/tasks/rails/auth_tasks.rake +4 -0
  52. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 022e8a17beff8064ba4e48a9e57291c6380ef6240d753f2aac9b877619ad4525
4
+ data.tar.gz: f9c078225e321580caaa9d47bddd3905fbd5e6af673f1fd038430189100841e9
5
+ SHA512:
6
+ metadata.gz: 908356f0e6c4d3bb58dfca0f2af495b3837435a6130158f463cb235e682c780ce82183a2f7d18563677d9d94b179b18580c62599849c303e0e486741503c6e12
7
+ data.tar.gz: 8e42c802b4fa14ad066f7e2258ab9ed4bbaa0ce3f454b13708c33e6ae04e0dfd64a8feff0a71d437734cdd2f8b6f039ea30451bf2b161b7bf34c72d8133cbe43
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2026 Shiboshree Roy, Gemini CLI
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # 🛡️ Rails::Auth
2
+
3
+ **Rails::Auth** is a high-performance, security-first authentication engine for Ruby on Rails. Designed as a modern, transparent alternative to Devise, it empowers users with deep visibility and control over their account security through database-backed sessions and enterprise-grade protection.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/rails-auth.svg)](https://badge.fury.io/rb/rails-auth)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Rails Version](https://img.shields.io/badge/Rails-7.0+-red.svg)](https://rubyonrails.org)
8
+
9
+ ---
10
+
11
+ ## 📖 Table of Contents
12
+ - [🚀 Key Features](#-key-features)
13
+ - [📦 Installation](#-installation)
14
+ - [🛠️ Getting Started](#-getting-started)
15
+ - [📖 Usage](#-usage)
16
+ - [Controller Helpers](#controller-helpers)
17
+ - [Role-Based Access Control (RBAC)](#role-based-access-control-rbac)
18
+ - [Avatar Support](#avatar-support)
19
+ - [🛡️ Security Dashboard](#-security-dashboard)
20
+ - [⚙️ Configuration](#-configuration)
21
+ - [🎨 Customization](#-customization)
22
+ - [👥 Authors & Maintainers](#-authors--maintainers)
23
+ - [📄 License](#-license)
24
+
25
+ ---
26
+
27
+ ## 🚀 Key Features
28
+
29
+ ### 🔐 Enterprise Security
30
+ - **JWT Authentication**: Built-in support for stateless API authentication via `Authorization: Bearer <token>`.
31
+ - **Security Audit Logs**: Comprehensive tracking of sensitive actions (login failures, MFA toggles, password changes).
32
+ - **Admin Impersonation**: Securely log in as any user for customer support while tracking the original admin's actions.
33
+ - **Two-Factor Authentication (MFA)**: Modern TOTP support (Google Authenticator, Authy) with automated QR codes.
34
+ - **Account Locking (Lockable)**: Protection against brute-force attacks with email-based unlocking.
35
+ - **Email Confirmation (Confirmable)**: Ensure every user is verified before they can access your app.
36
+ - **Role-Based Access Control (RBAC)**: Built-in `user`, `moderator`, and `admin` roles with clean authorization helpers.
37
+
38
+ ### 📱 Advanced Session Management
39
+ - **Database-Backed Sessions**: Every device login is tracked, allowing for complete transparency.
40
+ - **Remote Device Revocation**: Log out of lost or stolen devices remotely from the dashboard.
41
+ - **Global Sign-Out**: Instant "Sign out of all devices" for emergency account security.
42
+ - **Device Intelligence**: Automatically identifies **Browser**, **OS**, and **IP Address** for every session.
43
+
44
+ ### 🛠️ Developer Experience
45
+ - **Active Storage Ready**: Seamlessly integrated avatar/profile picture support.
46
+ - **Model Independent**: Easily attach to `User`, `Admin`, `Member`, or any other model.
47
+ - **Minimalistic Core**: Built on top of Rails' native `has_secure_password`.
48
+ - **Generator Driven**: Scaffold everything you need in seconds.
49
+
50
+ ---
51
+
52
+ ## 📦 Installation
53
+
54
+ Add this line to your application's Gemfile:
55
+
56
+ ```ruby
57
+ gem "rails-auth"
58
+ ```
59
+
60
+ Then execute:
61
+ ```bash
62
+ $ bundle install
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 🛠️ Getting Started
68
+
69
+ ### 1. Run the Installer
70
+ Scaffold the configuration and engine routes:
71
+ ```bash
72
+ $ rails g rails_auth:install
73
+ ```
74
+
75
+ ### 2. Generate Your Authentication Model
76
+ Create or update your user model (e.g., `User`):
77
+ ```bash
78
+ $ rails g rails_auth:model User
79
+ ```
80
+ This will generate:
81
+ - The `User` & `Session` models.
82
+ - Migrations for MFA, Lockable, Confirmable, and RBAC.
83
+
84
+ ### 3. Run Migrations
85
+ ```bash
86
+ $ rails db:migrate
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 📖 Usage
92
+
93
+ ### Base Controller Setup
94
+ Include the authentication concern in your `ApplicationController`:
95
+
96
+ ```ruby
97
+ class ApplicationController < ActionController::Base
98
+ include Rails::Auth::AuthenticatableController
99
+ end
100
+ ```
101
+
102
+ ### Controller Helpers
103
+ | Helper | Description |
104
+ | :--- | :--- |
105
+ | `authenticate_user!` | Before action to ensure the user is logged in. |
106
+ | `current_user` | Returns the currently authenticated user. |
107
+ | `current_session` | Returns the database record for the current session. |
108
+ | `user_signed_in?` | Check if a user is currently authenticated. |
109
+ | `require_admin!` | Authorization helper to restrict access to admins. |
110
+ | `authorize_role!(*roles)` | Restrict access to specific roles (e.g., `:moderator`, `:admin`). |
111
+
112
+ ### Role-Based Access Control (RBAC)
113
+ ```ruby
114
+ class Admin::SettingsController < ApplicationController
115
+ before_action :require_admin!
116
+
117
+ def update
118
+ # Only admins can reach here
119
+ end
120
+ end
121
+ ```
122
+
123
+ ### Avatar Support
124
+ Rails::Auth uses Active Storage for avatars. Once Active Storage is installed in your host app:
125
+ ```erb
126
+ <% if current_user.avatar.attached? %>
127
+ <%= image_tag current_user.avatar.variant(resize_to_limit: [50, 50]) %>
128
+ <% end %>
129
+ ```
130
+
131
+ ### JWT API Authentication
132
+ Rails::Auth automatically supports JWTs for API endpoints.
133
+ 1. Send a `POST` to `/auth/session.json` with email/password.
134
+ 2. Receive a token: `{ "token": "eyJhb..." }`.
135
+ 3. Use it in subsequent requests: `Authorization: Bearer eyJhb...`
136
+
137
+ ### Admin Impersonation
138
+ Admins can impersonate users for support purposes:
139
+ ```ruby
140
+ # Start Impersonation (Controller action)
141
+ sign_in(user, impersonated_by: current_user)
142
+
143
+ # Stop Impersonation
144
+ rails_auth.stop_impersonations_path, method: :delete
145
+ ```
146
+ *Note: Impersonation events are securely logged in the `SecurityEvent` table.*
147
+
148
+ ---
149
+
150
+ ## 🛡️ Security Dashboard
151
+
152
+ Users can manage their security settings, view active sessions, and enable MFA at `/auth/security/sessions`.
153
+
154
+ ### Linking to the Dashboard
155
+ ```erb
156
+ <%= link_to "Security Settings", rails_auth.security_sessions_path %>
157
+ ```
158
+
159
+ ---
160
+
161
+ ## ⚙️ Configuration
162
+
163
+ Customize the gem behavior in `config/initializers/rails_auth.rb`:
164
+
165
+ ```ruby
166
+ Rails::Auth.setup do |config|
167
+ config.user_class_name = "User"
168
+ config.session_class_name = "Session"
169
+ end
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 🎨 Customization
175
+
176
+ ### Views
177
+ Override the default views by copying them to your application:
178
+ ```bash
179
+ $ rails g rails_auth:views
180
+ ```
181
+
182
+ ### Controllers
183
+ You can inherit from `Rails::Auth::ApplicationController` or include the `AuthenticatableController` in your own controllers for deep integration.
184
+
185
+ ---
186
+
187
+ ## 🧪 Testing
188
+
189
+ Rails::Auth is tested against modern Rails versions. Run the suite:
190
+ ```bash
191
+ $ bundle exec rake test
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 👥 Authors & Maintainers
197
+
198
+ - **Shiboshree Roy** - *Lead Developer*
199
+
200
+ ---
201
+
202
+ ## 📄 License
203
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
204
+
205
+ ---
206
+ Built with ❤️ for the Rails Community by **Shiboshree Roy**.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,114 @@
1
+ module Rails
2
+ module Auth
3
+ module AuthenticatableController
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_action :authenticate_user!
8
+ helper_method :current_user, :current_session, :user_signed_in?, :impersonating?
9
+ end
10
+
11
+ private
12
+
13
+ def authenticate_user!
14
+ redirect_to rails_auth.new_session_path unless user_signed_in?
15
+ end
16
+
17
+ def current_user
18
+ Rails::Auth::Current.user ||= (authenticate_by_session_token || authenticate_by_jwt)
19
+ end
20
+
21
+ def current_session
22
+ Rails::Auth::Current.session
23
+ end
24
+
25
+ def true_user
26
+ return nil unless current_session&.impersonated_by_id
27
+ Rails::Auth.user_class.find(current_session.impersonated_by_id)
28
+ end
29
+
30
+ def impersonating?
31
+ true_user.present?
32
+ end
33
+
34
+ def user_signed_in?
35
+ current_user.present?
36
+ end
37
+
38
+ def require_admin!
39
+ authorize_role!(:admin)
40
+ end
41
+
42
+ def authorize_role!(*roles)
43
+ unless user_signed_in? && roles.any? { |role| current_user.send("#{role}?") }
44
+ if request.format.json?
45
+ render json: { error: "Unauthorized" }, status: :forbidden
46
+ else
47
+ redirect_to main_app.root_path, alert: "You are not authorized to access this page."
48
+ end
49
+ end
50
+ end
51
+
52
+ def sign_in(user, impersonated_by: nil)
53
+ session_record = user.sessions.create!(
54
+ ip_address: request.remote_ip,
55
+ user_agent: request.user_agent,
56
+ impersonated_by_id: impersonated_by&.id,
57
+ last_active_at: Time.current
58
+ )
59
+
60
+ unless request.format.json?
61
+ cookies.signed.permanent[:session_token] = {
62
+ value: session_record.token,
63
+ httponly: true,
64
+ secure: Rails.env.production?
65
+ }
66
+ end
67
+
68
+ Rails::Auth::Current.user = user
69
+ Rails::Auth::Current.session = session_record
70
+
71
+ event = impersonated_by ? :impersonation_started : :login_success
72
+ user.log_security_event!(event, request, { impersonated_by_id: impersonated_by&.id })
73
+
74
+ session_record
75
+ end
76
+
77
+ def sign_out
78
+ current_user&.log_security_event!(:logout, request)
79
+ current_session&.destroy
80
+ cookies.delete(:session_token)
81
+ Rails::Auth::Current.user = nil
82
+ Rails::Auth::Current.session = nil
83
+ end
84
+
85
+ def authenticate_by_session_token
86
+ if token = cookies.signed[:session_token]
87
+ session_record = Rails::Auth.session_class.find_by(token: token)
88
+ if session_record
89
+ session_record.update(last_active_at: Time.current, ip_address: request.remote_ip)
90
+ Rails::Auth::Current.session = session_record
91
+ session_record.user
92
+ end
93
+ end
94
+ end
95
+
96
+ def authenticate_by_jwt
97
+ header = request.headers["Authorization"]
98
+ return nil unless header.present?
99
+
100
+ token = header.split(" ").last
101
+ decoded = Rails::Auth.decode_jwt(token)
102
+ return nil unless decoded
103
+
104
+ user = Rails::Auth.user_class.find_by(id: decoded[:user_id])
105
+ if user
106
+ # Optional: We could also track JWT sessions in the database if we wanted revocation,
107
+ # but usually JWTs are stateless. For "Ultimate" we might want to track them.
108
+ # Let's keep it simple for now or use a JTI claim.
109
+ user
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,9 @@
1
+ module Rails
2
+ module Auth
3
+ class ApplicationController < ::ApplicationController
4
+ include Rails::Auth::AuthenticatableController
5
+
6
+ protect_from_forgery with: :exception
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Rails
2
+ module Auth
3
+ class ConfirmationsController < ApplicationController
4
+ skip_before_action :authenticate_user!
5
+
6
+ def show
7
+ user = Rails::Auth.user_class.find_by(confirmation_token: params[:confirmation_token])
8
+ if user
9
+ user.confirm!
10
+ sign_in(user)
11
+ redirect_to main_app.root_path, notice: "Your account has been confirmed."
12
+ else
13
+ redirect_to new_session_path, alert: "Invalid confirmation token."
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ module Rails
2
+ module Auth
3
+ class ImpersonationsController < ApplicationController
4
+ before_action :require_admin!
5
+ skip_before_action :require_admin!, only: [ :destroy ]
6
+
7
+ def create
8
+ user = Rails::Auth.user_class.find(params[:user_id])
9
+
10
+ if user == current_user
11
+ redirect_to main_app.root_path, alert: "You cannot impersonate yourself."
12
+ return
13
+ end
14
+
15
+ # Store the current admin session so we can go back
16
+ admin_user = current_user
17
+
18
+ # Sign out current session (admin)
19
+ sign_out
20
+
21
+ # Sign in as the target user, but mark it as impersonated
22
+ sign_in(user, impersonated_by: admin_user)
23
+
24
+ redirect_to main_app.root_path, notice: "You are now impersonating #{user.email}."
25
+ end
26
+
27
+ def destroy
28
+ unless impersonating?
29
+ redirect_to main_app.root_path, alert: "You are not impersonating anyone."
30
+ return
31
+ end
32
+
33
+ admin_user = true_user
34
+
35
+ # Sign out of the impersonated session
36
+ current_user.log_security_event!(:impersonation_stopped, request, { impersonated_by_id: admin_user.id })
37
+ sign_out
38
+
39
+ # Sign back in as the admin
40
+ sign_in(admin_user)
41
+
42
+ redirect_to main_app.root_path, notice: "Impersonation stopped. Welcome back, #{admin_user.email}."
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ module Rails
2
+ module Auth
3
+ class MfaController < ApplicationController
4
+ def show
5
+ current_user.generate_otp_secret! unless current_user.otp_secret
6
+ @qrcode = RQRCode::QRCode.new(current_user.otp_provisioning_uri)
7
+ end
8
+
9
+ def create
10
+ if current_user.verify_otp(params[:otp_code])
11
+ current_user.update(otp_enabled: true)
12
+ redirect_to security_sessions_path, notice: "Two-factor authentication enabled."
13
+ else
14
+ flash.now[:alert] = "Invalid OTP code. Please try again."
15
+ @qrcode = RQRCode::QRCode.new(current_user.otp_provisioning_uri)
16
+ render :show, status: :unprocessable_entity
17
+ end
18
+ end
19
+
20
+ def destroy
21
+ current_user.update(otp_enabled: false, otp_secret: nil)
22
+ redirect_to security_sessions_path, notice: "Two-factor authentication disabled."
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module Rails
2
+ module Auth
3
+ class OtpVerificationsController < ApplicationController
4
+ skip_before_action :authenticate_user!
5
+
6
+ def new
7
+ unless session[:otp_user_id]
8
+ redirect_to new_session_path, alert: "Session expired."
9
+ end
10
+ end
11
+
12
+ def create
13
+ user = Rails::Auth.user_class.find(session[:otp_user_id])
14
+ if user.verify_otp(params[:otp_code])
15
+ session.delete(:otp_user_id)
16
+ sign_in(user)
17
+ redirect_to main_app.root_path, notice: "Signed in successfully."
18
+ else
19
+ flash.now[:alert] = "Invalid OTP code."
20
+ render :new, status: :unprocessable_entity
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ module Rails
2
+ module Auth
3
+ class PasswordResetsController < ApplicationController
4
+ skip_before_action :authenticate_user!
5
+ before_action :set_user, only: [ :edit, :update ]
6
+
7
+ def new
8
+ end
9
+
10
+ def create
11
+ @user = Rails::Auth.user_class.find_by(email: params[:email])
12
+ if @user
13
+ @user.generate_password_reset_token!
14
+ UserMailer.password_reset(@user).deliver_now
15
+ end
16
+ redirect_to rails_auth.new_session_path, notice: "If an account with that email exists, you will receive a password reset link shortly."
17
+ end
18
+
19
+ def edit
20
+ unless @user&.password_reset_token_valid?
21
+ redirect_to rails_auth.new_password_reset_path, alert: "Password reset link has expired or is invalid."
22
+ end
23
+ end
24
+
25
+ def update
26
+ if @user.password_reset_token_valid?
27
+ if @user.update(password_params)
28
+ @user.clear_password_reset_token!
29
+ @user.sessions.destroy_all # Security: Sign out of all sessions after password change
30
+ sign_in(@user)
31
+ redirect_to main_app.root_path, notice: "Password has been reset successfully and all other sessions have been signed out."
32
+ else
33
+ render :edit, status: :unprocessable_entity
34
+ end
35
+ else
36
+ redirect_to rails_auth.new_password_reset_path, alert: "Password reset link has expired."
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def set_user
43
+ @user = Rails::Auth.user_class.find_by!(reset_token: params[:id])
44
+ end
45
+
46
+ def password_params
47
+ params.require(:user).permit(:password, :password_confirmation)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,24 @@
1
+ module Rails
2
+ module Auth
3
+ class ProfilesController < ApplicationController
4
+ def edit
5
+ @user = current_user
6
+ end
7
+
8
+ def update
9
+ @user = current_user
10
+ if @user.update(profile_params)
11
+ redirect_to security_sessions_path, notice: "Profile updated successfully."
12
+ else
13
+ render :edit, status: :unprocessable_entity
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def profile_params
20
+ params.require(:user).permit(:email, :password, :password_confirmation, :avatar)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Rails
2
+ module Auth
3
+ class RegistrationsController < ApplicationController
4
+ skip_before_action :authenticate_user!, only: [ :new, :create ]
5
+
6
+ def new
7
+ @user = Rails::Auth.user_class.new
8
+ end
9
+
10
+ def create
11
+ @user = Rails::Auth.user_class.new(user_params)
12
+ if @user.save
13
+ @user.send_confirmation_instructions
14
+ redirect_to rails_auth.new_session_path, notice: "A confirmation link has been sent to your email address. Please follow the link to activate your account."
15
+ else
16
+ render :new, status: :unprocessable_entity
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def user_params
23
+ params.require(:user).permit(:email, :password, :password_confirmation, :avatar)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Rails
2
+ module Auth
3
+ class SecurityController < ApplicationController
4
+ def sessions
5
+ @sessions = current_user.sessions.order(last_active_at: :desc)
6
+ end
7
+
8
+ def revoke_session
9
+ session_to_revoke = current_user.sessions.find(params[:id])
10
+ session_to_revoke.destroy
11
+
12
+ if session_to_revoke == current_session
13
+ sign_out
14
+ redirect_to rails_auth.new_session_path, notice: "Your current session was revoked. Please sign in again."
15
+ else
16
+ redirect_to rails_auth.security_sessions_path, notice: "Session revoked successfully."
17
+ end
18
+ end
19
+
20
+ def revoke_all_sessions
21
+ current_user.sessions.destroy_all
22
+ sign_out
23
+ redirect_to rails_auth.new_session_path, notice: "All sessions have been revoked. Please sign in again."
24
+ end
25
+ end
26
+ end
27
+ end