action_auth 1.8.9 → 2.0.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -0
  3. data/README.md +485 -36
  4. data/Rakefile +0 -2
  5. data/app/assets/javascripts/action_auth/application.js +72 -71
  6. data/app/assets/stylesheets/action_auth/application.css +38 -1
  7. data/app/controllers/action_auth/application_controller.rb +122 -1
  8. data/app/controllers/action_auth/connected_accounts_controller.rb +16 -0
  9. data/app/controllers/action_auth/identity/email_verifications_controller.rb +36 -5
  10. data/app/controllers/action_auth/identity/emails_controller.rb +16 -4
  11. data/app/controllers/action_auth/identity/password_resets_controller.rb +20 -22
  12. data/app/controllers/action_auth/magics/requests_controller.rb +27 -6
  13. data/app/controllers/action_auth/magics/sign_ins_controller.rb +22 -7
  14. data/app/controllers/action_auth/passwords_controller.rb +7 -18
  15. data/app/controllers/action_auth/provider_callbacks_controller.rb +147 -0
  16. data/app/controllers/action_auth/registrations_controller.rb +7 -25
  17. data/app/controllers/action_auth/sessions/passkeys_controller.rb +17 -9
  18. data/app/controllers/action_auth/sessions_controller.rb +27 -25
  19. data/app/controllers/action_auth/sms_auths/requests_controller.rb +40 -14
  20. data/app/controllers/action_auth/sms_auths/sign_ins_controller.rb +40 -15
  21. data/app/controllers/action_auth/users_controller.rb +35 -1
  22. data/app/controllers/action_auth/webauthn_credential_authentications_controller.rb +36 -51
  23. data/app/controllers/action_auth/webauthn_credentials_controller.rb +63 -51
  24. data/app/helpers/action_auth/application_helper.rb +83 -0
  25. data/app/mailers/action_auth/application_mailer.rb +1 -1
  26. data/app/mailers/action_auth/user_mailer.rb +7 -0
  27. data/app/models/action_auth/connected_account.rb +18 -0
  28. data/app/models/action_auth/session.rb +9 -1
  29. data/app/models/action_auth/user.rb +197 -4
  30. data/app/models/action_auth/webauthn_credential.rb +2 -0
  31. data/app/views/action_auth/identity/email_verifications/show.html.erb +23 -0
  32. data/app/views/action_auth/identity/emails/edit.html.erb +41 -42
  33. data/app/views/action_auth/identity/password_resets/edit.html.erb +10 -11
  34. data/app/views/action_auth/identity/password_resets/new.html.erb +4 -9
  35. data/app/views/action_auth/magics/requests/new.html.erb +4 -14
  36. data/app/views/action_auth/magics/sign_ins/show.html.erb +18 -0
  37. data/app/views/action_auth/passwords/edit.html.erb +9 -21
  38. data/app/views/action_auth/registrations/new.html.erb +5 -27
  39. data/app/views/action_auth/sessions/_account.html.erb +30 -0
  40. data/app/views/action_auth/sessions/_connected_accounts.html.erb +32 -0
  41. data/app/views/action_auth/sessions/_security_keys.html.erb +32 -0
  42. data/app/views/action_auth/sessions/_sessions.html.erb +28 -0
  43. data/app/views/action_auth/sessions/index.html.erb +10 -62
  44. data/app/views/action_auth/sessions/new.html.erb +3 -21
  45. data/app/views/action_auth/sessions/passkeys/new.html.erb +10 -4
  46. data/app/views/action_auth/shared/_errors.html.erb +10 -0
  47. data/app/views/action_auth/shared/_flash.html.erb +11 -0
  48. data/app/views/action_auth/shared/_links.html.erb +3 -0
  49. data/app/views/action_auth/shared/_oauth_buttons.html.erb +5 -0
  50. data/app/views/action_auth/shared/_webauthn_error.html.erb +1 -0
  51. data/app/views/action_auth/sms_auths/requests/new.html.erb +4 -14
  52. data/app/views/action_auth/sms_auths/sign_ins/show.html.erb +6 -2
  53. data/app/views/action_auth/user_mailer/account_locked.html.erb +17 -0
  54. data/app/views/action_auth/user_mailer/account_locked.text.erb +12 -0
  55. data/app/views/action_auth/user_mailer/email_verification.html.erb +5 -0
  56. data/app/views/action_auth/user_mailer/email_verification.text.erb +3 -0
  57. data/app/views/action_auth/user_mailer/magic_link.html.erb +4 -0
  58. data/app/views/action_auth/user_mailer/magic_link.text.erb +5 -0
  59. data/app/views/action_auth/user_mailer/password_reset.text.erb +1 -1
  60. data/app/views/action_auth/webauthn_credential_authentications/new.html.erb +9 -3
  61. data/app/views/action_auth/webauthn_credentials/new.html.erb +10 -3
  62. data/app/views/layouts/action_auth/application.html.erb +2 -7
  63. data/config/routes.rb +13 -3
  64. data/db/migrate/20261105120000_add_magic_link_used_at_to_users.rb +5 -0
  65. data/db/migrate/20261105130000_add_account_lockout_and_suspension_to_users.rb +7 -0
  66. data/db/migrate/20261105130100_create_action_auth_connected_accounts.rb +19 -0
  67. data/lib/action_auth/configuration.rb +96 -0
  68. data/lib/action_auth/controllers/helpers.rb +44 -11
  69. data/lib/action_auth/engine.rb +27 -1
  70. data/lib/action_auth/routing/helpers.rb +8 -0
  71. data/lib/action_auth/version.rb +1 -1
  72. data/lib/generators/action_auth/views/USAGE +8 -0
  73. data/lib/generators/action_auth/views/views_generator.rb +59 -0
  74. data/lib/tasks/action_auth_tasks.rake +19 -1
  75. metadata +28 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 662322944520ce4f520b67eb2dfecf9e8868442a8a0e4cd0d3951cf416c14977
4
- data.tar.gz: 0a66bcfb184a82c4916ee38fea0829b3685f9da3ddde0dc27323b5590b32ab58
3
+ metadata.gz: 7288928796e68a851288a1a39e8ae0cf91b1b780f67e71239defe4f06a046958
4
+ data.tar.gz: d834a7faff6b75ec498f1857c55e0b1f7a84946339be7d2d45fe27b08286768f
5
5
  SHA512:
6
- metadata.gz: def7e4ffb120c8b2ee9bb2c133e8f2e5fab0f85fdbd828eeeee50c31b2df0824047277831f2130ec37c78db70fab20253b39b63e82edd65a1809097d732d912a
7
- data.tar.gz: 1a9feeb2f33a721ca0c7846583084ee3a488d8d3bbcaed70da81eda68e684bcd02ced414140cf415e5346ac8941d219ec3b0ad1b7e418a8e0a205b8c11d4ec0f
6
+ metadata.gz: 061e97a12095940d5c9cedd9ceaa7a0a502d2dd9fab1c85f3a69d691f1ab9046f03b1e79f7abc3538fbf696ecbcc8433a0b9c094d5ddd28edac1f1aaaa512ef5
7
+ data.tar.gz: b7783924fc814c5df66d154d765fcb85e1ee49ed4ea5cd9860f5d17e8f2c81cd03dc48bd3556fe6d00c0e3f57c747ef6c5fd1b9c6396733412d53f327e19618b
data/CHANGELOG.md ADDED
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ This release fixes serious security problems. **Everyone should upgrade**, especially if the `webauthn` gem is installed or SMS authentication is turned on. Please read [Upgrading](#upgrading) first, because a few changes can affect your app or its tests.
6
+
7
+ ### Security
8
+
9
+ - **Passkey sign in didn't verify anything.** `Sessions::PasskeysController#create` looked up the credential ID and signed in as its owner without checking the signature or the challenge. Anyone who had a credential ID could sign in as that user. It's on by default when the `webauthn` gem is installed. It now verifies the signature, the challenge, the sign count and that the user was verified on the device.
10
+ - **SMS sign in could be used to take over other accounts.** A request with no phone number matched the first account without one, which set a code on that account and let the code be used to sign in to it. A request could also send `sms_code[]=` with a list of codes to try them all at once. Both are fixed. SMS authentication is off by default.
11
+ - **Magic links and SMS codes skipped the security key.** Accounts with a security key now have to use it after either one, as they do after a password.
12
+ - **Magic links could be used more than once.** A link worked as many times as anyone liked for 20 minutes, so one found in a mailbox, a forwarded email or a log could still sign in. A link now works once, even if it's clicked twice at the same moment, and using one uses up any others sent to that account. Opening a link now shows a page with a "Sign in" button, so mail scanners that open links don't use them up. Links also stop working when the account's email changes. Before, a link sent to the old address could still sign in and mark the new address as verified.
13
+ - **Someone could keep access to an account set up with another person's email address.** Anyone could sign up with an address they didn't own, or change their own account to it. When the address's real owner first signed in with a magic link, the account was marked as verified, but the password and any open sessions still worked for whoever set it up. That first magic link now replaces the password, ends every session and removes connected OAuth accounts. Security keys stay, and are still asked for.
14
+ - **Opening the email verification link was enough to verify an address.** Anyone could sign up with someone else's address and their own password. If the address's owner clicked the link in the email they were sent, or a mail scanner opened it, the account was verified and that password worked. The link now opens a page that asks for the account's password, so verifying takes both the link and the password. Wrong passwords on that page count towards account lockout.
15
+ - **Password reset links kept working after an email change.** A link sent to the old address could still be used, including by someone who had moved their account to another person's address. Links are now tied to the address they were sent to.
16
+ - **Deleting an account didn't ask for the password.** Anyone with access to a signed in browser could delete the account. It now requires the current password, with guesses limited per session. An account created through SMS has no password it could know, so it has to have signed in with a new code in the last 10 minutes instead.
17
+ - **WebAuthn challenges could be reused.** They're now used up on the first attempt, which matters most for synced passkeys, since they always report a sign count of 0 and that check can't catch a replay.
18
+ - **Session cookies were inconsistent.** Magic link, SMS and passkey sign ins left out the `secure` flag and ignored `insert_cookie_domain`, and most other paths ignored `insert_cookie_domain` too. Every path now sets the cookie in the same way. It's also `secure` on any HTTPS request, not only in production.
19
+ - **The password reset form revealed who has an account.** It now gives the same answer for every address.
20
+ - **Rate limits added** to magic link requests, SMS requests and code guesses (per client and per phone number), passkey sign in, password reset and verification requests, and changing your email. Before this, a six digit SMS code could be guessed as fast as requests could be sent.
21
+ - **WebAuthn scripts no longer load on pages with password fields.** They come from a third-party CDN, so they now load only on the three screens that use them. Stimulus was also pinned to an exact version. It previously loaded whatever version was newest.
22
+ - SMS codes come from `SecureRandom`, and CSRF protection is now on whether or not the app's defaults turn it on.
23
+
24
+ ### Fixed
25
+
26
+ - A passkey registered through the normal flow often couldn't be used for passkey sign in, because registration didn't ask for a discoverable credential. Passkeys registered before this release may need to be registered again to be used without a password.
27
+ - Two step sign in couldn't work in an app whose `ApplicationController` requires sign in, because the WebAuthn controllers inherited from it by accident.
28
+ - Changing your email was impossible from the default form, which had no current password field. An unverified user can now also correct a mistyped address.
29
+ - Logging out another device also logged out the current one.
30
+ - Visiting the password screens, or submitting the email change form, while signed out raised an error instead of redirecting to sign in.
31
+ - Sign up, password change and password reset raised an error when the password was missing or Have I Been Pwned was unreachable. An outage now counts as "not pwned" and is logged.
32
+ - A blank new password could report success without changing anything when pwned checks were off.
33
+ - Invalid input on the magic link and SMS forms showed a generic error page. They now show the form with the problem.
34
+ - WebAuthn requests with a malformed body raised an error. They now get a 422.
35
+ - `edit_user_password_path` was declared as a view helper but didn't exist, and the `edit_password_path` documented in the README wasn't available in views. `edit_user_password_path` and `user_password_path` now work.
36
+ - `password_complexity_check = false` was ignored during sign up when pwned checks were on.
37
+ - The "Verify Email" link always showed "That email verification link is invalid".
38
+ - The magic link email had no plain text version, and the password reset one had HTML tags in its plain text version.
39
+ - WebAuthn failures now show a message on the page instead of doing nothing, and a security key nickname containing `&` or `#` is no longer cut off.
40
+ - WebAuthn works when the app's initializer never calls `ActionAuth.configure`.
41
+ - `pwned_enabled` and `webauthn_enabled` being set without the gem installed no longer raises an error.
42
+ - User agents over 255 characters no longer fail sign in on MySQL.
43
+ - `session_timeout = nil` turns off session expiry instead of raising an error.
44
+ - `bin/rails action_auth:install` added a second `mount` to an app whose existing one was written differently, such as at another path, which stopped the app from booting. It now leaves any existing mount alone.
45
+ - The gemspec said Rails 7.1 was supported, but `rate_limit` needs 7.2. It now says `>= 7.2`, and CI tests 7.2, 8.0 and 8.1.
46
+
47
+ ### Added
48
+
49
+ - **OAuth.** Sign in with Google, GitHub, Facebook, X or any other [OmniAuth](https://github.com/omniauth/omniauth) strategy. It's opt in, like WebAuthn: add the gems and at least one `config.oauth_provider`. People can connect and disconnect providers from the management screen. A matching email address never signs anyone in to an existing account. See [OAuth](README.md#oauth).
50
+ - **Account lockout, on by default.** 10 failed passwords or SMS codes in a row lock an account for 30 minutes, and its owner is sent an email. Magic links, passkeys, OAuth and existing sessions keep working. See [Account Lockout](README.md#account-lockout).
51
+ - **Account suspension, on by default, but only your app can suspend anyone.** `user.suspend_account!` blocks every way of signing in and signs them out everywhere. See [Account Suspension](README.md#account-suspension).
52
+ - `bin/rails generate action_auth:views` copies the views into your app, either all of them or just the groups you name, with `--layout` to include the layout.
53
+ - `config.layout` renders the screens in your app's layout. Your route helpers work there without a `main_app.` prefix.
54
+ - `config.parent_controller` makes ActionAuth's controllers inherit from your `ApplicationController`, so your helpers work. Its `authenticate_user!` filter is skipped where it has to be.
55
+ - `config.after_sign_in_path` and `config.after_sign_out_path`.
56
+ - Partials to override one piece at a time: `shared/_flash`, `shared/_errors`, `shared/_links`, `sessions/_sessions`, `sessions/_security_keys` and `sessions/_account`.
57
+ - The `action_auth_links` and `action_auth_webauthn_tags` helpers, and the `action_auth_controller?` predicate.
58
+ - Links to change email, change password and delete the account on the management screen, which also marks the current device.
59
+ - Sprockets apps no longer need to add ActionAuth's assets to `manifest.js`.
60
+
61
+ ### Changed
62
+
63
+ - Password resets now work on unverified accounts, so an address's owner can always take over an account set up with it. Finishing one also verifies the address, ends every session and removes connected OAuth accounts. Security keys stay.
64
+ - After changing a password or an email you're sent to the management screen, since you're still signed in. With `verify_email_on_sign_in` off, signing up goes to `after_sign_in_path`.
65
+ - A session is only written to once a minute at most, not on every request.
66
+ - The redirects after `PATCH` and `DELETE` requests use `303 See Other`, which Turbo needs.
67
+
68
+ ### Upgrading
69
+
70
+ See [Upgrading](README.md#upgrading) in the README for the commands to run and the settings you can add.
71
+
72
+ - **There are new migrations.** Run this, or magic link requests will raise an error telling you to:
73
+
74
+ ```bash
75
+ bin/rails action_auth:install:migrations
76
+ bin/rails db:migrate
77
+ ```
78
+
79
+ Nothing else breaks while they're waiting to run, such as during a deploy. Lockout and suspension just do nothing until then, and `suspend_account!` raises an error instead of appearing to work.
80
+ - **Accounts now lock after too many failed sign ins.** Set `config.account_lockout_enabled = false` to turn this off, including if your tests fail a sign in 10 times for one user. The email it sends uses the same mail setup as your other ActionAuth emails. If it can't be sent, the problem is logged and sign in carries on.
81
+
82
+ - **Your delete account button needs a password field.** If you used the `button_to` from the README, it will now send people to the management screen with a message instead of deleting the account. That screen has a working form, so you can link to it instead, or see [Account Deletion](README.md#account-deletion) for a form to use.
83
+ - **Magic links now take a second click**, on the "Sign in" button. Links in emails sent before you upgrade stop working, so those people will need to request a new one. If you test the flow, `POST` the token to `magics_sign_ins_path` instead of opening the link. To restyle the new page, override `magics/sign_ins/show`.
84
+ - **Verifying an email now asks for the password.** The link opens a page with a password field, and submitting it verifies the address. Links already sent keep working, and go to the new page. If your tests open the link and expect the address to be verified, `PATCH` the token and password instead: `patch identity_email_verification_path, params: { sid: token, password: password }`. To restyle the new page, override `identity/email_verifications/show`.
85
+ - **Password reset links sent before you upgrade stop working**, because they're now tied to the address. They only last 20 minutes, so people can simply request another.
86
+ - **The first magic link on an unverified account replaces its password**, signs it out everywhere else and removes its connected OAuth accounts. The page with the "Sign in" button warns them first, and they can use Reset Password afterwards to choose a new password. There's no way to tell a stranger's password from the owner's at that point, so this applies to everyone. If you run with `verify_email_on_sign_in = false`, expect it to reach most of your password users, because nothing else verifies them. If your tests sign in with a magic link and then use the password, or count sessions, verify the user first.
87
+ - **Your test suite may need a change.** Password complexity, session timeouts and `same_site` used to be skipped when `Rails.env.test?`. If your tests create users with simple passwords, turn the check off in your test environment, or use stronger passwords:
88
+
89
+ ```ruby
90
+ config.password_complexity_check = !Rails.env.test?
91
+ ```
92
+
93
+ - **If you copied views into your app**, compare them with the new ones (`bin/rails generate action_auth:views` shows the differences):
94
+ - A copied `identity/emails/edit` needs a `password_challenge` field, or changing an email will keep failing.
95
+ - A copied `sessions/index` keeps working but won't get the new links.
96
+ - If you turn on OAuth, copied `sessions/new`, `registrations/new` and `sessions/index` views need the partials for it. See [Your own views](README.md#your-own-views).
97
+ - **If you copied the layout**, it keeps working. You can remove the WebAuthn tags and script from it, and add `<%= action_auth_webauthn_tags %>` to any custom WebAuthn view instead.
98
+ - **If you have your own WebAuthn JavaScript**, passkey sign in now answers with `{ "status": "ok", "redirect_url": "..." }` or a 422, rather than a redirect. Error bodies are now fixed messages rather than including the library's error.
99
+ - **Flash messages** are now displayed by the views. If your layout also displays them it won't show them twice, but the view's version is the one shown. To use your layout's styling, make `app/views/action_auth/shared/_flash.html.erb` an empty file.
100
+ - **Rate limits need a working cache.** They use `config.cache_store`, and `:null_store` turns them off. With several server processes, use a shared store such as Solid Cache or Redis.
101
+ - `GET /sessions/:id` was routed but had no action, and has been removed.
102
+ - CI no longer runs on Ruby 3.2, which has reached end of life.