rails_simple_auth 1.0.1 → 1.0.2

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +77 -5
  3. data/app/controllers/rails_simple_auth/confirmations_controller.rb +19 -17
  4. data/app/controllers/rails_simple_auth/omniauth_callbacks_controller.rb +6 -4
  5. data/app/controllers/rails_simple_auth/passwords_controller.rb +13 -12
  6. data/app/controllers/rails_simple_auth/registrations_controller.rb +6 -4
  7. data/app/controllers/rails_simple_auth/sessions_controller.rb +21 -16
  8. data/app/mailers/rails_simple_auth/auth_mailer.rb +10 -7
  9. data/app/views/rails_simple_auth/confirmations/new.html.erb +2 -2
  10. data/app/views/rails_simple_auth/passwords/new.html.erb +2 -2
  11. data/app/views/rails_simple_auth/registrations/new.html.erb +5 -5
  12. data/app/views/rails_simple_auth/sessions/magic_link_form.html.erb +2 -2
  13. data/app/views/rails_simple_auth/sessions/new.html.erb +2 -2
  14. data/lib/generators/rails_simple_auth/css/css_generator.rb +20 -20
  15. data/lib/generators/rails_simple_auth/install/install_generator.rb +32 -32
  16. data/lib/generators/rails_simple_auth/install/templates/initializer.rb +3 -3
  17. data/lib/generators/rails_simple_auth/install/templates/migration.rb +2 -2
  18. data/lib/generators/rails_simple_auth/temporary_users/USAGE +21 -0
  19. data/lib/generators/rails_simple_auth/temporary_users/templates/add_temporary_to_users.rb.erb +8 -0
  20. data/lib/generators/rails_simple_auth/temporary_users/temporary_users_generator.rb +40 -0
  21. data/lib/generators/rails_simple_auth/views/views_generator.rb +8 -8
  22. data/lib/rails_simple_auth/configuration.rb +21 -7
  23. data/lib/rails_simple_auth/controllers/concerns/authentication.rb +17 -18
  24. data/lib/rails_simple_auth/controllers/concerns/session_management.rb +24 -0
  25. data/lib/rails_simple_auth/engine.rb +1 -1
  26. data/lib/rails_simple_auth/models/concerns/authenticatable.rb +13 -5
  27. data/lib/rails_simple_auth/models/concerns/confirmable.rb +38 -3
  28. data/lib/rails_simple_auth/models/concerns/oauth_connectable.rb +5 -5
  29. data/lib/rails_simple_auth/models/concerns/temporary_user.rb +105 -0
  30. data/lib/rails_simple_auth/models/session.rb +2 -4
  31. data/lib/rails_simple_auth/routes.rb +15 -15
  32. data/lib/rails_simple_auth/version.rb +1 -1
  33. data/lib/rails_simple_auth.rb +14 -12
  34. metadata +15 -11
@@ -1,64 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rails/generators"
4
- require "rails/generators/active_record"
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
5
 
6
6
  module RailsSimpleAuth
7
7
  module Generators
8
8
  class InstallGenerator < Rails::Generators::Base
9
9
  include Rails::Generators::Migration
10
10
 
11
- source_root File.expand_path("templates", __dir__)
11
+ source_root File.expand_path('templates', __dir__)
12
12
 
13
- class_option :user_model, type: :string, default: "User",
14
- desc: "Name of your User model"
13
+ class_option :user_model, type: :string, default: 'User',
14
+ desc: 'Name of your User model'
15
15
  class_option :skip_migration, type: :boolean, default: false,
16
- desc: "Skip generating migration"
16
+ desc: 'Skip generating migration'
17
17
 
18
18
  def self.next_migration_number(dirname)
19
19
  ActiveRecord::Generators::Base.next_migration_number(dirname)
20
20
  end
21
21
 
22
22
  def create_initializer
23
- template "initializer.rb", "config/initializers/rails_simple_auth.rb"
23
+ template 'initializer.rb', 'config/initializers/rails_simple_auth.rb'
24
24
  end
25
25
 
26
26
  def create_migration
27
27
  return if options[:skip_migration]
28
28
 
29
- migration_template "migration.rb", "db/migrate/add_rails_simple_auth.rb"
29
+ migration_template 'migration.rb', 'db/migrate/add_rails_simple_auth.rb'
30
30
  end
31
31
 
32
32
  def add_routes
33
- route "rails_simple_auth_routes"
33
+ route 'rails_simple_auth_routes'
34
34
  end
35
35
 
36
36
  def show_instructions
37
- say ""
38
- say "RailsSimpleAuth installed successfully!", :green
39
- say ""
40
- say "Next steps:"
41
- say " 1. Review and edit the migration: db/migrate/xxx_add_rails_simple_auth.rb"
42
- say " 2. Run: rails db:migrate"
37
+ say ''
38
+ say 'RailsSimpleAuth installed successfully!', :green
39
+ say ''
40
+ say 'Next steps:'
41
+ say ' 1. Review and edit the migration: db/migrate/xxx_add_rails_simple_auth.rb'
42
+ say ' 2. Run: rails db:migrate'
43
43
  say " 3. Add concerns to your #{options[:user_model]} model:"
44
- say ""
44
+ say ''
45
45
  say " class #{options[:user_model]} < ApplicationRecord"
46
- say " include RailsSimpleAuth::Models::Concerns::Authenticatable"
47
- say " include RailsSimpleAuth::Models::Concerns::Confirmable # optional"
48
- say " include RailsSimpleAuth::Models::Concerns::MagicLinkable # optional"
49
- say " include RailsSimpleAuth::Models::Concerns::OAuthConnectable # optional"
50
- say " end"
51
- say ""
52
- say " 4. Add before_action to protect routes:"
53
- say ""
54
- say " class ApplicationController < ActionController::Base"
55
- say " before_action :require_authentication"
56
- say " end"
57
- say ""
58
- say "Optional generators:"
59
- say " rails generate rails_simple_auth:views # Copy views for customization"
60
- say " rails generate rails_simple_auth:css # Copy CSS for styling"
61
- say ""
46
+ say ' include RailsSimpleAuth::Models::Concerns::Authenticatable'
47
+ say ' include RailsSimpleAuth::Models::Concerns::Confirmable # optional'
48
+ say ' include RailsSimpleAuth::Models::Concerns::MagicLinkable # optional'
49
+ say ' include RailsSimpleAuth::Models::Concerns::OAuthConnectable # optional'
50
+ say ' end'
51
+ say ''
52
+ say ' 4. Add before_action to protect routes:'
53
+ say ''
54
+ say ' class ApplicationController < ActionController::Base'
55
+ say ' before_action :require_authentication'
56
+ say ' end'
57
+ say ''
58
+ say 'Optional generators:'
59
+ say ' rails generate rails_simple_auth:views # Copy views for customization'
60
+ say ' rails generate rails_simple_auth:css # Copy CSS for styling'
61
+ say ''
62
62
  end
63
63
  end
64
64
  end
@@ -58,14 +58,14 @@ RailsSimpleAuth.configure do |config|
58
58
  # ============================================================================
59
59
 
60
60
  # Layout to use for auth pages (default: "application")
61
- config.layout = "application"
61
+ config.layout = 'application'
62
62
 
63
63
  # ============================================================================
64
64
  # Mailer
65
65
  # ============================================================================
66
66
 
67
67
  # From address for auth emails
68
- config.mailer_sender = ENV.fetch("MAILER_FROM", "noreply@example.com")
68
+ config.mailer_sender = ENV.fetch('MAILER_FROM', 'noreply@example.com')
69
69
 
70
70
  # Custom mailer class (must implement confirmation, magic_link, password_reset methods)
71
71
  # config.mailer_class = "RailsSimpleAuth::AuthMailer"
@@ -75,7 +75,7 @@ RailsSimpleAuth.configure do |config|
75
75
  # ============================================================================
76
76
 
77
77
  # Your user model class name
78
- config.user_class_name = "<%= options[:user_model] %>"
78
+ config.user_class_name = '<%= options[:user_model] %>'
79
79
 
80
80
  # Custom session model (if you want to use your own)
81
81
  # config.session_class_name = "RailsSimpleAuth::Session"
@@ -11,7 +11,7 @@ class AddRailsSimpleAuth < ActiveRecord::Migration[8.0]
11
11
  # Uncomment and modify as needed:
12
12
 
13
13
  # Required fields for authentication
14
- # add_column :users, :email_address, :string, null: false
14
+ # add_column :users, :email, :string, null: false
15
15
  # add_column :users, :password_digest, :string, null: false
16
16
 
17
17
  # Email confirmation (optional - if using Confirmable concern)
@@ -22,7 +22,7 @@ class AddRailsSimpleAuth < ActiveRecord::Migration[8.0]
22
22
  # add_column :users, :admin, :boolean, default: false
23
23
 
24
24
  # Indexes
25
- # add_index :users, :email_address, unique: true
25
+ # add_index :users, :email, unique: true
26
26
  # add_index :users, :confirmed_at
27
27
 
28
28
  # ============================================================================
@@ -0,0 +1,21 @@
1
+ Description:
2
+ Creates a migration to add temporary user support to your User model.
3
+ Temporary users allow guest/demo user flows where visitors can try
4
+ your app without signing up, then convert to permanent accounts.
5
+
6
+ Example:
7
+ bin/rails generate rails_simple_auth:temporary_users
8
+
9
+ This will create:
10
+ db/migrate/YYYYMMDDHHMMSS_add_temporary_to_users.rb
11
+
12
+ After running the migration, include the concern in your User model:
13
+ include RailsSimpleAuth::Models::Concerns::TemporaryUser
14
+
15
+ And enable in your initializer:
16
+ config.temporary_users_enabled = true
17
+
18
+ The concern provides:
19
+ - temporary? / permanent? methods
20
+ - temporary / permanent / temporary_expired scopes
21
+ - convert_to_permanent!(email:, password:) for account conversion
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTemporaryToUsers < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
4
+ def change
5
+ add_column :users, :temporary, :boolean, default: false, null: false
6
+ add_index :users, [:temporary, :created_at], name: "index_users_on_temporary_and_created_at"
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+
6
+ module RailsSimpleAuth
7
+ module Generators
8
+ class TemporaryUsersGenerator < Rails::Generators::Base
9
+ include Rails::Generators::Migration
10
+
11
+ source_root File.expand_path('templates', __dir__)
12
+
13
+ desc 'Creates a migration to add temporary user support to your User model'
14
+
15
+ def self.next_migration_number(dirname)
16
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
17
+ end
18
+
19
+ def create_migration_file
20
+ migration_template 'add_temporary_to_users.rb.erb',
21
+ 'db/migrate/add_temporary_to_users.rb'
22
+ end
23
+
24
+ def show_instructions
25
+ say ''
26
+ say 'Temporary users migration created!', :green
27
+ say ''
28
+ say 'Next steps:', :yellow
29
+ say ' 1. Run: bin/rails db:migrate'
30
+ say ''
31
+ say ' 2. Include the concern in your User model:'
32
+ say ' include RailsSimpleAuth::Models::Concerns::TemporaryUser'
33
+ say ''
34
+ say ' 3. Enable in your initializer:'
35
+ say ' config.temporary_users_enabled = true'
36
+ say ''
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rails/generators"
3
+ require 'rails/generators'
4
4
 
5
5
  module RailsSimpleAuth
6
6
  module Generators
7
7
  class ViewsGenerator < Rails::Generators::Base
8
- source_root File.expand_path("../../../../app/views/rails_simple_auth", __dir__)
8
+ source_root File.expand_path('../../../../app/views/rails_simple_auth', __dir__)
9
9
 
10
10
  class_option :only, type: :array, default: [],
11
- desc: "Only copy specific view directories (sessions, registrations, passwords, confirmations, mailers)"
11
+ desc: 'Only copy specific view directories (sessions, registrations, passwords, confirmations, mailers)'
12
12
 
13
13
  def copy_views
14
14
  view_directories.each do |dir|
15
15
  directory dir, "app/views/rails_simple_auth/#{dir}"
16
16
  end
17
17
 
18
- say ""
19
- say "Views copied to app/views/rails_simple_auth/", :green
20
- say ""
21
- say "You can now customize these views. Rails will use your local copies"
18
+ say ''
19
+ say 'Views copied to app/views/rails_simple_auth/', :green
20
+ say ''
21
+ say 'You can now customize these views. Rails will use your local copies'
22
22
  say "instead of the gem's views."
23
- say ""
23
+ say ''
24
24
  end
25
25
 
26
26
  private
@@ -10,14 +10,17 @@ module RailsSimpleAuth
10
10
  :mailer_sender, :mailer_class,
11
11
  :user_class_name, :session_class_name,
12
12
  :password_minimum_length,
13
- :after_sign_in_callback, :after_sign_out_callback, :after_sign_up_callback, :after_confirmation_callback
13
+ :after_sign_in_callback, :after_sign_out_callback, :after_sign_up_callback, :after_confirmation_callback,
14
+ :temporary_users_enabled
15
+
16
+ attr_reader :temporary_user_cleanup_days
14
17
 
15
18
  def initialize
16
19
  @magic_link_enabled = true
17
20
  @email_confirmation_enabled = true
18
21
  @oauth_enabled = false
19
22
  @oauth_providers = []
20
- @oauth_link_existing_accounts = true # Allow OAuth to link to existing email accounts
23
+ @oauth_link_existing_accounts = true # Allow OAuth to link to existing email accounts
21
24
 
22
25
  @magic_link_expiry = 15.minutes
23
26
  @password_reset_expiry = 15.minutes
@@ -37,14 +40,14 @@ module RailsSimpleAuth
37
40
  @after_sign_up_path = :root_path
38
41
  @after_confirmation_path = :new_session_path
39
42
 
40
- @layout = "rails_simple_auth"
43
+ @layout = 'rails_simple_auth'
41
44
  @app_name = nil
42
45
 
43
- @mailer_sender = "noreply@example.com"
44
- @mailer_class = "RailsSimpleAuth::AuthMailer"
46
+ @mailer_sender = 'noreply@example.com'
47
+ @mailer_class = 'RailsSimpleAuth::AuthMailer'
45
48
 
46
- @user_class_name = "User"
47
- @session_class_name = "RailsSimpleAuth::Session"
49
+ @user_class_name = 'User'
50
+ @session_class_name = 'RailsSimpleAuth::Session'
48
51
 
49
52
  @password_minimum_length = 8
50
53
 
@@ -52,6 +55,9 @@ module RailsSimpleAuth
52
55
  @after_sign_out_callback = nil
53
56
  @after_sign_up_callback = nil
54
57
  @after_confirmation_callback = nil
58
+
59
+ @temporary_users_enabled = false
60
+ @temporary_user_cleanup_days = 7
55
61
  end
56
62
 
57
63
  def user_class
@@ -93,5 +99,13 @@ module RailsSimpleAuth
93
99
  def rate_limit_for(action)
94
100
  rate_limits&.dig(action.to_sym)
95
101
  end
102
+
103
+ def temporary_user_cleanup_days=(value)
104
+ unless value.is_a?(Integer) && value.positive?
105
+ raise ConfigurationError, 'temporary_user_cleanup_days must be a positive integer'
106
+ end
107
+
108
+ @temporary_user_cleanup_days = value
109
+ end
96
110
  end
97
111
  end
@@ -17,9 +17,9 @@ module RailsSimpleAuth
17
17
  return unless (session_token = cookies.signed.permanent[:session_token])
18
18
 
19
19
  session_record = RailsSimpleAuth.configuration.session_class
20
- .includes(:user)
21
- .active
22
- .find_by(id: session_token)
20
+ .includes(:user)
21
+ .active
22
+ .find_by(id: session_token)
23
23
 
24
24
  if session_record
25
25
  RailsSimpleAuth::Current.user = session_record.user
@@ -51,8 +51,8 @@ module RailsSimpleAuth
51
51
 
52
52
  # SECURITY: Validate path to prevent open redirect attacks
53
53
  # Only store relative paths that start with / but not //
54
- return unless path.start_with?("/")
55
- return if path.start_with?("//")
54
+ return unless path.start_with?('/')
55
+ return if path.start_with?('//')
56
56
 
57
57
  session[:return_to] = path
58
58
  end
@@ -63,34 +63,33 @@ module RailsSimpleAuth
63
63
 
64
64
  def redirect_to_sign_in
65
65
  respond_to do |format|
66
- format.html { redirect_to new_session_path, alert: "Please sign in to continue." }
67
- format.json { render json: { error: "Authentication required" }, status: :unauthorized }
68
- format.turbo_stream { redirect_to new_session_path, alert: "Please sign in to continue." }
66
+ format.html { redirect_to new_session_path, alert: 'Please sign in to continue.' }
67
+ format.json { render json: { error: 'Authentication required' }, status: :unauthorized }
68
+ format.turbo_stream { redirect_to new_session_path, alert: 'Please sign in to continue.' }
69
69
  end
70
70
  end
71
71
 
72
72
  def client_ip
73
- request.headers["CF-Connecting-IP"] ||
74
- request.headers["X-Forwarded-For"]&.split(",")&.first&.strip ||
73
+ request.headers['CF-Connecting-IP'] ||
74
+ request.headers['X-Forwarded-For']&.split(',')&.first&.strip ||
75
75
  request.remote_ip
76
76
  end
77
77
 
78
78
  def resolve_path(config_key)
79
79
  path_config = RailsSimpleAuth.configuration.public_send(config_key)
80
80
 
81
- result = case path_config
81
+ case path_config
82
82
  when Symbol then send(path_config)
83
83
  when Proc then path_config.call(self)
84
84
  when String then path_config
85
85
  else
86
- Rails.logger.warn(
87
- "[RailsSimpleAuth] Invalid path configuration for #{config_key}: " \
88
- "expected Symbol, Proc, or String, got #{path_config.class.name}. " \
89
- "Falling back to root_path."
90
- )
91
- root_path
86
+ Rails.logger.warn(
87
+ "[RailsSimpleAuth] Invalid path configuration for #{config_key}: " \
88
+ "expected Symbol, Proc, or String, got #{path_config.class.name}. " \
89
+ 'Falling back to root_path.'
90
+ )
91
+ root_path
92
92
  end
93
- result
94
93
  rescue NoMethodError => e
95
94
  Rails.logger.error(
96
95
  "[RailsSimpleAuth] Path helper '#{path_config}' not found for #{config_key}. " \
@@ -39,6 +39,30 @@ module RailsSimpleAuth
39
39
  RailsSimpleAuth::Current.session = nil
40
40
  end
41
41
 
42
+ # Destroy temporary user session when signing in with a different account
43
+ # This cleans up guest/demo users when they sign in or register
44
+ # @param signing_in_user [User, nil] The user being signed in (to avoid self-destruction)
45
+ def destroy_temporary_user_session(signing_in_user = nil)
46
+ return unless RailsSimpleAuth.configuration.temporary_users_enabled
47
+ return unless RailsSimpleAuth::Current.user&.temporary?
48
+
49
+ temp_user = RailsSimpleAuth::Current.user
50
+
51
+ # Don't destroy if the user is re-authenticating as themselves
52
+ return if signing_in_user && temp_user.id == signing_in_user.id
53
+
54
+ temp_user_id = temp_user.id
55
+
56
+ temp_user.transaction do
57
+ destroy_current_session
58
+ temp_user.destroy!
59
+ end
60
+
61
+ Rails.logger.info("[RailsSimpleAuth] Destroyed temporary user #{temp_user_id} on sign in")
62
+ rescue ActiveRecord::RecordNotDestroyed => e
63
+ Rails.logger.error("[RailsSimpleAuth] Failed to destroy temporary user #{temp_user_id}: #{e.message}")
64
+ end
65
+
42
66
  # Run after sign in callback if configured
43
67
  def run_after_sign_in_callback(user)
44
68
  run_callback(:after_sign_in_callback, user)
@@ -8,7 +8,7 @@ module RailsSimpleAuth
8
8
  g.test_framework :minitest
9
9
  end
10
10
 
11
- initializer "rails_simple_auth.helpers" do
11
+ initializer 'rails_simple_auth.helpers' do
12
12
  ActiveSupport.on_load(:action_controller_base) do
13
13
  include RailsSimpleAuth::Controllers::Concerns::Authentication
14
14
  include RailsSimpleAuth::Controllers::Concerns::SessionManagement
@@ -10,25 +10,26 @@ module RailsSimpleAuth
10
10
  has_secure_password
11
11
 
12
12
  has_many :sessions,
13
- class_name: "RailsSimpleAuth::Session",
13
+ class_name: RailsSimpleAuth.configuration.session_class_name,
14
14
  dependent: :destroy,
15
15
  inverse_of: :user
16
16
 
17
- validates :email_address,
17
+ validates :email,
18
18
  presence: true,
19
19
  uniqueness: { case_sensitive: false },
20
- format: { with: URI::MailTo::EMAIL_REGEXP }
20
+ format: { with: URI::MailTo::EMAIL_REGEXP },
21
+ unless: :temporary?
21
22
 
22
23
  validate :password_meets_minimum_length, if: :password_required?
23
24
 
24
- normalizes :email_address, with: ->(email) { email.strip.downcase }
25
+ normalizes :email, with: ->(email) { email.strip.downcase }
25
26
  end
26
27
 
27
28
  class_methods do
28
29
  def find_by_email(email)
29
30
  return nil if email.blank?
30
31
 
31
- find_by(email_address: email.to_s.strip.downcase)
32
+ find_by(email: email.to_s.strip.downcase)
32
33
  end
33
34
  end
34
35
 
@@ -47,9 +48,16 @@ module RailsSimpleAuth
47
48
  count
48
49
  end
49
50
 
51
+ # Returns false by default. Override in TemporaryUser concern.
52
+ def temporary?
53
+ false
54
+ end
55
+
50
56
  private
51
57
 
52
58
  def password_required?
59
+ return false if temporary?
60
+
53
61
  password_digest.blank? || password.present?
54
62
  end
55
63
 
@@ -8,6 +8,7 @@ module RailsSimpleAuth
8
8
 
9
9
  included do
10
10
  # Requires `confirmed_at` datetime column
11
+ # Optional `unconfirmed_email` string column for reconfirmation
11
12
  scope :confirmed, -> { where.not(confirmed_at: nil) }
12
13
  scope :unconfirmed, -> { where(confirmed_at: nil) }
13
14
  end
@@ -22,17 +23,51 @@ module RailsSimpleAuth
22
23
  !confirmed?
23
24
  end
24
25
 
26
+ # Check if user is changing their email (reconfirmation)
27
+ def reconfirming?
28
+ respond_to?(:unconfirmed_email) && unconfirmed_email.present?
29
+ end
30
+
31
+ # Check if user needs confirmation (either unconfirmed or reconfirming)
32
+ def unconfirmed_or_reconfirming?
33
+ unconfirmed? || reconfirming?
34
+ end
35
+
36
+ # Get the email that needs confirmation
37
+ def confirmable_email
38
+ reconfirming? ? unconfirmed_email : email
39
+ end
40
+
25
41
  # Confirm the user's email
42
+ # Handles both initial confirmation and reconfirmation (email change)
43
+ # Also sets temporary: false if TemporaryUser concern is included
44
+ # Returns true on success, false on failure (with errors populated)
26
45
  def confirm!
27
- return true if confirmed?
46
+ attrs = { confirmed_at: Time.current }
47
+ attrs[:temporary] = false if respond_to?(:temporary?)
28
48
 
29
- update!(confirmed_at: Time.current)
49
+ if reconfirming?
50
+ # Email change confirmation - check email uniqueness first
51
+ if self.class.where.not(id: id).exists?(email: unconfirmed_email)
52
+ errors.add(:email, 'is already taken by another user')
53
+ return false
54
+ end
55
+ attrs[:email] = unconfirmed_email
56
+ attrs[:unconfirmed_email] = nil
57
+ update(attrs)
58
+ elsif unconfirmed?
59
+ # Initial confirmation
60
+ update(attrs)
61
+ else
62
+ # Already confirmed and not reconfirming
63
+ true
64
+ end
30
65
  end
31
66
 
32
67
  # Generate email confirmation token using Rails signed_id
33
68
  def generate_confirmation_token
34
69
  signed_id(
35
- purpose: :email_confirmation,
70
+ purpose: :confirm_email,
36
71
  expires_in: RailsSimpleAuth.configuration.confirmation_expiry
37
72
  )
38
73
  end
@@ -18,13 +18,13 @@ module RailsSimpleAuth
18
18
  # - true (default): Links OAuth to existing email accounts (safe for providers that verify emails)
19
19
  # - false: Only allows OAuth for accounts created via OAuth with same provider+uid
20
20
  def from_oauth(auth_hash)
21
- email = auth_hash.dig("info", "email")
22
- provider = auth_hash["provider"]
23
- uid = auth_hash["uid"]
21
+ email = auth_hash.dig('info', 'email')
22
+ provider = auth_hash['provider']
23
+ uid = auth_hash['uid']
24
24
 
25
25
  if email.blank?
26
26
  Rails.logger.warn(
27
- "[RailsSimpleAuth] OAuth auth_hash missing email. " \
27
+ '[RailsSimpleAuth] OAuth auth_hash missing email. ' \
28
28
  "Provider: #{provider}, UID: #{uid}. Ensure email scope is requested."
29
29
  )
30
30
  return nil
@@ -62,7 +62,7 @@ module RailsSimpleAuth
62
62
 
63
63
  # Create new user for new OAuth signups
64
64
  user = new(
65
- email_address: email,
65
+ email: email,
66
66
  password: SecureRandom.hex(32) # Random password for OAuth users
67
67
  )
68
68