propel_authentication 0.1.4 → 0.2.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -2
  3. data/README.md +6 -6
  4. data/lib/generators/propel_authentication/install_generator.rb +135 -153
  5. data/lib/generators/propel_authentication/templates/application_mailer.rb +6 -0
  6. data/lib/generators/propel_authentication/templates/auth/passwords_controller.rb.tt +84 -78
  7. data/lib/generators/propel_authentication/templates/auth/signup_controller.rb.tt +242 -0
  8. data/lib/generators/propel_authentication/templates/{tokens_controller.rb.tt → auth/tokens_controller.rb.tt} +39 -22
  9. data/lib/generators/propel_authentication/templates/auth_mailer.rb +3 -1
  10. data/lib/generators/propel_authentication/templates/authenticatable.rb +8 -2
  11. data/lib/generators/propel_authentication/templates/concerns/confirmable.rb +1 -1
  12. data/lib/generators/propel_authentication/templates/concerns/lockable.rb +4 -2
  13. data/lib/generators/propel_authentication/templates/concerns/{propel_authentication.rb → propel_authentication_concern.rb} +33 -3
  14. data/lib/generators/propel_authentication/templates/concerns/recoverable.rb +16 -6
  15. data/lib/generators/propel_authentication/templates/core/configuration_methods.rb +104 -64
  16. data/lib/generators/propel_authentication/templates/db/seeds.rb +50 -4
  17. data/lib/generators/propel_authentication/templates/doc/signup_flow.md +315 -0
  18. data/lib/generators/propel_authentication/templates/models/agency.rb.tt +13 -0
  19. data/lib/generators/propel_authentication/templates/models/agent.rb.tt +13 -0
  20. data/lib/generators/propel_authentication/templates/{invitation.rb → models/invitation.rb.tt} +6 -0
  21. data/lib/generators/propel_authentication/templates/models/organization.rb.tt +12 -0
  22. data/lib/generators/propel_authentication/templates/{user.rb → models/user.rb.tt} +5 -0
  23. data/lib/generators/propel_authentication/templates/propel_authentication.rb.tt +94 -9
  24. data/lib/generators/propel_authentication/templates/routes/auth_routes.rb.tt +55 -0
  25. data/lib/generators/propel_authentication/templates/services/auth_notification_service.rb +3 -3
  26. data/lib/generators/propel_authentication/templates/test/concerns/confirmable_test.rb.tt +34 -10
  27. data/lib/generators/propel_authentication/templates/test/concerns/propel_authentication_test.rb.tt +1 -1
  28. data/lib/generators/propel_authentication/templates/test/concerns/recoverable_test.rb.tt +4 -4
  29. data/lib/generators/propel_authentication/templates/test/controllers/auth/lockable_integration_test.rb.tt +18 -15
  30. data/lib/generators/propel_authentication/templates/test/controllers/auth/password_reset_integration_test.rb.tt +38 -40
  31. data/lib/generators/propel_authentication/templates/test/controllers/auth/signup_controller_test.rb.tt +201 -0
  32. data/lib/generators/propel_authentication/templates/test/controllers/auth/tokens_controller_test.rb.tt +33 -25
  33. data/lib/generators/propel_authentication/templates/test/mailers/auth_mailer_test.rb.tt +51 -36
  34. data/lib/generators/propel_authentication/templates/views/auth_mailer/email_confirmation.html.erb +2 -2
  35. data/lib/generators/propel_authentication/templates/views/auth_mailer/email_confirmation.text.erb +1 -1
  36. data/lib/generators/propel_authentication/test/generators/authentication/install_generator_test.rb +4 -4
  37. data/lib/generators/propel_authentication/test/generators/authentication/uninstall_generator_test.rb +1 -1
  38. data/lib/generators/propel_authentication/test/integration/generator_integration_test.rb +1 -1
  39. data/lib/generators/propel_authentication/test/integration/multi_version_generator_test.rb +13 -12
  40. data/lib/generators/propel_authentication/unpack_generator.rb +19 -15
  41. data/lib/propel_authentication.rb +1 -1
  42. metadata +14 -11
  43. data/lib/generators/propel_authentication/templates/agency.rb +0 -7
  44. data/lib/generators/propel_authentication/templates/agent.rb +0 -7
  45. data/lib/generators/propel_authentication/templates/auth/base_passwords_controller.rb.tt +0 -99
  46. data/lib/generators/propel_authentication/templates/auth/base_tokens_controller.rb.tt +0 -90
  47. data/lib/generators/propel_authentication/templates/organization.rb +0 -7
@@ -0,0 +1,315 @@
1
+ # Progressive Signup Flow
2
+
3
+ This authentication system supports a **progressive multi-step signup flow** that allows users to create organizations and agencies based on their needs.
4
+
5
+ ## Overview
6
+
7
+ The signup flow creates:
8
+ 1. **Organization** - The main tenant/company
9
+ 2. **User** - The primary user account (owner role)
10
+ 3. **Agency** - Optional organizational unit (if agency tenancy enabled + provided)
11
+ 4. **Agent** - Automatic relationship between user and agency (if agency created)
12
+
13
+ ## Configuration
14
+
15
+ Agency tenancy can be enabled/disabled in `config/initializers/propel_api.rb`:
16
+
17
+ ```ruby
18
+ PropelApi.configure do |config|
19
+ config.agency_tenancy = true # Enable agency-level tenancy
20
+ # config.agency_tenancy = false # Disable for organization-only tenancy
21
+ end
22
+ ```
23
+
24
+ ## Signup Endpoints
25
+
26
+ ### Simple Signup (Organization + User only)
27
+ ```http
28
+ POST /signup
29
+ Content-Type: application/json
30
+
31
+ {
32
+ "user": {
33
+ "email_address": "john@newcompany.com",
34
+ "username": "john_doe",
35
+ "password": "securepassword123",
36
+ "password_confirmation": "securepassword123",
37
+ "first_name": "John",
38
+ "last_name": "Doe",
39
+ "phone_number": "+1-555-0123",
40
+ "time_zone": "America/New_York"
41
+ },
42
+ "organization": {
43
+ "name": "NewCo Inc",
44
+ "website": "https://newco.com",
45
+ "time_zone": "America/New_York",
46
+ "description": "Innovative solutions company"
47
+ }
48
+ }
49
+ ```
50
+
51
+ #### Response (agency tenancy disabled)
52
+ ```json
53
+ {
54
+ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
55
+ "user": {
56
+ "id": 123,
57
+ "email_address": "john@newcompany.com",
58
+ "username": "john_doe",
59
+ "first_name": "John",
60
+ "last_name": "Doe",
61
+ "organization_id": 456,
62
+ "created_at": "2024-01-15T10:30:00Z"
63
+ },
64
+ "organization": {
65
+ "id": 456,
66
+ "name": "NewCo Inc",
67
+ "website": "https://newco.com",
68
+ "time_zone": "America/New_York"
69
+ },
70
+ "agency": null,
71
+ "agent": null,
72
+ "message": "Account created successfully! Ready to start working.",
73
+ "next_steps": [
74
+ {
75
+ "action": "invite_team_members",
76
+ "description": "Invite colleagues to join your organization",
77
+ "endpoint": "/invitations",
78
+ "method": "POST"
79
+ },
80
+ {
81
+ "action": "start_creating_resources",
82
+ "description": "Begin creating and managing your content",
83
+ "endpoint": "/"
84
+ }
85
+ ]
86
+ }
87
+ ```
88
+
89
+ ### Complete Signup (Organization + User + Agency + Agent)
90
+ ```http
91
+ POST /signup
92
+ Content-Type: application/json
93
+
94
+ {
95
+ "user": {
96
+ "email_address": "sarah@designstudio.com",
97
+ "username": "sarah_creative",
98
+ "password": "securepassword123",
99
+ "password_confirmation": "securepassword123",
100
+ "first_name": "Sarah",
101
+ "last_name": "Designer"
102
+ },
103
+ "organization": {
104
+ "name": "Creative Design Studio",
105
+ "website": "https://designstudio.com",
106
+ "time_zone": "UTC"
107
+ },
108
+ "agency": {
109
+ "name": "Creative Department",
110
+ "description": "Main creative and design operations",
111
+ "website": "https://creative.designstudio.com",
112
+ "phone_number": "+1-555-DESIGN"
113
+ },
114
+ "agent": {
115
+ "role": "owner"
116
+ }
117
+ }
118
+ ```
119
+
120
+ #### Response (agency tenancy enabled)
121
+ ```json
122
+ {
123
+ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
124
+ "user": {
125
+ "id": 789,
126
+ "email_address": "sarah@designstudio.com",
127
+ "username": "sarah_creative",
128
+ "first_name": "Sarah",
129
+ "last_name": "Designer",
130
+ "organization_id": 101,
131
+ "created_at": "2024-01-15T14:20:00Z"
132
+ },
133
+ "organization": {
134
+ "id": 101,
135
+ "name": "Creative Design Studio",
136
+ "website": "https://designstudio.com",
137
+ "time_zone": "UTC"
138
+ },
139
+ "agency": {
140
+ "id": 202,
141
+ "name": "Creative Department",
142
+ "organization_id": 101,
143
+ "description": "Main creative and design operations",
144
+ "website": "https://creative.designstudio.com"
145
+ },
146
+ "agent": {
147
+ "id": 303,
148
+ "user_id": 789,
149
+ "agency_id": 202,
150
+ "role": "owner",
151
+ "created_at": "2024-01-15T14:20:00Z"
152
+ },
153
+ "message": "Account created successfully! Ready to start working.",
154
+ "next_steps": [
155
+ {
156
+ "action": "create_additional_agencies",
157
+ "description": "Add more agencies to organize your team",
158
+ "endpoint": "/agencies",
159
+ "method": "POST"
160
+ },
161
+ {
162
+ "action": "start_creating_resources",
163
+ "description": "Begin creating and managing your content (you can create resources immediately)",
164
+ "endpoint": "/",
165
+ "note": "Your agency is ready for resource creation"
166
+ },
167
+ {
168
+ "action": "invite_team_members",
169
+ "description": "Invite colleagues to join your organization",
170
+ "endpoint": "/invitations",
171
+ "method": "POST"
172
+ }
173
+ ]
174
+ }
175
+ ```
176
+
177
+ ## Progressive Enhancement Flow
178
+
179
+ ### Step 1: Core Signup
180
+ User creates account with minimal information (User + Organization).
181
+
182
+ ### Step 2: Add Agencies (Optional)
183
+ ```http
184
+ POST /api/v1/agencies
185
+ Authorization: Bearer {token_from_signup}
186
+ Content-Type: application/json
187
+
188
+ {
189
+ "data": {
190
+ "name": "Marketing Department",
191
+ "description": "Marketing and growth operations"
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### Step 3: Invite Team Members
197
+ ```http
198
+ POST /api/v1/invitations
199
+ Authorization: Bearer {token_from_signup}
200
+ Content-Type: application/json
201
+
202
+ {
203
+ "data": {
204
+ "email_address": "teammate@designstudio.com",
205
+ "agency_id": 202,
206
+ "role": "manager"
207
+ }
208
+ }
209
+ ```
210
+
211
+ ### Step 4: Start Creating Resources
212
+ With the JWT token, users can immediately create resources:
213
+
214
+ ```http
215
+ POST /api/v1/projects
216
+ Authorization: Bearer {token_from_signup}
217
+ Content-Type: application/json
218
+
219
+ {
220
+ "data": {
221
+ "name": "New Website Design",
222
+ "agency_id": 202
223
+ }
224
+ }
225
+ ```
226
+
227
+ ## Error Handling
228
+
229
+ ### Missing Required Agency Data (when agency tenancy enabled)
230
+ ```json
231
+ {
232
+ "error": "Agency information required",
233
+ "message": "Agency details must be provided when agency tenancy is enabled",
234
+ "code": "MISSING_AGENCY_DATA",
235
+ "hint": "Include an 'agency' object with name and other details in your request"
236
+ }
237
+ ```
238
+
239
+ ### Validation Errors
240
+ ```json
241
+ {
242
+ "error": "Validation failed",
243
+ "details": {
244
+ "email_address": ["Email address has already been taken"],
245
+ "password": ["Password confirmation doesn't match Password"]
246
+ },
247
+ "message": "Please correct the errors and try again"
248
+ }
249
+ ```
250
+
251
+ ## JWT Token Structure
252
+
253
+ The returned JWT token contains:
254
+ ```json
255
+ {
256
+ "user_id": 789,
257
+ "email_address": "sarah@designstudio.com",
258
+ "organization_id": 101,
259
+ "agency_ids": [202],
260
+ "iat": 1705329600,
261
+ "exp": 1705416000
262
+ }
263
+ ```
264
+
265
+ This enables immediate API access with proper organizational and agency scoping.
266
+
267
+ ## User-Provided vs. System-Automatic
268
+
269
+ ### 🔴 REQUIRED User Input
270
+ - **email_address** - Validated for format & uniqueness
271
+ - **username** - Validated for uniqueness
272
+ - **password** - Minimum 8 characters, secure validation
273
+ - **password_confirmation** - Must match password
274
+ - **organization.name** - Organization name (required)
275
+
276
+ ### 🟡 OPTIONAL User Input
277
+ - **first_name, last_name** - For personalization (optional)
278
+ - **phone_number** - Contact information (optional)
279
+ - **time_zone** - User/organization timezone (optional)
280
+ - **organization.website** - Company website (optional)
281
+ - **organization.description** - Company description (optional)
282
+ - **agency.{name, description, etc}** - When agency tenancy enabled (optional)
283
+
284
+ ### 🤖 SYSTEM-AUTOMATIC
285
+ - **user.id** - Auto-generated primary key
286
+ - **password_digest** - Auto-hashed with bcrypt
287
+ - **organization_id** - Auto-assigned from created organization
288
+ - **role: 'owner'** - Auto-assigned for signup user
289
+ - **JWT token** - Auto-generated for immediate API access
290
+ - **Agent record** - Auto-created if agency provided
291
+ - **created_at/updated_at** - Auto-set timestamps
292
+ - **Organization/agency scoping** - Auto-applied to all resources
293
+
294
+ ## Architecture Benefits
295
+
296
+ 1. **Immediate Access**: Users can start working immediately after signup
297
+ 2. **Progressive Enhancement**: Start simple, add complexity as needed
298
+ 3. **Proper Scoping**: All resources automatically scoped to organization/agency
299
+ 4. **Security**: JWT tokens contain proper context for authorization
300
+ 5. **Flexibility**: Works with or without agency tenancy
301
+ 6. **Smart Defaults**: Minimal required input, system handles the rest
302
+
303
+ ## Configuration Options
304
+
305
+ Users can choose their tenancy model:
306
+
307
+ - **Organization-only tenancy**: `agency_tenancy = false`
308
+ - Simpler data model
309
+ - Resources scoped to organization only
310
+ - Good for single-agency organizations
311
+
312
+ - **Multi-agency tenancy**: `agency_tenancy = true` (default)
313
+ - More complex but flexible
314
+ - Resources scoped to organization + agency
315
+ - Good for multi-department organizations
@@ -0,0 +1,13 @@
1
+ class Agency < ApplicationRecord
2
+ # Multi-tenant associations
3
+ belongs_to :organization
4
+ has_many :agents, dependent: :destroy
5
+
6
+ validates :name, presence: true
7
+ <% if @rendering_engine == 'json_facet' -%>
8
+
9
+ # Facets
10
+ json_facet :short, fields: [:id, :name]
11
+ json_facet :details, fields: [:id, :name, :organization_id, :created_at, :updated_at]
12
+ <% end -%>
13
+ end
@@ -0,0 +1,13 @@
1
+ class Agent < ApplicationRecord
2
+ # Multi-tenant associations
3
+ belongs_to :agency
4
+ belongs_to :user
5
+
6
+ validates :user_id, uniqueness: { scope: :agency_id }
7
+ <% if @rendering_engine == 'json_facet' -%>
8
+
9
+ # Facets
10
+ json_facet :short, fields: [:id, :title]
11
+ json_facet :details, fields: [:id, :title, :role, :organization_id, :created_at, :updated_at]
12
+ <% end -%>
13
+ end
@@ -15,6 +15,12 @@ class Invitation < ApplicationRecord
15
15
  scope :valid, -> { where(status: :pending).where('created_at > ?', 7.days.ago) }
16
16
  scope :recent, -> { where('created_at > ?', 30.days.ago) }
17
17
 
18
+ <% if @rendering_engine == 'json_facet' -%>
19
+ # Facets
20
+ json_facet :short, fields: [:id, :email_address, :organization_id]
21
+ json_facet :details, fields: [:id, :name, :status, :organization_id, :created_at, :updated_at]
22
+ <% end -%>
23
+
18
24
  # Check if invitation is still valid (not expired)
19
25
  def invitation_valid?
20
26
  pending? && created_at > 7.days.ago
@@ -0,0 +1,12 @@
1
+ class Organization < ApplicationRecord
2
+ # Multi-tenant associations
3
+ has_many :users, dependent: :destroy
4
+ has_many :agencies, dependent: :destroy
5
+
6
+ validates :name, presence: true
7
+ <% if @rendering_engine == 'json_facet' -%>
8
+ # Facets
9
+ json_facet :short, fields: [:id, :name, :website, :time_zone]
10
+ json_facet :details, fields: [:id, :name, :website, :time_zone, :meta, :settings, :created_at, :updated_at]
11
+ <% end -%>
12
+ end
@@ -3,6 +3,7 @@ class User < ApplicationRecord
3
3
  belongs_to :organization
4
4
  has_many :invitations, foreign_key: :inviter_id, dependent: :destroy
5
5
  has_many :agents, dependent: :destroy
6
+ has_many :agencies, through: :agents
6
7
 
7
8
  # Validations
8
9
  validates :email_address, presence: true, uniqueness: true
@@ -18,4 +19,8 @@ class User < ApplicationRecord
18
19
  # Future enhancements (Epic 2)
19
20
  # include Invitable
20
21
  # include Statusable
22
+
23
+ # Facets
24
+ json_facet :short, fields: [:id, :email_address, :username, :phone_number, :first_name, :middle_name, :last_name, :time_zone, :status]
25
+ json_facet :details, fields: [:id, :email_address, :username, :phone_number, :first_name, :middle_name, :last_name, :time_zone, :confirmation_sent_at, :unconfirmed_email_address, :confirmed_at, :status, :last_login_at, :failed_login_attempts, :locked_at, :organization_id, :meta, :settings, :created_at, :updated_at], include: [:organization]
21
26
  end
@@ -44,8 +44,12 @@ module PropelAuthentication
44
44
  :support_email,
45
45
  :enable_email_notifications,
46
46
  :enable_sms_notifications,
47
+ :agency_tenancy,
48
+ :require_organization_id,
49
+ :require_user_id,
47
50
  :namespace,
48
- :version
51
+ :version,
52
+ :auth_scope
49
53
 
50
54
  def initialize
51
55
  # JWT Configuration defaults
@@ -73,19 +77,48 @@ module PropelAuthentication
73
77
  @password_reset_rate_limit = 1.minute
74
78
 
75
79
  # Frontend URL for email links (configure for your frontend application)
76
- @frontend_url = Rails.env.development? ? 'http://localhost:3000' : nil
80
+ # Priority: Rails credentials -> ENV variables -> environment-specific fallbacks
81
+ @frontend_url = Rails.application.credentials.frontend_url ||
82
+ ENV['FRONTEND_URL'] ||
83
+ case Rails.env
84
+ when 'development', 'test'
85
+ 'http://localhost:3000'
86
+ when 'staging'
87
+ 'https://staging.yourapp.com' # Replace with your staging URL
88
+ when 'production'
89
+ 'https://yourapp.com' # Replace with your production URL
90
+ else
91
+ 'http://localhost:3000' # Safe fallback for any other environment
92
+ end
77
93
 
78
94
  # Email configuration
79
- @email_from_address = "noreply@#{Rails.application.class.module_parent.name.downcase}.com"
80
- @support_email = "support@#{Rails.application.class.module_parent.name.downcase}.com"
95
+ # Priority: Rails credentials -> ENV variables -> sensible defaults
96
+ @email_from_address = Rails.application.credentials.email_from_address ||
97
+ ENV['EMAIL_FROM_ADDRESS'] ||
98
+ "noreply@#{Rails.application.class.module_parent.name.downcase}.com"
99
+
100
+ @support_email = Rails.application.credentials.support_email ||
101
+ ENV['SUPPORT_EMAIL'] ||
102
+ "support@#{Rails.application.class.module_parent.name.downcase}.com"
81
103
 
82
104
  # Notification preferences
83
105
  @enable_email_notifications = true
84
106
  @enable_sms_notifications = false # Requires SMS provider configuration
85
107
 
86
- # API namespace and versioning configuration
87
- @namespace = nil # Default to no namespace for clean URLs like /login
88
- @version = nil # Default to no version for clean URLs like /login
108
+ # Tenancy configuration
109
+ # Controls whether multi-agency tenancy is enabled within organizations
110
+ @agency_tenancy = true # Enable agency-level tenancy by default
111
+
112
+ # API tenancy requirements (for PropelApi integration)
113
+ # Controls whether APIs require explicit tenancy context in requests
114
+ @require_organization_id = false # Auto-assign organization when missing (developer-friendly default)
115
+ @require_user_id = false # Auto-assign current user when missing (common workflow default)
116
+
117
+ # API namespace and versioning configuration
118
+ # Default to no namespace/version/auth_scope for clean URLs like /login, /me, /signup
119
+ @namespace = nil
120
+ @version = nil
121
+ @auth_scope = nil
89
122
  end
90
123
  end
91
124
  end
@@ -93,7 +126,13 @@ end
93
126
  # Configure PropelAuthentication with secure defaults
94
127
  PropelAuthentication.configure do |config|
95
128
  # JWT Configuration for API authentication
96
- config.jwt_secret = Rails.application.credentials.secret_key_base || ENV['SECRET_KEY_BASE'] || 'your-secret-key'
129
+ # Priority: Rails credentials -> ENV variables -> fallback (development/test only)
130
+ config.jwt_secret = Rails.application.credentials.jwt_secret ||
131
+ ENV['JWT_SECRET'] ||
132
+ Rails.application.credentials.secret_key_base ||
133
+ ENV['SECRET_KEY_BASE'] ||
134
+ (Rails.env.development? || Rails.env.test? ? 'development-secret-key' :
135
+ raise('JWT_SECRET must be set in production'))
97
136
  config.jwt_expiration = 24.hours
98
137
 
99
138
  # Password requirements
@@ -102,6 +141,25 @@ PropelAuthentication.configure do |config|
102
141
  # User registration settings
103
142
  config.allow_registration = true
104
143
 
144
+ # Multi-tenancy settings
145
+ # Controls whether agency-level tenancy is enabled within organizations
146
+ # When true: Organization -> Agency -> Resources structure
147
+ # When false: Organization -> Resources structure (simpler)
148
+ config.agency_tenancy = true
149
+
150
+ # API tenancy requirements (for PropelApi integration)
151
+ # Controls whether clients must provide explicit tenancy context in API requests
152
+
153
+ # Require explicit organization_id in API requests?
154
+ # false (recommended): Auto-assigns current user's organization_id when missing
155
+ # true (strict): Returns 422 error if organization_id not provided by client
156
+ config.require_organization_id = false
157
+
158
+ # Require explicit user_id in API requests?
159
+ # false (recommended): Auto-assigns current user as creator when missing
160
+ # true (admin mode): Returns 422 error if user_id not provided (for delegation scenarios)
161
+ config.require_user_id = false
162
+
105
163
  # Email confirmation (for future use)
106
164
  config.require_email_confirmation = false
107
165
 
@@ -122,9 +180,36 @@ PropelAuthentication.configure do |config|
122
180
  config.jwt_expiration = 2.hours
123
181
  end
124
182
 
125
- # URL structure configuration (set by generator)
183
+ # URL structure configuration (set by generator based on your choices)
184
+ # - Default: Clean URLs like /login, /me, /signup (no additional namespacing)
185
+ # - With PropelApi: Automatically adopts API namespace for consistency
186
+ # - Override: Set explicit values to customize URL structure
187
+ #
188
+ # Current configuration:
126
189
  config.namespace = <%= @auth_namespace ? "'#{@auth_namespace}'" : 'nil' %>
127
190
  config.version = <%= @auth_version ? "'#{@auth_version}'" : 'nil' %>
191
+ config.auth_scope = <%= @auth_scope ? "'#{@auth_scope}'" : 'nil' %>
192
+
193
+ # Examples for customization:
194
+ # For clean URLs (default):
195
+ # config.namespace = nil
196
+ # config.version = nil
197
+ # config.auth_scope = nil # → /login, /me → TokensController
198
+ #
199
+ # For organized URLs with auth scope:
200
+ # config.namespace = nil
201
+ # config.version = nil
202
+ # config.auth_scope = 'auth' # → /auth/login, /auth/me → Auth::TokensController
203
+ #
204
+ # For API-style URLs:
205
+ # config.namespace = 'api'
206
+ # config.version = 'v1'
207
+ # config.auth_scope = nil # → /api/v1/login, /api/v1/me → Api::V1::TokensController
208
+ #
209
+ # For API-style URLs with auth scope:
210
+ # config.namespace = 'api'
211
+ # config.version = 'v1'
212
+ # config.auth_scope = 'auth' # → /api/v1/auth/login → Api::V1::Auth::TokensController
128
213
  end
129
214
 
130
215
  # PropelAuthentication is now fully extracted to your application!
@@ -0,0 +1,55 @@
1
+ # JWT Authentication routes for <%= auth_route_prefix %>
2
+ <%- if @auth_namespace.present? || @auth_version.present? || @auth_scope.present? -%>
3
+ <%- namespace_parts = [] -%>
4
+ <%- namespace_parts << @auth_namespace if @auth_namespace.present? -%>
5
+ <%- namespace_parts << @auth_version if @auth_version.present? -%>
6
+ <%- namespace_parts.each_with_index do |part, index| -%>
7
+ <%= " " * index %>namespace :<%= part %> do
8
+ <%- end -%>
9
+ <%- if @auth_scope.present? -%>
10
+ <%= " " * namespace_parts.length %>namespace :<%= @auth_scope %> do
11
+ <%= " " * (namespace_parts.length + 1) %>post 'signup', to: 'signup#create', as: :signup
12
+ <%= " " * (namespace_parts.length + 1) %>post 'login', to: 'tokens#create', as: :login
13
+ <%= " " * (namespace_parts.length + 1) %>get 'me', to: 'tokens#show', as: :me
14
+ <%= " " * (namespace_parts.length + 1) %>delete 'logout', to: 'tokens#destroy', as: :logout
15
+ <%= " " * (namespace_parts.length + 1) %>post 'unlock', to: 'tokens#unlock', as: :unlock
16
+ <%= " " * (namespace_parts.length + 1) %>post 'reset', to: 'passwords#create', as: :reset
17
+ <%= " " * (namespace_parts.length + 1) %>get 'reset', to: 'passwords#show', as: :verify_reset
18
+ <%= " " * (namespace_parts.length + 1) %>patch 'reset', to: 'passwords#update', as: :update_reset
19
+ <%= " " * namespace_parts.length %>end
20
+ <%- else -%>
21
+ <%= " " * namespace_parts.length %>post 'signup', to: 'signup#create', as: :signup
22
+ <%= " " * namespace_parts.length %>post 'login', to: 'tokens#create', as: :login
23
+ <%= " " * namespace_parts.length %>get 'me', to: 'tokens#show', as: :me
24
+ <%= " " * namespace_parts.length %>delete 'logout', to: 'tokens#destroy', as: :logout
25
+ <%= " " * namespace_parts.length %>post 'unlock', to: 'tokens#unlock', as: :unlock
26
+ <%= " " * namespace_parts.length %>post 'reset', to: 'passwords#create', as: :reset
27
+ <%= " " * namespace_parts.length %>get 'reset', to: 'passwords#show', as: :verify_reset
28
+ <%= " " * namespace_parts.length %>patch 'reset', to: 'passwords#update', as: :update_reset
29
+ <%- end -%>
30
+ <%- namespace_parts.length.times do |index| -%>
31
+ <%= " " * (namespace_parts.length - 1 - index) %>end
32
+ <%- end -%>
33
+ <%- else -%>
34
+ <%- if @auth_scope.present? -%>
35
+ namespace :<%= @auth_scope %> do
36
+ post 'signup', to: 'signup#create', as: :signup
37
+ post 'login', to: 'tokens#create', as: :login
38
+ get 'me', to: 'tokens#show', as: :me
39
+ delete 'logout', to: 'tokens#destroy', as: :logout
40
+ post 'unlock', to: 'tokens#unlock', as: :unlock
41
+ post 'reset', to: 'passwords#create', as: :reset
42
+ get 'reset', to: 'passwords#show', as: :verify_reset
43
+ patch 'reset', to: 'passwords#update', as: :update_reset
44
+ end
45
+ <%- else -%>
46
+ post 'signup', to: 'signup#create', as: :signup
47
+ post 'login', to: 'tokens#create', as: :login
48
+ get 'me', to: 'tokens#show', as: :me
49
+ delete 'logout', to: 'tokens#destroy', as: :logout
50
+ post 'unlock', to: 'tokens#unlock', as: :unlock
51
+ post 'reset', to: 'passwords#create', as: :reset
52
+ get 'reset', to: 'passwords#show', as: :verify_reset
53
+ patch 'reset', to: 'passwords#update', as: :update_reset
54
+ <%- end -%>
55
+ <%- end -%>
@@ -64,17 +64,17 @@ class AuthNotificationService
64
64
  private
65
65
 
66
66
  def build_password_reset_url(token)
67
- base_url = PropelAuth.configuration.frontend_url || default_frontend_url
67
+ base_url = PropelAuthentication.configuration.frontend_url || default_frontend_url
68
68
  "#{base_url}/reset-password?token=#{token}"
69
69
  end
70
70
 
71
71
  def build_account_unlock_url(token)
72
- base_url = PropelAuth.configuration.frontend_url || default_frontend_url
72
+ base_url = PropelAuthentication.configuration.frontend_url || default_frontend_url
73
73
  "#{base_url}/unlock-account?token=#{token}"
74
74
  end
75
75
 
76
76
  def build_invitation_url(token)
77
- base_url = PropelAuth.configuration.frontend_url || default_frontend_url
77
+ base_url = PropelAuthentication.configuration.frontend_url || default_frontend_url
78
78
  "#{base_url}/accept-invitation?token=#{token}"
79
79
  end
80
80