propel_authentication 0.1.3 → 0.1.4

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -0
  3. data/README.md +251 -113
  4. data/lib/generators/{propel_auth → propel_authentication}/install_generator.rb +17 -17
  5. data/lib/generators/{propel_auth → propel_authentication}/templates/auth_mailer.rb +1 -1
  6. data/lib/generators/{propel_auth → propel_authentication}/templates/authenticatable.rb +3 -3
  7. data/lib/generators/{propel_auth → propel_authentication}/templates/concerns/confirmable.rb +2 -2
  8. data/lib/generators/{propel_auth → propel_authentication}/templates/concerns/lockable.rb +6 -6
  9. data/lib/generators/{propel_auth → propel_authentication}/templates/concerns/recoverable.rb +6 -6
  10. data/lib/generators/{propel_auth → propel_authentication/templates}/core/configuration_methods.rb +38 -21
  11. data/lib/generators/{propel_auth → propel_authentication}/templates/invitation.rb +2 -2
  12. data/lib/generators/{propel_auth/templates/propel_auth.rb.tt → propel_authentication/templates/propel_authentication.rb.tt} +6 -14
  13. data/lib/generators/{propel_auth → propel_authentication}/templates/test/concerns/lockable_test.rb.tt +12 -12
  14. data/lib/generators/{propel_auth → propel_authentication}/templates/test/concerns/propel_authentication_test.rb.tt +1 -1
  15. data/lib/generators/{propel_auth → propel_authentication}/templates/test/concerns/recoverable_test.rb.tt +7 -7
  16. data/lib/generators/{propel_auth → propel_authentication}/templates/user_test.rb.tt +1 -1
  17. data/lib/generators/{propel_auth → propel_authentication}/unpack_generator.rb +38 -25
  18. data/lib/propel_authentication.rb +3 -0
  19. metadata +98 -98
  20. data/lib/generators/propel_auth/pack_generator.rb +0 -277
  21. data/lib/propel_auth.rb +0 -3
  22. /data/lib/generators/{propel_auth → propel_authentication}/templates/agency.rb +0 -0
  23. /data/lib/generators/{propel_auth → propel_authentication}/templates/agent.rb +0 -0
  24. /data/lib/generators/{propel_auth → propel_authentication}/templates/auth/base_passwords_controller.rb.tt +0 -0
  25. /data/lib/generators/{propel_auth → propel_authentication}/templates/auth/base_tokens_controller.rb.tt +0 -0
  26. /data/lib/generators/{propel_auth → propel_authentication}/templates/auth/passwords_controller.rb.tt +0 -0
  27. /data/lib/generators/{propel_auth → propel_authentication}/templates/concerns/propel_authentication.rb +0 -0
  28. /data/lib/generators/{propel_auth → propel_authentication}/templates/concerns/rack_session_disable.rb +0 -0
  29. /data/lib/generators/{propel_auth → propel_authentication}/templates/config/environments/development_email.rb +0 -0
  30. /data/lib/generators/{propel_auth → propel_authentication}/templates/db/migrate/create_agencies.rb +0 -0
  31. /data/lib/generators/{propel_auth → propel_authentication}/templates/db/migrate/create_agents.rb +0 -0
  32. /data/lib/generators/{propel_auth → propel_authentication}/templates/db/migrate/create_invitations.rb +0 -0
  33. /data/lib/generators/{propel_auth → propel_authentication}/templates/db/migrate/create_organizations.rb +0 -0
  34. /data/lib/generators/{propel_auth → propel_authentication}/templates/db/migrate/create_users.rb +0 -0
  35. /data/lib/generators/{propel_auth → propel_authentication}/templates/db/seeds.rb +0 -0
  36. /data/lib/generators/{propel_auth → propel_authentication}/templates/organization.rb +0 -0
  37. /data/lib/generators/{propel_auth → propel_authentication}/templates/services/auth_notification_service.rb +0 -0
  38. /data/lib/generators/{propel_auth → propel_authentication}/templates/test/concerns/confirmable_test.rb.tt +0 -0
  39. /data/lib/generators/{propel_auth → propel_authentication}/templates/test/controllers/auth/lockable_integration_test.rb.tt +0 -0
  40. /data/lib/generators/{propel_auth → propel_authentication}/templates/test/controllers/auth/password_reset_integration_test.rb.tt +0 -0
  41. /data/lib/generators/{propel_auth → propel_authentication}/templates/test/controllers/auth/tokens_controller_test.rb.tt +0 -0
  42. /data/lib/generators/{propel_auth → propel_authentication}/templates/test/mailers/auth_mailer_test.rb.tt +0 -0
  43. /data/lib/generators/{propel_auth → propel_authentication}/templates/test/mailers/previews/auth_mailer_preview.rb +0 -0
  44. /data/lib/generators/{propel_auth → propel_authentication}/templates/tokens_controller.rb.tt +0 -0
  45. /data/lib/generators/{propel_auth → propel_authentication}/templates/user.rb +0 -0
  46. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/account_unlock.html.erb +0 -0
  47. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/account_unlock.text.erb +0 -0
  48. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/email_confirmation.html.erb +0 -0
  49. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/email_confirmation.text.erb +0 -0
  50. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/password_reset.html.erb +0 -0
  51. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/password_reset.text.erb +0 -0
  52. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/user_invitation.html.erb +0 -0
  53. /data/lib/generators/{propel_auth → propel_authentication}/templates/views/auth_mailer/user_invitation.text.erb +0 -0
  54. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/Dockerfile +0 -0
  55. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/Gemfile +0 -0
  56. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/README.md +0 -0
  57. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/Rakefile +0 -0
  58. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/assets/stylesheets/application.css +0 -0
  59. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/controllers/application_controller.rb +0 -0
  60. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/helpers/application_helper.rb +0 -0
  61. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/jobs/application_job.rb +0 -0
  62. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/mailers/application_mailer.rb +0 -0
  63. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/models/application_record.rb +0 -0
  64. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/views/layouts/application.html.erb +0 -0
  65. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/views/layouts/mailer.html.erb +0 -0
  66. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/views/layouts/mailer.text.erb +0 -0
  67. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/views/pwa/manifest.json.erb +0 -0
  68. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/app/views/pwa/service-worker.js +0 -0
  69. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/brakeman +0 -0
  70. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/dev +0 -0
  71. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/docker-entrypoint +0 -0
  72. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/rails +0 -0
  73. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/rake +0 -0
  74. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/rubocop +0 -0
  75. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/setup +0 -0
  76. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/bin/thrust +0 -0
  77. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/application.rb +0 -0
  78. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/boot.rb +0 -0
  79. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/cable.yml +0 -0
  80. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/credentials.yml.enc +0 -0
  81. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/database.yml +0 -0
  82. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/environment.rb +0 -0
  83. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/environments/development.rb +0 -0
  84. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/environments/production.rb +0 -0
  85. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/environments/test.rb +0 -0
  86. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/initializers/assets.rb +0 -0
  87. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/initializers/content_security_policy.rb +0 -0
  88. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/initializers/filter_parameter_logging.rb +0 -0
  89. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/initializers/inflections.rb +0 -0
  90. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/locales/en.yml +0 -0
  91. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/master.key +0 -0
  92. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/puma.rb +0 -0
  93. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/routes.rb +0 -0
  94. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config/storage.yml +0 -0
  95. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/config.ru +0 -0
  96. /data/lib/generators/{propel_auth → propel_authentication}/test/dummy/db/schema.rb +0 -0
  97. /data/lib/generators/{propel_auth → propel_authentication}/test/generators/authentication/controllers/tokens_controller_test.rb +0 -0
  98. /data/lib/generators/{propel_auth → propel_authentication}/test/generators/authentication/install_generator_test.rb +0 -0
  99. /data/lib/generators/{propel_auth → propel_authentication}/test/generators/authentication/uninstall_generator_test.rb +0 -0
  100. /data/lib/generators/{propel_auth → propel_authentication}/test/integration/generator_integration_test.rb +0 -0
  101. /data/lib/generators/{propel_auth → propel_authentication}/test/integration/multi_version_generator_test.rb +0 -0
@@ -2,15 +2,15 @@ require 'rails/generators/base'
2
2
  require 'rails/generators/active_record'
3
3
  require_relative 'unpack_generator'
4
4
  ##
5
- # PropelAuth installer that provides JWT-based authentication for Rails applications
5
+ # PropelAuthentication installer that provides JWT-based authentication for Rails applications
6
6
  #
7
7
  # This creates a COMPLETELY STANDALONE authentication system with no gem dependencies.
8
8
  # Usage:
9
- # rails generate propel_auth:install # Full standalone system (runtime + generators)
9
+ # rails generate propel_authentication:install # Full standalone system (runtime + generators)
10
10
  #
11
11
  # This generator:
12
12
  # 1. Installs all runtime code (models, controllers, views, tests, etc.)
13
- # 2. Automatically extracts generator logic to lib/generators/propel_auth/
13
+ # 2. Automatically extracts generator logic to lib/generators/propel_authentication/
14
14
  # 3. Creates a system that requires NO gem dependencies
15
15
  #
16
16
  # After installation, you can remove 'propel_auth' from your Gemfile completely.
@@ -19,13 +19,13 @@ require_relative 'unpack_generator'
19
19
  # Like Rails' built-in generators (action_text:install, devise:install),
20
20
  # this generator is designed to be run once per application.
21
21
  #
22
- require_relative 'core/configuration_methods'
22
+ require_relative 'templates/core/configuration_methods'
23
23
 
24
- module PropelAuth
24
+ module PropelAuthentication
25
25
  class InstallGenerator < Rails::Generators::Base
26
26
  source_root File.expand_path("templates", __dir__)
27
27
  include Rails::Generators::Migration
28
- include PropelAuth::ConfigurationMethods
28
+ include PropelAuthentication::ConfigurationMethods
29
29
 
30
30
  def self.next_migration_number(dir)
31
31
  ActiveRecord::Generators::Base.next_migration_number(dir)
@@ -37,12 +37,12 @@ module PropelAuth
37
37
  initialize_propel_auth_settings
38
38
 
39
39
  if behavior == :revoke
40
- remove_file "config/initializers/propel_auth.rb"
41
- say "Removed PropelAuth configuration", :red
40
+ remove_file "config/initializers/propel_authentication.rb"
41
+ say "Removed PropelAuthentication configuration", :red
42
42
  else
43
43
  # Convert to ERB template to support configuration
44
- template "propel_auth.rb.tt", "config/initializers/propel_auth.rb"
45
- say "Created PropelAuth configuration with namespace: #{namespace_display}, version: #{version_display}", :green
44
+ template "propel_authentication.rb.tt", "config/initializers/propel_authentication.rb"
45
+ say "Created PropelAuthentication configuration with namespace: #{namespace_display}, version: #{version_display}", :green
46
46
  end
47
47
  end
48
48
 
@@ -136,7 +136,7 @@ module PropelAuth
136
136
  end
137
137
 
138
138
  def extract_generator_for_customization
139
- generator_path = "lib/generators/propel_auth"
139
+ generator_path = "lib/generators/propel_authentication"
140
140
 
141
141
  if File.exist?(generator_path)
142
142
  say ""
@@ -147,13 +147,13 @@ module PropelAuth
147
147
  say "📦 Extracting generator logic for full customization...", :blue
148
148
 
149
149
  # Automatically run the unpack generator to extract generator logic
150
- invoke PropelAuth::UnpackGenerator, [], { force: false }
150
+ invoke PropelAuthentication::UnpackGenerator, [], { force: false }
151
151
 
152
152
  say ""
153
- say "✅ Generator logic extracted to lib/generators/propel_auth/", :green
153
+ say "✅ Generator logic extracted to lib/generators/propel_authentication/", :green
154
154
  say "💡 Your application is now completely standalone - no gem dependency needed!", :cyan
155
155
  say "🗑️ You can now remove 'propel_auth' from your Gemfile", :yellow
156
- say "📦 All PropelAuth runtime code is now in config/initializers/propel_auth.rb", :blue
156
+ say "📦 All PropelAuthentication runtime code is now in config/initializers/propel_authentication.rb", :blue
157
157
  end
158
158
  end
159
159
 
@@ -175,11 +175,11 @@ module PropelAuth
175
175
  end
176
176
 
177
177
  say "\n🎨 Customization:", :bold
178
- say "• Generator logic: lib/generators/propel_auth/install_generator.rb", :blue
179
- say "• Templates: lib/generators/propel_auth/templates/", :blue
178
+ say "• Generator logic: lib/generators/propel_authentication/install_generator.rb", :blue
179
+ say "• Templates: lib/generators/propel_authentication/templates/", :blue
180
180
  say "• Modify any part of the system - it's all yours now!", :cyan
181
181
 
182
- say "\n🗑️ To uninstall: rails destroy propel_auth:install", :yellow
182
+ say "\n🗑️ To uninstall: rails destroy propel_authentication:install", :yellow
183
183
  end
184
184
 
185
185
  private
@@ -167,7 +167,7 @@ class AuthMailer < ApplicationMailer
167
167
  # Generate confirmation URL with token
168
168
  def confirmation_url(user)
169
169
  token = user.confirmation_token
170
- base_url = PropelAuth.configuration.frontend_url || "#{request.protocol}#{request.host_with_port}"
170
+ base_url = PropelAuthentication.configuration.frontend_url || "#{request.protocol}#{request.host_with_port}"
171
171
  "#{base_url}/auth/confirm?token=#{token}"
172
172
  end
173
173
 
@@ -14,10 +14,10 @@ module Authenticatable
14
14
  email_address: email_address,
15
15
  organization_id: organization_id,
16
16
  iat: Time.now.to_i,
17
- exp: PropelAuth.configuration.jwt_expiration.from_now.to_i
17
+ exp: PropelAuthentication.configuration.jwt_expiration.from_now.to_i
18
18
  }
19
19
 
20
- JWT.encode(payload, PropelAuth.configuration.jwt_secret, 'HS256')
20
+ JWT.encode(payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
21
21
  end
22
22
 
23
23
  class_methods do
@@ -25,7 +25,7 @@ module Authenticatable
25
25
  def find_by_jwt_token(token)
26
26
  return nil unless token
27
27
 
28
- decoded_token = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
28
+ decoded_token = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
29
29
  payload = decoded_token.first
30
30
 
31
31
  # Check if token is expired (JWT gem handles this, but double-check)
@@ -108,11 +108,11 @@ module Confirmable
108
108
  end
109
109
 
110
110
  def confirmation_period
111
- PropelAuth.configuration.confirmation_period || 24.hours
111
+ PropelAuthentication.configuration.confirmation_period || 24.hours
112
112
  end
113
113
 
114
114
  def confirmation_resend_interval
115
- PropelAuth.configuration.confirmation_resend_interval || 1.minute
115
+ PropelAuthentication.configuration.confirmation_resend_interval || 1.minute
116
116
  end
117
117
 
118
118
  module ClassMethods
@@ -6,7 +6,7 @@ module Lockable
6
6
  return false unless locked_at.present?
7
7
 
8
8
  # Check if lockout duration has passed (automatic unlock)
9
- if locked_at + PropelAuth.configuration.lockout_duration > Time.current
9
+ if locked_at + PropelAuthentication.configuration.lockout_duration > Time.current
10
10
  true # Still within lockout period
11
11
  else
12
12
  # Lockout period expired, automatically unlock
@@ -26,7 +26,7 @@ module Lockable
26
26
 
27
27
  self.failed_login_attempts = (failed_login_attempts || 0) + 1
28
28
 
29
- if failed_login_attempts >= PropelAuth.configuration.max_failed_attempts
29
+ if failed_login_attempts >= PropelAuthentication.configuration.max_failed_attempts
30
30
  lock_account!
31
31
  else
32
32
  save!
@@ -42,7 +42,7 @@ module Lockable
42
42
  def lock_account!
43
43
  update!(
44
44
  locked_at: Time.current,
45
- failed_login_attempts: PropelAuth.configuration.max_failed_attempts
45
+ failed_login_attempts: PropelAuthentication.configuration.max_failed_attempts
46
46
  )
47
47
 
48
48
  # Send account locked email notification if enabled
@@ -75,7 +75,7 @@ module Lockable
75
75
  exp: 1.hour.from_now.to_i # Unlock tokens expire in 1 hour
76
76
  }
77
77
 
78
- JWT.encode(payload, PropelAuth.configuration.jwt_secret, 'HS256')
78
+ JWT.encode(payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
79
79
  end
80
80
 
81
81
  # Unlock account using a valid unlock token
@@ -83,7 +83,7 @@ module Lockable
83
83
  return false unless token.present?
84
84
 
85
85
  begin
86
- decoded_token = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
86
+ decoded_token = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
87
87
  payload = decoded_token.first
88
88
 
89
89
  # Verify token is for this user and is an unlock token
@@ -104,7 +104,7 @@ module Lockable
104
104
  return nil unless token.present?
105
105
 
106
106
  begin
107
- decoded_token = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
107
+ decoded_token = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
108
108
  payload = decoded_token.first
109
109
 
110
110
  # Check if token is expired (JWT gem handles this, but double-check)
@@ -15,12 +15,12 @@ module Recoverable
15
15
  email_address: self.email_address,
16
16
  type: 'password_reset',
17
17
  iat: now.to_f, # Use float for higher precision
18
- exp: PropelAuth.configuration.password_reset_expiration.from_now.to_i,
18
+ exp: PropelAuthentication.configuration.password_reset_expiration.from_now.to_i,
19
19
  password_hash: self.password_digest[0..10], # Bind token to current password
20
20
  nonce: SecureRandom.hex(8) # Add random nonce for uniqueness
21
21
  }
22
22
 
23
- JWT.encode(payload, PropelAuth.configuration.jwt_secret, 'HS256')
23
+ JWT.encode(payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
24
24
  end
25
25
 
26
26
  # Validate password reset token
@@ -28,7 +28,7 @@ module Recoverable
28
28
  return false if token.blank?
29
29
 
30
30
  begin
31
- decoded = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
31
+ decoded = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
32
32
  payload = decoded.first
33
33
 
34
34
  # Validate token structure and content
@@ -55,7 +55,7 @@ module Recoverable
55
55
  return false if new_password != new_password_confirmation
56
56
 
57
57
  # Validate password length (configuration is a Range)
58
- password_range = PropelAuth.configuration.password_length
58
+ password_range = PropelAuthentication.configuration.password_length
59
59
  return false unless password_range.include?(new_password.length)
60
60
 
61
61
  begin
@@ -77,7 +77,7 @@ module Recoverable
77
77
  return nil if token.blank?
78
78
 
79
79
  begin
80
- decoded = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
80
+ decoded = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
81
81
  payload = decoded.first
82
82
 
83
83
  # Validate token structure
@@ -102,7 +102,7 @@ module Recoverable
102
102
  return nil if token.blank?
103
103
 
104
104
  begin
105
- decoded = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
105
+ decoded = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
106
106
  decoded.first['user_id']
107
107
  rescue JWT::DecodeError
108
108
  nil
@@ -1,33 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # Module providing configuration detection and validation for PropelAuth generators
4
+ # Module providing configuration detection and validation for PropelAuthentication generators
5
5
  #
6
- module PropelAuth
6
+ module PropelAuthentication
7
7
  module ConfigurationMethods
8
8
 
9
- # Shared class options across all PropelAuth generators
9
+ # Shared class options across all PropelAuthentication generators
10
10
  def self.included(base)
11
11
  base.class_option :namespace,
12
- type: :string,
13
- default: nil,
14
- desc: "Authentication namespace (e.g., 'api', 'admin_api'). Use 'none' for no namespace. Defaults to PropelAuth configuration."
12
+ type: :string,
13
+ default: nil,
14
+ desc: "Authentication namespace (e.g., 'api', 'admin_api'). Use 'none' for no namespace. Defaults to PropelAuthentication configuration."
15
15
 
16
16
  base.class_option :version,
17
- type: :string,
18
- default: nil,
19
- desc: "Authentication version (e.g., 'v1', 'v2'). Use 'none' for no versioning. Defaults to PropelAuth configuration."
17
+ type: :string,
18
+ default: nil,
19
+ desc: "Authentication version (e.g., 'v1', 'v2'). Use 'none' for no versioning. Defaults to PropelAuthentication configuration."
20
20
 
21
21
  # Legacy support for existing --api-version option
22
22
  base.class_option :api_version,
23
- type: :string,
24
- default: nil,
25
- desc: "Legacy: Use --version instead. Authentication version (e.g., 'v1', 'v2')."
23
+ type: :string,
24
+ default: nil,
25
+ desc: "Legacy: Use --version instead. Authentication version (e.g., 'v1', 'v2')."
26
26
  end
27
27
 
28
28
  protected
29
29
 
30
- # Initialize shared PropelAuth settings used across all generators
30
+ # Initialize shared PropelAuthentication settings used across all generators
31
31
  def initialize_propel_auth_settings
32
32
  @auth_namespace = determine_auth_namespace
33
33
  @auth_version = determine_auth_version
@@ -36,7 +36,7 @@ module PropelAuth
36
36
  # Determine the authentication namespace to use
37
37
  # Priority order:
38
38
  # 1. Command line option (--namespace)
39
- # 2. PropelAuth configuration (if exists)
39
+ # 2. PropelAuthentication configuration (if exists)
40
40
  # 3. Default fallback (nil for clean URLs like /login)
41
41
  def determine_auth_namespace
42
42
  if options[:namespace] == 'none'
@@ -47,10 +47,10 @@ module PropelAuth
47
47
  return options[:namespace]
48
48
  end
49
49
 
50
- # Try to read from PropelAuth configuration
50
+ # Try to read from PropelAuthentication configuration
51
51
  begin
52
- if defined?(PropelAuth) && PropelAuth.configuration.respond_to?(:namespace)
53
- config_namespace = PropelAuth.configuration.namespace
52
+ if defined?(PropelAuthentication) && PropelAuthentication.configuration.respond_to?(:namespace)
53
+ config_namespace = PropelAuthentication.configuration.namespace
54
54
  return config_namespace if config_namespace.present? && config_namespace != 'none'
55
55
  end
56
56
  rescue => e
@@ -64,7 +64,7 @@ module PropelAuth
64
64
  # Determine the authentication version to use
65
65
  # Priority order:
66
66
  # 1. Command line option (--version or legacy --api-version)
67
- # 2. PropelAuth configuration (if exists)
67
+ # 2. PropelAuthentication configuration (if exists)
68
68
  # 3. Default fallback (nil for clean URLs like /login)
69
69
  def determine_auth_version
70
70
  # Check for 'none' in either option
@@ -82,10 +82,10 @@ module PropelAuth
82
82
  return options[:api_version]
83
83
  end
84
84
 
85
- # Try to read from PropelAuth configuration
85
+ # Try to read from PropelAuthentication configuration
86
86
  begin
87
- if defined?(PropelAuth) && PropelAuth.configuration.respond_to?(:version)
88
- config_version = PropelAuth.configuration.version
87
+ if defined?(PropelAuthentication) && PropelAuthentication.configuration.respond_to?(:version)
88
+ config_version = PropelAuthentication.configuration.version
89
89
  return config_version if config_version.present? && config_version != 'none'
90
90
  end
91
91
  rescue => e
@@ -130,5 +130,22 @@ module PropelAuth
130
130
  path_parts << 'auth'
131
131
  path_parts.join('/')
132
132
  end
133
+
134
+ # Helper methods for templates
135
+ def api_versioned?
136
+ @auth_version.present? && @auth_version != 'none'
137
+ end
138
+
139
+ def api_only_app?
140
+ defined?(Rails.application.config.api_only) && Rails.application.config.api_only
141
+ end
142
+
143
+ def controller_namespace
144
+ if api_versioned?
145
+ auth_controller_namespace
146
+ else
147
+ 'Auth'
148
+ end
149
+ end
133
150
  end
134
151
  end
@@ -42,14 +42,14 @@ class Invitation < ApplicationRecord
42
42
  invitation_hash: generate_invitation_hash
43
43
  }
44
44
 
45
- JWT.encode(payload, PropelAuth.configuration.jwt_secret, 'HS256')
45
+ JWT.encode(payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
46
46
  end
47
47
 
48
48
  # Find invitation by JWT token with validation
49
49
  def self.find_by_invitation_token(token)
50
50
  begin
51
51
  # Decode and validate the JWT token
52
- payload = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })[0]
52
+ payload = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })[0]
53
53
 
54
54
  # Verify this is an invitation token
55
55
  return nil unless payload['type'] == 'invitation'
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # PropelAuth Runtime and Configuration
3
+ # PropelAuthentication Runtime and Configuration
4
4
  # This file was generated by: rails generate propel_auth:install
5
5
  #
6
- # This contains ALL PropelAuth runtime functionality - no gem dependency required!
6
+ # This contains ALL PropelAuthentication runtime functionality - no gem dependency required!
7
7
  # This module provides JWT-based authentication for Rails applications
8
8
  # with features like account lockout, password reset, and email confirmation.
9
9
 
10
10
  require "rails"
11
11
 
12
- module PropelAuth
12
+ module PropelAuthentication
13
13
  class Error < StandardError; end
14
14
 
15
15
  class << self
@@ -88,18 +88,10 @@ module PropelAuth
88
88
  @version = nil # Default to no version for clean URLs like /login
89
89
  end
90
90
  end
91
-
92
- class Engine < Rails::Engine
93
- initializer 'propel_auth.load_generators' do |app|
94
- config.generators do |g|
95
- g.test_framework :minitest, fixture: true
96
- end
97
- end
98
- end
99
91
  end
100
92
 
101
- # Configure PropelAuth with secure defaults
102
- PropelAuth.configure do |config|
93
+ # Configure PropelAuthentication with secure defaults
94
+ PropelAuthentication.configure do |config|
103
95
  # JWT Configuration for API authentication
104
96
  config.jwt_secret = Rails.application.credentials.secret_key_base || ENV['SECRET_KEY_BASE'] || 'your-secret-key'
105
97
  config.jwt_expiration = 24.hours
@@ -135,7 +127,7 @@ PropelAuth.configure do |config|
135
127
  config.version = <%= @auth_version ? "'#{@auth_version}'" : 'nil' %>
136
128
  end
137
129
 
138
- # PropelAuth is now fully extracted to your application!
130
+ # PropelAuthentication is now fully extracted to your application!
139
131
  # - All runtime code is in this file
140
132
  # - Generator logic can be extracted with: rails generate propel_auth:unpack
141
133
  # - You can remove 'propel_auth' from your Gemfile after installation
@@ -21,13 +21,13 @@ class LockableTest < ActiveSupport::TestCase
21
21
 
22
22
  test "should lock account after max failed attempts" do
23
23
  # Simulate 10 failed attempts
24
- PropelAuth.configuration.max_failed_attempts.times do
24
+ PropelAuthentication.configuration.max_failed_attempts.times do
25
25
  @user.increment_failed_attempts
26
26
  end
27
27
 
28
- assert @user.locked?, "User should be locked after #{PropelAuth.configuration.max_failed_attempts} failed attempts"
28
+ assert @user.locked?, "User should be locked after #{PropelAuthentication.configuration.max_failed_attempts} failed attempts"
29
29
  assert @user.locked_at.present?, "Locked at timestamp should be set"
30
- assert_equal PropelAuth.configuration.max_failed_attempts, @user.failed_login_attempts, "Failed attempts should equal max"
30
+ assert_equal PropelAuthentication.configuration.max_failed_attempts, @user.failed_login_attempts, "Failed attempts should equal max"
31
31
  end
32
32
 
33
33
  test "should reset failed attempts on successful login" do
@@ -54,10 +54,10 @@ class LockableTest < ActiveSupport::TestCase
54
54
 
55
55
  test "should automatically unlock after lockout duration" do
56
56
  # Lock the account with a past timestamp
57
- past_time = (PropelAuth.configuration.lockout_duration + 1.minute).ago
57
+ past_time = (PropelAuthentication.configuration.lockout_duration + 1.minute).ago
58
58
  @user.update!(
59
59
  locked_at: past_time,
60
- failed_login_attempts: PropelAuth.configuration.max_failed_attempts
60
+ failed_login_attempts: PropelAuthentication.configuration.max_failed_attempts
61
61
  )
62
62
 
63
63
  # Check if automatically unlocked
@@ -69,7 +69,7 @@ class LockableTest < ActiveSupport::TestCase
69
69
  recent_time = 5.minutes.ago
70
70
  @user.update!(
71
71
  locked_at: recent_time,
72
- failed_login_attempts: PropelAuth.configuration.max_failed_attempts
72
+ failed_login_attempts: PropelAuthentication.configuration.max_failed_attempts
73
73
  )
74
74
 
75
75
  # Should still be locked
@@ -130,15 +130,15 @@ class LockableTest < ActiveSupport::TestCase
130
130
 
131
131
  test "should integrate with failed login attempt configuration" do
132
132
  # Test with different configuration
133
- original_max = PropelAuth.configuration.max_failed_attempts
134
- PropelAuth.configuration.max_failed_attempts = 3
133
+ original_max = PropelAuthentication.configuration.max_failed_attempts
134
+ PropelAuthentication.configuration.max_failed_attempts = 3
135
135
 
136
136
  # Should lock after 3 attempts with new config
137
137
  3.times { @user.increment_failed_attempts }
138
138
  assert @user.locked?, "Should respect updated configuration"
139
139
 
140
140
  # Restore original config
141
- PropelAuth.configuration.max_failed_attempts = original_max
141
+ PropelAuthentication.configuration.max_failed_attempts = original_max
142
142
  end
143
143
 
144
144
  # CRITICAL EDGE CASE TESTS - Missing from initial implementation
@@ -164,7 +164,7 @@ class LockableTest < ActiveSupport::TestCase
164
164
  iat: Time.now.to_i,
165
165
  exp: 1.second.ago.to_i
166
166
  }
167
- expired_token = JWT.encode(expired_payload, PropelAuth.configuration.jwt_secret, 'HS256')
167
+ expired_token = JWT.encode(expired_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
168
168
 
169
169
  assert_not @user.unlock_with_token!(expired_token), "Should reject expired unlock token"
170
170
  assert @user.locked?, "Should remain locked with expired token"
@@ -256,7 +256,7 @@ class LockableTest < ActiveSupport::TestCase
256
256
  @user.lock_account!
257
257
 
258
258
  unlock_token = @user.generate_unlock_token
259
- decoded = JWT.decode(unlock_token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
259
+ decoded = JWT.decode(unlock_token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
260
260
  payload = decoded.first
261
261
 
262
262
  assert_equal @user.id, payload['user_id'], "Token should contain correct user ID"
@@ -272,7 +272,7 @@ class LockableTest < ActiveSupport::TestCase
272
272
  @user.lock_account!
273
273
 
274
274
  unlock_token = @user.generate_unlock_token
275
- decoded = JWT.decode(unlock_token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
275
+ decoded = JWT.decode(unlock_token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
276
276
  payload = decoded.first
277
277
 
278
278
  token_lifetime = payload['exp'] - payload['iat']
@@ -48,7 +48,7 @@ class PropelAuthenticationTest < ActionDispatch::IntegrationTest
48
48
  user_id: @user.id,
49
49
  exp: 1.hour.ago.to_i
50
50
  }
51
- expired_token = JWT.encode(payload, PropelAuth.configuration.jwt_secret, 'HS256')
51
+ expired_token = JWT.encode(payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
52
52
 
53
53
  with_test_routes do
54
54
  get '/test_auth', headers: { 'Authorization' => "Bearer #{expired_token}" }
@@ -17,7 +17,7 @@ class RecoverableTest < ActiveSupport::TestCase
17
17
  assert_equal 3, reset_token.split('.').length, "JWT should have 3 parts separated by dots"
18
18
 
19
19
  # VERIFY TOKEN CONTENT: Decode and validate payload
20
- decoded = JWT.decode(reset_token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
20
+ decoded = JWT.decode(reset_token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
21
21
  payload = decoded.first
22
22
 
23
23
  assert_equal @user.id, payload['user_id'], "Token should contain correct user ID"
@@ -50,7 +50,7 @@ class RecoverableTest < ActiveSupport::TestCase
50
50
  assert @user.valid_password_reset_token?(reset_token), "Valid token should pass validation"
51
51
 
52
52
  # VERIFY TOKEN BINDING: Token should be bound to specific user
53
- decoded = JWT.decode(reset_token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
53
+ decoded = JWT.decode(reset_token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
54
54
  payload = decoded.first
55
55
  assert_equal @user.id, payload['user_id'], "Token should be bound to correct user"
56
56
  end
@@ -67,7 +67,7 @@ class RecoverableTest < ActiveSupport::TestCase
67
67
  exp: 1.second.ago.to_i, # Already expired
68
68
  password_hash: @user.password_digest[0..10]
69
69
  }
70
- expired_token = JWT.encode(expired_payload, PropelAuth.configuration.jwt_secret, 'HS256')
70
+ expired_token = JWT.encode(expired_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
71
71
 
72
72
  # VERIFY EXPIRATION: Expired token should be rejected
73
73
  assert_not @user.valid_password_reset_token?(expired_token), "Expired token should be rejected"
@@ -264,7 +264,7 @@ class RecoverableTest < ActiveSupport::TestCase
264
264
  exp: 1.second.ago.to_i, # Already expired
265
265
  password_hash: @user.password_digest[0..10]
266
266
  }
267
- expired_token = JWT.encode(expired_payload, PropelAuth.configuration.jwt_secret, 'HS256')
267
+ expired_token = JWT.encode(expired_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
268
268
 
269
269
  # EXECUTE LOOKUP: Try to find user with expired token
270
270
  found_user = User.find_user_by_password_reset_token(expired_token)
@@ -277,7 +277,7 @@ class RecoverableTest < ActiveSupport::TestCase
277
277
  # SECURITY: Verify token expiration is reasonable (not too long)
278
278
 
279
279
  reset_token = @user.generate_password_reset_token
280
- decoded = JWT.decode(reset_token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
280
+ decoded = JWT.decode(reset_token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
281
281
  payload = decoded.first
282
282
 
283
283
  token_lifetime = payload['exp'] - payload['iat']
@@ -299,7 +299,7 @@ class RecoverableTest < ActiveSupport::TestCase
299
299
  exp: 1.hour.from_now.to_i,
300
300
  password_hash: @user.password_digest[0..10]
301
301
  }
302
- unlock_token = JWT.encode(unlock_payload, PropelAuth.configuration.jwt_secret, 'HS256')
302
+ unlock_token = JWT.encode(unlock_payload, PropelAuthentication.configuration.jwt_secret, 'HS256')
303
303
 
304
304
  # VERIFY TYPE VALIDATION: Unlock token should not work for password reset
305
305
  assert_not @user.valid_password_reset_token?(unlock_token), "Should reject wrong token type"
@@ -313,7 +313,7 @@ class RecoverableTest < ActiveSupport::TestCase
313
313
  # SECURITY: Verify token includes password hash binding
314
314
 
315
315
  reset_token = @user.generate_password_reset_token
316
- decoded = JWT.decode(reset_token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })
316
+ decoded = JWT.decode(reset_token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })
317
317
  payload = decoded.first
318
318
 
319
319
  # VERIFY BINDING: Token should contain password hash fragment
@@ -47,7 +47,7 @@ class UserTest < ActiveSupport::TestCase
47
47
  assert_equal 3, token.split('.').length, "JWT should have 3 parts"
48
48
 
49
49
  # Verify token content
50
- payload = JWT.decode(token, PropelAuth.configuration.jwt_secret, true, { algorithm: 'HS256' })[0]
50
+ payload = JWT.decode(token, PropelAuthentication.configuration.jwt_secret, true, { algorithm: 'HS256' })[0]
51
51
  assert_equal user.id, payload['user_id'], "Token should include user ID"
52
52
  assert_equal user.organization_id, payload['organization_id'], "Token should include organization ID"
53
53
  assert payload['exp'].present?, "Token should include expiration"