plutonium 0.50.0 → 0.51.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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +85 -102
- data/.claude/skills/plutonium-app/SKILL.md +572 -0
- data/.claude/skills/plutonium-auth/SKILL.md +163 -300
- data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
- data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
- data/.claude/skills/plutonium-testing/SKILL.md +6 -5
- data/.claude/skills/plutonium-ui/SKILL.md +900 -0
- data/CHANGELOG.md +27 -2
- data/Rakefile +2 -1
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1009 -1214
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +52 -51
- data/app/assets/plutonium.min.js.map +3 -3
- data/docs/.vitepress/config.ts +37 -27
- data/docs/getting-started/index.md +22 -29
- data/docs/getting-started/installation.md +37 -80
- data/docs/getting-started/tutorial/index.md +4 -5
- data/docs/guides/adding-resources.md +66 -377
- data/docs/guides/authentication.md +94 -463
- data/docs/guides/authorization.md +124 -370
- data/docs/guides/creating-packages.md +94 -296
- data/docs/guides/custom-actions.md +121 -441
- data/docs/guides/index.md +22 -42
- data/docs/guides/multi-tenancy.md +116 -187
- data/docs/guides/nested-resources.md +103 -431
- data/docs/guides/search-filtering.md +123 -240
- data/docs/guides/testing.md +5 -4
- data/docs/guides/theming.md +157 -407
- data/docs/guides/troubleshooting.md +5 -3
- data/docs/guides/user-invites.md +106 -425
- data/docs/guides/user-profile.md +76 -243
- data/docs/index.md +1 -1
- data/docs/reference/app/generators.md +517 -0
- data/docs/reference/app/index.md +158 -0
- data/docs/reference/app/packages.md +146 -0
- data/docs/reference/app/portals.md +377 -0
- data/docs/reference/auth/accounts.md +230 -0
- data/docs/reference/auth/index.md +88 -0
- data/docs/reference/auth/profile.md +185 -0
- data/docs/reference/behavior/controllers.md +395 -0
- data/docs/reference/behavior/index.md +22 -0
- data/docs/reference/behavior/interactions.md +341 -0
- data/docs/reference/behavior/policies.md +417 -0
- data/docs/reference/index.md +56 -49
- data/docs/reference/resource/actions.md +423 -0
- data/docs/reference/resource/definition.md +508 -0
- data/docs/reference/resource/index.md +50 -0
- data/docs/reference/resource/model.md +348 -0
- data/docs/reference/resource/query.md +305 -0
- data/docs/reference/tenancy/entity-scoping.md +361 -0
- data/docs/reference/tenancy/index.md +36 -0
- data/docs/reference/tenancy/invites.md +393 -0
- data/docs/reference/tenancy/nested-resources.md +267 -0
- data/docs/reference/testing/index.md +287 -0
- data/docs/reference/ui/assets.md +400 -0
- data/docs/reference/ui/components.md +165 -0
- data/docs/reference/ui/displays.md +104 -0
- data/docs/reference/ui/forms.md +284 -0
- data/docs/reference/ui/index.md +30 -0
- data/docs/reference/ui/layouts.md +106 -0
- data/docs/reference/ui/pages.md +189 -0
- data/docs/reference/ui/tables.md +117 -0
- data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
- data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
- data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +0 -20
- data/lib/generators/pu/invites/install_generator.rb +1 -0
- data/lib/plutonium/definition/base.rb +1 -1
- data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
- data/lib/plutonium/helpers/turbo_helper.rb +11 -0
- data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
- data/lib/plutonium/resource/controller.rb +1 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
- data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
- data/lib/plutonium/resource/policy.rb +7 -0
- data/lib/plutonium/routing/mapper_extensions.rb +15 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/form/base.rb +6 -2
- data/lib/plutonium/ui/form/components/json.rb +58 -0
- data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
- data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
- data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
- data/lib/plutonium/ui/form/resource.rb +0 -4
- data/lib/plutonium/ui/grid/resource.rb +1 -1
- data/lib/plutonium/ui/layout/base.rb +1 -0
- data/lib/plutonium/ui/page/base.rb +0 -7
- data/lib/plutonium/ui/page/index.rb +4 -4
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +8 -0
- data/lib/tasks/release.rake +15 -1
- data/package.json +10 -10
- data/src/css/slim_select.css +4 -0
- data/src/js/controllers/slim_select_controller.js +61 -0
- data/src/js/turbo/turbo_actions.js +33 -0
- data/yarn.lock +553 -543
- metadata +44 -33
- data/.claude/skills/plutonium-assets/SKILL.md +0 -512
- data/.claude/skills/plutonium-controller/SKILL.md +0 -396
- data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
- data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
- data/.claude/skills/plutonium-forms/SKILL.md +0 -465
- data/.claude/skills/plutonium-installation/SKILL.md +0 -331
- data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
- data/.claude/skills/plutonium-invites/SKILL.md +0 -408
- data/.claude/skills/plutonium-model/SKILL.md +0 -440
- data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
- data/.claude/skills/plutonium-package/SKILL.md +0 -198
- data/.claude/skills/plutonium-policy/SKILL.md +0 -456
- data/.claude/skills/plutonium-portal/SKILL.md +0 -410
- data/.claude/skills/plutonium-views/SKILL.md +0 -651
- data/docs/reference/assets/index.md +0 -496
- data/docs/reference/controller/index.md +0 -412
- data/docs/reference/definition/actions.md +0 -462
- data/docs/reference/definition/fields.md +0 -383
- data/docs/reference/definition/index.md +0 -326
- data/docs/reference/definition/query.md +0 -351
- data/docs/reference/generators/index.md +0 -648
- data/docs/reference/interaction/index.md +0 -449
- data/docs/reference/model/features.md +0 -248
- data/docs/reference/model/index.md +0 -218
- data/docs/reference/policy/index.md +0 -456
- data/docs/reference/portal/index.md +0 -379
- data/docs/reference/views/forms.md +0 -411
- data/docs/reference/views/index.md +0 -544
|
@@ -1,559 +1,190 @@
|
|
|
1
1
|
# Authentication
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add Rodauth-based authentication to your Plutonium app.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Goal
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- User registration and login
|
|
9
|
-
- Password reset
|
|
10
|
-
- Email verification
|
|
11
|
-
- Multi-factor authentication (OTP, WebAuthn, SMS)
|
|
12
|
-
- Session management
|
|
13
|
-
- Account lockout
|
|
7
|
+
Authenticated users can sign up, log in, change passwords, and reset forgotten passwords. Pages in protected portals are gated.
|
|
14
8
|
|
|
15
|
-
##
|
|
9
|
+
## Quick path — basic user auth
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Install Rodauth
|
|
13
|
+
rails generate pu:rodauth:install
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
# 2. Create a user account type
|
|
16
|
+
rails generate pu:rodauth:account user
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
rails
|
|
23
|
-
|
|
18
|
+
# 3. Run migrations
|
|
19
|
+
rails db:migrate
|
|
20
|
+
|
|
21
|
+
# 4. Wire auth into a portal
|
|
22
|
+
# (when you run `pu:pkg:portal admin --auth=user`, this happens automatically)
|
|
24
23
|
```
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
Then mount your portal with the auth constraint:
|
|
27
26
|
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
```ruby
|
|
28
|
+
# config/routes.rb
|
|
29
|
+
Rails.application.routes.draw do
|
|
30
|
+
constraints Rodauth::Rails.authenticate(:user) do
|
|
31
|
+
mount AdminPortal::Engine, at: "/admin"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
31
34
|
```
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
- Required gems (`rodauth-rails`, `bcrypt`, `sequel-activerecord_connection`)
|
|
35
|
-
- `app/rodauth/rodauth_app.rb` - Main Roda app
|
|
36
|
-
- `app/rodauth/rodauth_plugin.rb` - Base plugin
|
|
37
|
-
- `app/controllers/rodauth_controller.rb` - Base controller
|
|
38
|
-
- `config/initializers/rodauth.rb` - Configuration
|
|
36
|
+
For accounts with more features, options, and admin patterns: see [Reference › Auth › Accounts](/reference/auth/accounts).
|
|
39
37
|
|
|
40
|
-
##
|
|
38
|
+
## Common variations
|
|
41
39
|
|
|
42
|
-
###
|
|
40
|
+
### Multi-factor auth (TOTP)
|
|
43
41
|
|
|
44
42
|
```bash
|
|
45
|
-
rails
|
|
46
|
-
rails db:migrate
|
|
43
|
+
rails generate pu:rodauth:account user --otp --recovery_codes
|
|
47
44
|
```
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
- `login`, `logout`, `remember`
|
|
51
|
-
- `create_account`, `verify_account`, `verify_account_grace_period`
|
|
52
|
-
- `reset_password`, `reset_password_notify`
|
|
53
|
-
- `change_login`, `verify_login_change`
|
|
54
|
-
- `change_password`, `change_password_notify`
|
|
55
|
-
- `case_insensitive_login`, `internal_request`
|
|
46
|
+
Then enable in the user-facing security section (see [User profile](./user-profile)).
|
|
56
47
|
|
|
57
|
-
###
|
|
48
|
+
### Hardened admin account
|
|
49
|
+
|
|
50
|
+
For an admin role with 2FA required, lockout, audit logging, and no public signup:
|
|
58
51
|
|
|
59
52
|
```bash
|
|
60
|
-
rails
|
|
61
|
-
rails db:migrate
|
|
53
|
+
rails generate pu:rodauth:admin admin
|
|
62
54
|
```
|
|
63
55
|
|
|
64
|
-
|
|
65
|
-
- Multi-phase login (email first, then password)
|
|
66
|
-
- TOTP two-factor authentication (required)
|
|
67
|
-
- Recovery codes
|
|
68
|
-
- Account lockout
|
|
69
|
-
- Active sessions tracking
|
|
70
|
-
- Audit logging
|
|
71
|
-
- **No public signup** - accounts created via rake task
|
|
72
|
-
|
|
73
|
-
### SaaS Account (Multi-tenant)
|
|
56
|
+
Create the first admin with the rake task:
|
|
74
57
|
|
|
75
58
|
```bash
|
|
76
|
-
rails
|
|
77
|
-
rails g pu:saas:setup --user Customer --entity Organization --roles=member,admin,owner
|
|
78
|
-
rails g pu:saas:setup --user Customer --entity Organization --no-allow-signup
|
|
79
|
-
rails db:migrate
|
|
59
|
+
rails rodauth_admin:create[admin@example.com,password123]
|
|
80
60
|
```
|
|
81
61
|
|
|
82
|
-
|
|
83
|
-
- Customer account model with Rodauth authentication
|
|
84
|
-
- Organization entity model with unique name
|
|
85
|
-
- OrganizationCustomer membership join model with role enum
|
|
62
|
+
### Multi-tenant SaaS — user + entity + membership in one shot
|
|
86
63
|
|
|
87
|
-
You can also run individual generators:
|
|
88
64
|
```bash
|
|
89
|
-
rails
|
|
90
|
-
rails g pu:saas:entity Organization # Just the entity model
|
|
91
|
-
rails g pu:saas:membership --user Customer --entity Organization # Just the membership
|
|
65
|
+
rails generate pu:saas:setup --user Customer --entity Organization
|
|
92
66
|
```
|
|
93
67
|
|
|
94
|
-
|
|
68
|
+
⚠️ This is a **meta-generator** — it also runs `pu:saas:portal`, `pu:profile:setup`, `pu:saas:welcome`, and `pu:invites:install`. Don't re-run those manually. See [Reference › Auth › Accounts › SaaS setup](/reference/auth/accounts#saas-setup-pu-saas-setup).
|
|
95
69
|
|
|
96
|
-
###
|
|
70
|
+
### API-only (JWT)
|
|
97
71
|
|
|
98
72
|
```bash
|
|
99
|
-
|
|
100
|
-
rails g pu:rodauth:account user --kitchen_sink
|
|
101
|
-
|
|
102
|
-
# Disable default features (explicit selection only)
|
|
103
|
-
rails g pu:rodauth:account user --no-defaults
|
|
104
|
-
|
|
105
|
-
# Enable specific features
|
|
106
|
-
rails g pu:rodauth:account user --otp --recovery_codes --lockout
|
|
107
|
-
|
|
108
|
-
# Skip email setup
|
|
109
|
-
rails g pu:rodauth:account user --no-mails
|
|
110
|
-
|
|
111
|
-
# API-only mode (JWT, no sessions)
|
|
112
|
-
rails g pu:rodauth:account user --api_only --jwt --jwt_refresh
|
|
113
|
-
|
|
114
|
-
# Use Argon2 instead of bcrypt
|
|
115
|
-
rails g pu:rodauth:account user --argon2
|
|
116
|
-
|
|
117
|
-
# Mark as primary account (no URL prefix)
|
|
118
|
-
rails g pu:rodauth:account user --primary
|
|
73
|
+
rails generate pu:rodauth:account api_user --api_only --jwt --jwt_refresh
|
|
119
74
|
```
|
|
120
75
|
|
|
121
|
-
### Available Features
|
|
122
|
-
|
|
123
|
-
| Feature | Description |
|
|
124
|
-
|---------|-------------|
|
|
125
|
-
| `login` | Basic login/logout |
|
|
126
|
-
| `create_account` | User registration |
|
|
127
|
-
| `verify_account` | Email verification |
|
|
128
|
-
| `reset_password` | Password reset via email |
|
|
129
|
-
| `change_password` | Change password when logged in |
|
|
130
|
-
| `change_login` | Change email address |
|
|
131
|
-
| `verify_login_change` | Verify email change |
|
|
132
|
-
| `remember` | "Remember me" functionality |
|
|
133
|
-
| `otp` | TOTP two-factor authentication |
|
|
134
|
-
| `sms_codes` | SMS-based 2FA |
|
|
135
|
-
| `recovery_codes` | Backup codes for 2FA |
|
|
136
|
-
| `webauthn` | WebAuthn/passkey authentication |
|
|
137
|
-
| `lockout` | Lock account after failed attempts |
|
|
138
|
-
| `active_sessions` | Track/manage active sessions |
|
|
139
|
-
| `audit_logging` | Log authentication events |
|
|
140
|
-
| `email_auth` | Passwordless email login |
|
|
141
|
-
| `jwt` | JWT token authentication |
|
|
142
|
-
| `jwt_refresh` | JWT refresh tokens |
|
|
143
|
-
| `close_account` | Allow account deletion |
|
|
144
|
-
|
|
145
|
-
## Generated Files
|
|
146
|
-
|
|
147
76
|
```
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
├── mailers/rodauth/
|
|
152
|
-
│ └── user_mailer.rb # Account-specific mailer
|
|
153
|
-
├── models/
|
|
154
|
-
│ └── user.rb # Account model
|
|
155
|
-
├── rodauth/
|
|
156
|
-
│ ├── rodauth_app.rb # Main Roda app
|
|
157
|
-
│ ├── rodauth_plugin.rb # Base plugin
|
|
158
|
-
│ └── user_rodauth_plugin.rb # Account-specific config
|
|
159
|
-
├── policies/
|
|
160
|
-
│ └── user_policy.rb # Account policy
|
|
161
|
-
├── definitions/
|
|
162
|
-
│ └── user_definition.rb # Account definition
|
|
163
|
-
└── views/rodauth/
|
|
164
|
-
└── user_mailer/ # Email templates
|
|
165
|
-
db/migrate/
|
|
166
|
-
└── xxx_create_users.rb # Account table migration
|
|
77
|
+
POST /api_users/login
|
|
78
|
+
{"login": "user@example.com", "password": "secret"}
|
|
79
|
+
# → {"access_token": "...", "refresh_token": "..."}
|
|
167
80
|
```
|
|
168
81
|
|
|
169
|
-
## Connecting
|
|
82
|
+
## Connecting a portal to an account type
|
|
170
83
|
|
|
171
|
-
|
|
84
|
+
If you create the portal with `--auth=`, it's wired automatically:
|
|
172
85
|
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
class ResourceController < PlutoniumController
|
|
176
|
-
include Plutonium::Resource::Controller
|
|
177
|
-
include Plutonium::Auth::Rodauth(:user)
|
|
178
|
-
end
|
|
86
|
+
```bash
|
|
87
|
+
rails generate pu:pkg:portal customer --auth=user
|
|
179
88
|
```
|
|
180
89
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| Method | Description |
|
|
184
|
-
|--------|-------------|
|
|
185
|
-
| `current_user` | The authenticated account |
|
|
186
|
-
| `logout_url` | URL to logout |
|
|
187
|
-
| `rodauth` | Access to Rodauth instance |
|
|
188
|
-
|
|
189
|
-
### Portal Configuration
|
|
190
|
-
|
|
191
|
-
For portals, include the auth module in the controller concern:
|
|
90
|
+
Manually, edit the portal's controller concern:
|
|
192
91
|
|
|
193
92
|
```ruby
|
|
194
|
-
# packages/
|
|
195
|
-
module
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
include Plutonium::Portal::Controller
|
|
200
|
-
include Plutonium::Auth::Rodauth(:admin)
|
|
201
|
-
end
|
|
202
|
-
end
|
|
93
|
+
# packages/customer_portal/app/controllers/customer_portal/concerns/controller.rb
|
|
94
|
+
module CustomerPortal::Concerns::Controller
|
|
95
|
+
extend ActiveSupport::Concern
|
|
96
|
+
include Plutonium::Portal::Controller
|
|
97
|
+
include Plutonium::Auth::Rodauth(:user)
|
|
203
98
|
end
|
|
204
99
|
```
|
|
205
100
|
|
|
206
|
-
|
|
101
|
+
Multiple account types — different portals use different Rodauth instances:
|
|
207
102
|
|
|
208
103
|
```ruby
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
@user_posts = current_user.posts
|
|
212
|
-
end
|
|
104
|
+
# Admin portal
|
|
105
|
+
include Plutonium::Auth::Rodauth(:admin)
|
|
213
106
|
|
|
214
|
-
#
|
|
215
|
-
|
|
216
|
-
Welcome, <%= current_user.email %>
|
|
217
|
-
<% end %>
|
|
107
|
+
# Customer portal
|
|
108
|
+
include Plutonium::Auth::Rodauth(:user)
|
|
218
109
|
```
|
|
219
110
|
|
|
220
|
-
|
|
111
|
+
See [Reference › App › Portals](/reference/app/portals#controller-concern-auth).
|
|
221
112
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
```ruby
|
|
225
|
-
# app/rodauth/user_rodauth_plugin.rb
|
|
226
|
-
class UserRodauthPlugin < RodauthPlugin
|
|
227
|
-
configure do
|
|
228
|
-
# Features enabled for this account
|
|
229
|
-
enable :login, :logout, :remember, :create_account, ...
|
|
113
|
+
## Customizing the auth flow
|
|
230
114
|
|
|
231
|
-
|
|
232
|
-
prefix "/users"
|
|
115
|
+
All inside `app/rodauth/<name>_rodauth_plugin.rb`, in the `configure do` block:
|
|
233
116
|
|
|
234
|
-
|
|
235
|
-
account_password_hash_column :password_hash
|
|
236
|
-
|
|
237
|
-
# Controller for views
|
|
238
|
-
rails_controller { Rodauth::UserController }
|
|
239
|
-
|
|
240
|
-
# Model
|
|
241
|
-
rails_account_model { User }
|
|
242
|
-
|
|
243
|
-
# Redirects
|
|
244
|
-
login_redirect "/"
|
|
245
|
-
logout_redirect "/"
|
|
246
|
-
|
|
247
|
-
# Session configuration
|
|
248
|
-
session_key "_user_session"
|
|
249
|
-
remember_cookie_key "_user_remember"
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Custom Login Redirect
|
|
117
|
+
### Custom login redirect
|
|
255
118
|
|
|
256
119
|
```ruby
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
# Or dynamically
|
|
261
|
-
login_redirect do
|
|
262
|
-
if rails_account.admin?
|
|
263
|
-
"/admin"
|
|
264
|
-
else
|
|
265
|
-
"/dashboard"
|
|
266
|
-
end
|
|
267
|
-
end
|
|
120
|
+
login_redirect do
|
|
121
|
+
rails_account.admin? ? "/admin" : "/dashboard"
|
|
268
122
|
end
|
|
269
123
|
```
|
|
270
124
|
|
|
271
|
-
###
|
|
125
|
+
### After-create hook (e.g. create a profile)
|
|
272
126
|
|
|
273
127
|
```ruby
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
password_minimum_length 12
|
|
277
|
-
|
|
278
|
-
# Custom complexity
|
|
279
|
-
password_meets_requirements? do |password|
|
|
280
|
-
super(password) && password.match?(/\d/) && password.match?(/[^a-zA-Z\d]/)
|
|
281
|
-
end
|
|
128
|
+
after_create_account do
|
|
129
|
+
Profile.create!(account_id: account_id, name: param("name"))
|
|
282
130
|
end
|
|
283
131
|
```
|
|
284
132
|
|
|
285
|
-
###
|
|
133
|
+
### Password requirements
|
|
286
134
|
|
|
287
135
|
```ruby
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
136
|
+
password_minimum_length 12
|
|
137
|
+
|
|
138
|
+
password_meets_requirements? do |password|
|
|
139
|
+
super(password) && password.match?(/\d/) && password.match?(/[^a-zA-Z\d]/)
|
|
291
140
|
end
|
|
292
141
|
```
|
|
293
142
|
|
|
294
|
-
### Prevent
|
|
143
|
+
### Prevent public signup
|
|
295
144
|
|
|
296
145
|
```ruby
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
request.halt unless internal_request?
|
|
300
|
-
end
|
|
146
|
+
before_create_account_route do
|
|
147
|
+
request.halt unless internal_request?
|
|
301
148
|
end
|
|
302
149
|
```
|
|
303
150
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
Emails are sent via Action Mailer.
|
|
151
|
+
Full customization surface: [Reference › Auth › Accounts › Common customizations](/reference/auth/accounts#common-customizations).
|
|
307
152
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
```ruby
|
|
311
|
-
# Gemfile
|
|
312
|
-
gem "letter_opener", group: :development
|
|
313
|
-
|
|
314
|
-
# config/environments/development.rb
|
|
315
|
-
config.action_mailer.delivery_method = :letter_opener
|
|
316
|
-
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Production
|
|
153
|
+
## Email setup (production)
|
|
320
154
|
|
|
321
155
|
```ruby
|
|
322
156
|
# config/environments/production.rb
|
|
323
157
|
config.action_mailer.delivery_method = :smtp
|
|
324
158
|
config.action_mailer.smtp_settings = {
|
|
325
|
-
address:
|
|
326
|
-
port:
|
|
327
|
-
user_name: ENV[
|
|
328
|
-
password: ENV[
|
|
159
|
+
address: "smtp.example.com",
|
|
160
|
+
port: 587,
|
|
161
|
+
user_name: ENV["SMTP_USER"],
|
|
162
|
+
password: ENV["SMTP_PASSWORD"]
|
|
329
163
|
}
|
|
330
164
|
```
|
|
331
165
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
Override templates in `app/views/rodauth/user_mailer/`:
|
|
335
|
-
|
|
336
|
-
```erb
|
|
337
|
-
<%# app/views/rodauth/user_mailer/reset_password.text.erb %>
|
|
338
|
-
Hi <%= @account.email %>,
|
|
339
|
-
|
|
340
|
-
Someone requested a password reset for your account.
|
|
341
|
-
|
|
342
|
-
Reset your password: <%= @reset_password_url %>
|
|
343
|
-
|
|
344
|
-
If you didn't request this, ignore this email.
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
## Customizing Views
|
|
348
|
-
|
|
349
|
-
Generate views to customize:
|
|
350
|
-
|
|
351
|
-
```bash
|
|
352
|
-
# Generate views for specific features
|
|
353
|
-
rails g pu:rodauth:views user --features login create_account reset_password
|
|
354
|
-
|
|
355
|
-
# Generate all views
|
|
356
|
-
rails g pu:rodauth:views user --all
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
Views are copied to `app/views/rodauth/user/` and can be customized as standard ERB templates.
|
|
360
|
-
|
|
361
|
-
## Multiple Account Types
|
|
362
|
-
|
|
363
|
-
### Different Portals, Different Accounts
|
|
364
|
-
|
|
365
|
-
```ruby
|
|
366
|
-
# packages/admin_portal/app/controllers/admin_portal/concerns/controller.rb
|
|
367
|
-
module AdminPortal
|
|
368
|
-
module Concerns
|
|
369
|
-
module Controller
|
|
370
|
-
extend ActiveSupport::Concern
|
|
371
|
-
include Plutonium::Portal::Controller
|
|
372
|
-
include Plutonium::Auth::Rodauth(:admin)
|
|
373
|
-
end
|
|
374
|
-
end
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
# packages/customer_portal/app/controllers/customer_portal/concerns/controller.rb
|
|
378
|
-
module CustomerPortal
|
|
379
|
-
module Concerns
|
|
380
|
-
module Controller
|
|
381
|
-
extend ActiveSupport::Concern
|
|
382
|
-
include Plutonium::Portal::Controller
|
|
383
|
-
include Plutonium::Auth::Rodauth(:customer)
|
|
384
|
-
end
|
|
385
|
-
end
|
|
386
|
-
end
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Shared Account Type
|
|
390
|
-
|
|
391
|
-
Multiple portals can share an account type:
|
|
392
|
-
|
|
393
|
-
```ruby
|
|
394
|
-
# Both portals include the same auth module
|
|
395
|
-
include Plutonium::Auth::Rodauth(:user)
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
## Public Portals
|
|
399
|
-
|
|
400
|
-
For portals that don't require authentication, use `Plutonium::Auth::Public`:
|
|
401
|
-
|
|
402
|
-
```ruby
|
|
403
|
-
# packages/public_portal/app/controllers/public_portal/concerns/controller.rb
|
|
404
|
-
module PublicPortal
|
|
405
|
-
module Concerns
|
|
406
|
-
module Controller
|
|
407
|
-
extend ActiveSupport::Concern
|
|
408
|
-
include Plutonium::Portal::Controller
|
|
409
|
-
include Plutonium::Auth::Public
|
|
410
|
-
end
|
|
411
|
-
end
|
|
412
|
-
end
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
This provides a `current_user` method that returns `"Guest"`.
|
|
416
|
-
|
|
417
|
-
## Two-Factor Authentication
|
|
418
|
-
|
|
419
|
-
### Enable During Generation
|
|
420
|
-
|
|
421
|
-
```bash
|
|
422
|
-
rails g pu:rodauth:account user --otp --recovery_codes
|
|
423
|
-
```
|
|
166
|
+
Override mailer templates in `app/views/rodauth/<account>_mailer/`.
|
|
424
167
|
|
|
425
|
-
|
|
168
|
+
## Accessing the current user
|
|
426
169
|
|
|
427
170
|
```ruby
|
|
428
|
-
#
|
|
429
|
-
|
|
430
|
-
enable :otp, :recovery_codes
|
|
171
|
+
# Controllers / views
|
|
172
|
+
current_user
|
|
431
173
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
end
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
Note: The `pu:rodauth:admin` generator automatically enables OTP and recovery codes.
|
|
438
|
-
|
|
439
|
-
## Creating Accounts
|
|
440
|
-
|
|
441
|
-
### Admin Accounts
|
|
442
|
-
|
|
443
|
-
Admin accounts are created via rake task (web registration is disabled):
|
|
444
|
-
|
|
445
|
-
```bash
|
|
446
|
-
# Interactive prompt for email
|
|
447
|
-
rails rodauth:admin
|
|
448
|
-
|
|
449
|
-
# With EMAIL environment variable
|
|
450
|
-
EMAIL=admin@example.com rails rodauth:admin
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
The task name matches the account name (e.g., `rails rodauth:admin` for an account named `admin`).
|
|
454
|
-
|
|
455
|
-
### Programmatic Account Creation
|
|
456
|
-
|
|
457
|
-
For accounts with self-registration enabled, use internal requests:
|
|
458
|
-
|
|
459
|
-
```ruby
|
|
460
|
-
# Create account via internal request
|
|
461
|
-
RodauthApp.rodauth(:user).create_account(
|
|
462
|
-
login: "user@example.com",
|
|
463
|
-
password: "secure_password"
|
|
464
|
-
)
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
In seeds:
|
|
468
|
-
|
|
469
|
-
```ruby
|
|
470
|
-
# db/seeds.rb
|
|
471
|
-
RodauthApp.rodauth(:user).create_account(
|
|
472
|
-
login: "user@example.com",
|
|
473
|
-
password: "password123"
|
|
474
|
-
)
|
|
174
|
+
# Policies
|
|
175
|
+
user
|
|
475
176
|
```
|
|
476
177
|
|
|
477
|
-
##
|
|
178
|
+
## Common issues
|
|
478
179
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
rails g pu:rodauth:account api_user --api_only --jwt --jwt_refresh
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
This enables:
|
|
486
|
-
- JWT token authentication
|
|
487
|
-
- Refresh tokens
|
|
488
|
-
- No session/cookie handling
|
|
489
|
-
|
|
490
|
-
### Using JWT
|
|
491
|
-
|
|
492
|
-
```bash
|
|
493
|
-
# Login
|
|
494
|
-
curl -X POST http://localhost:3000/api_users/login \
|
|
495
|
-
-H "Content-Type: application/json" \
|
|
496
|
-
-d '{"login": "user@example.com", "password": "secret"}'
|
|
497
|
-
|
|
498
|
-
# Response includes tokens
|
|
499
|
-
{"access_token": "...", "refresh_token": "..."}
|
|
500
|
-
|
|
501
|
-
# Authenticated requests
|
|
502
|
-
curl http://localhost:3000/api/posts \
|
|
503
|
-
-H "Authorization: Bearer <access_token>"
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
## Troubleshooting
|
|
507
|
-
|
|
508
|
-
### Routes Not Working
|
|
509
|
-
|
|
510
|
-
Restart the server after installing Rodauth:
|
|
511
|
-
|
|
512
|
-
```bash
|
|
513
|
-
bin/rails restart
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
### Emails Not Sending
|
|
517
|
-
|
|
518
|
-
Check Action Mailer configuration:
|
|
519
|
-
|
|
520
|
-
```ruby
|
|
521
|
-
# Verify mailer config
|
|
522
|
-
Rails.application.config.action_mailer.delivery_method
|
|
523
|
-
Rails.application.config.action_mailer.default_url_options
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
Use letter_opener in development to view emails in browser.
|
|
527
|
-
|
|
528
|
-
### Session Issues
|
|
529
|
-
|
|
530
|
-
Clear session cookies in the browser, or for active_sessions feature:
|
|
531
|
-
|
|
532
|
-
```ruby
|
|
533
|
-
# In rails runner
|
|
534
|
-
User.find_by(email: "user@example.com").active_session_keys.delete_all
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
### Migration Issues
|
|
538
|
-
|
|
539
|
-
Ensure all migrations have run:
|
|
540
|
-
|
|
541
|
-
```bash
|
|
542
|
-
rails db:migrate:status
|
|
543
|
-
rails db:migrate
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
### Account Not Verified
|
|
547
|
-
|
|
548
|
-
For development, you can manually verify accounts:
|
|
549
|
-
|
|
550
|
-
```ruby
|
|
551
|
-
# In rails runner
|
|
552
|
-
user = User.find_by(email: "user@example.com")
|
|
553
|
-
user.update!(status: 2) # 2 = verified
|
|
554
|
-
```
|
|
180
|
+
- **"You need to set up Rodauth"** — run `pu:rodauth:install` first.
|
|
181
|
+
- **Portal redirects to login even though you're authenticated** — the portal mount constraint references a different Rodauth account than the portal's controller concern uses. Match them up.
|
|
182
|
+
- **Email confirmation never arrives in development** — Plutonium sets ActionMailer to `:test` by default. Check `tmp/letter_opener/` or your mail interceptor. In production, configure SMTP (see above).
|
|
555
183
|
|
|
556
184
|
## Related
|
|
557
185
|
|
|
558
|
-
- [
|
|
559
|
-
- [
|
|
186
|
+
- [Reference › Auth](/reference/auth/) — full auth surface
|
|
187
|
+
- [Authorization](./authorization) — controlling who can do what AFTER login
|
|
188
|
+
- [Multi-tenancy](./multi-tenancy) — entity scoping for SaaS apps
|
|
189
|
+
- [User invites](./user-invites) — invitation-based onboarding
|
|
190
|
+
- [User profile](./user-profile) — account-settings page
|