add_auth 0.2.2 → 0.4.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/README.md +64 -17
  4. data/ROADMAP.md +98 -43
  5. data/SECURITY.md +8 -0
  6. data/app/controllers/add_auth/accounts_controller.rb +146 -0
  7. data/app/controllers/add_auth/assets_controller.rb +2 -1
  8. data/app/controllers/add_auth/external_identities_controller.rb +56 -0
  9. data/app/controllers/add_auth/mobile_sessions_controller.rb +120 -0
  10. data/app/controllers/add_auth/provider_sign_ins_controller.rb +248 -0
  11. data/app/controllers/add_auth/reauthentications_controller.rb +5 -0
  12. data/app/helpers/add_auth/sign_ins_helper.rb +2 -1
  13. data/app/jobs/add_auth/account_delivery_job.rb +14 -0
  14. data/app/jobs/add_auth/account_request_job.rb +17 -0
  15. data/app/mailers/add_auth/account_mailer.rb +12 -0
  16. data/app/views/add_auth/account_mailer/link.text.erb +7 -0
  17. data/app/views/add_auth/accounts/_check_email.html.erb +5 -0
  18. data/app/views/add_auth/accounts/_delete.html.erb +6 -0
  19. data/app/views/add_auth/accounts/_email.html.erb +12 -0
  20. data/app/views/add_auth/accounts/_invalid_proof.html.erb +5 -0
  21. data/app/views/add_auth/accounts/_password.html.erb +12 -0
  22. data/app/views/add_auth/accounts/_proof.html.erb +16 -0
  23. data/app/views/add_auth/accounts/_register.html.erb +17 -0
  24. data/app/views/add_auth/accounts/_request.html.erb +17 -0
  25. data/app/views/add_auth/accounts/_unavailable.html.erb +3 -0
  26. data/app/views/add_auth/external_identities/_index.html.erb +22 -0
  27. data/app/views/add_auth/external_identities/_unavailable.html.erb +3 -0
  28. data/app/views/add_auth/passkeys/_controls.html.erb +3 -1
  29. data/app/views/add_auth/provider_sign_ins/_enrollment.html.erb +12 -0
  30. data/app/views/add_auth/provider_sign_ins/_failure.html.erb +3 -0
  31. data/app/views/add_auth/provider_sign_ins/_prepare.html.erb +7 -0
  32. data/app/views/add_auth/reauthentications/_form.html.erb +9 -0
  33. data/app/views/add_auth/sessions/_list.html.erb +1 -1
  34. data/app/views/add_auth/sessions/index.html.erb +7 -0
  35. data/app/views/add_auth/sign_ins/_confirmation.html.erb +1 -0
  36. data/app/views/add_auth/sign_ins/_form.html.erb +17 -0
  37. data/app/views/add_auth/sign_ins/_remember.html.erb +6 -0
  38. data/app/views/layouts/add_auth/authentication.html.erb +4 -3
  39. data/exe/add_auth-preflight +26 -0
  40. data/lib/add_auth/configuration.rb +78 -4
  41. data/lib/add_auth/core/access_policy.rb +11 -1
  42. data/lib/add_auth/core/account_lifecycle.rb +295 -0
  43. data/lib/add_auth/core/account_policy.rb +68 -0
  44. data/lib/add_auth/core/external_identities/evidence.rb +98 -0
  45. data/lib/add_auth/core/external_identities.rb +278 -0
  46. data/lib/add_auth/core/intake.rb +6 -1
  47. data/lib/add_auth/core/maintenance.rb +5 -3
  48. data/lib/add_auth/core/migration/account_adoption.rb +79 -0
  49. data/lib/add_auth/core/migration/readiness.rb +41 -0
  50. data/lib/add_auth/core/mobile_authentication.rb +35 -0
  51. data/lib/add_auth/core/mobile_handoffs.rb +83 -0
  52. data/lib/add_auth/core/mobile_profile.rb +46 -0
  53. data/lib/add_auth/core/mobile_response.rb +12 -0
  54. data/lib/add_auth/core/native_authentication.rb +77 -0
  55. data/lib/add_auth/core/passwords/bcrypt_support.rb +28 -0
  56. data/lib/add_auth/core/passwords/credential.rb +49 -0
  57. data/lib/add_auth/core/passwords/legacy_bcrypt.rb +43 -0
  58. data/lib/add_auth/core/passwords/lifecycle.rb +60 -0
  59. data/lib/add_auth/core/remaining_factors.rb +24 -0
  60. data/lib/add_auth/core/sessions.rb +102 -17
  61. data/lib/add_auth/core/step_up.rb +13 -1
  62. data/lib/add_auth/migration/static_inventory.rb +116 -0
  63. data/lib/add_auth/rails/account_password_entry.rb +24 -0
  64. data/lib/add_auth/rails/authentication.rb +1 -1
  65. data/lib/add_auth/rails/authentication_pages.rb +6 -1
  66. data/lib/add_auth/rails/doctor.rb +80 -6
  67. data/lib/add_auth/rails/ejection.rb +1 -1
  68. data/lib/add_auth/rails/engine.rb +6 -1
  69. data/lib/add_auth/rails/migration/account_store.rb +41 -0
  70. data/lib/add_auth/rails/migration/effective_inventory.rb +162 -0
  71. data/lib/add_auth/rails/migration/key_type.rb +23 -0
  72. data/lib/add_auth/rails/migration/source_bridge.rb +53 -0
  73. data/lib/add_auth/rails/password_adoption.rb +37 -0
  74. data/lib/add_auth/rails/provider_enrollment.rb +61 -0
  75. data/lib/add_auth/rails/provider_libraries/apple_form_post_correlation.rb +127 -0
  76. data/lib/add_auth/rails/provider_libraries/apple_jwks.rb +68 -0
  77. data/lib/add_auth/rails/provider_libraries/apple_native.rb +55 -0
  78. data/lib/add_auth/rails/provider_libraries/callback_route.rb +16 -0
  79. data/lib/add_auth/rails/provider_libraries/omniauth.rb +136 -0
  80. data/lib/add_auth/rails/provider_libraries/omniauth_correlation.rb +107 -0
  81. data/lib/add_auth/rails/provider_libraries/openid_connect.rb +62 -0
  82. data/lib/add_auth/rails/provider_libraries/request_protection.rb +43 -0
  83. data/lib/add_auth/rails/runtime.rb +179 -8
  84. data/lib/add_auth/rails/stores/account_credentials.rb +44 -0
  85. data/lib/add_auth/rails/stores/account_lock.rb +16 -7
  86. data/lib/add_auth/rails/stores/account_tokens.rb +104 -0
  87. data/lib/add_auth/rails/stores/authority.rb +28 -0
  88. data/lib/add_auth/rails/stores/commit_dispatch.rb +32 -0
  89. data/lib/add_auth/rails/stores/external_identities.rb +91 -0
  90. data/lib/add_auth/rails/stores/maintenance.rb +1 -1
  91. data/lib/add_auth/rails/stores/mobile_handoffs.rb +53 -0
  92. data/lib/add_auth/rails/stores/security_events.rb +3 -6
  93. data/lib/add_auth/rails/user_lifecycle.rb +8 -10
  94. data/lib/add_auth/result.rb +1 -0
  95. data/lib/add_auth/version.rb +1 -1
  96. data/lib/add_auth.rb +4 -0
  97. data/lib/generators/add_auth/accounts/accounts_generator.rb +56 -0
  98. data/lib/generators/add_auth/accounts/templates/add_add_auth_accounts.rb.tt +49 -0
  99. data/lib/generators/add_auth/accounts/templates/add_add_auth_provisioning.rb.tt +9 -0
  100. data/lib/generators/add_auth/accounts/templates/add_auth_account_token.rb +6 -0
  101. data/lib/generators/add_auth/accounts/templates/add_auth_address_claim.rb +6 -0
  102. data/lib/generators/add_auth/devise_accounts/devise_accounts_generator.rb +23 -0
  103. data/lib/generators/add_auth/devise_accounts/templates/prepare_add_auth_devise_accounts.rb.tt +17 -0
  104. data/lib/generators/add_auth/email_link/email_link_generator.rb +4 -1
  105. data/lib/generators/add_auth/email_link/templates/add_auth_challenge.js +1 -0
  106. data/lib/generators/add_auth/email_tokens/templates/create_add_auth_sign_in_tokens.rb.tt +3 -1
  107. data/lib/generators/add_auth/external_identities/external_identities_generator.rb +52 -0
  108. data/lib/generators/add_auth/external_identities/templates/add_add_auth_external_enrollment.rb.tt +5 -0
  109. data/lib/generators/add_auth/external_identities/templates/add_add_auth_external_identity_validity.rb.tt +5 -0
  110. data/lib/generators/add_auth/external_identities/templates/add_auth_external_identity.rb +4 -0
  111. data/lib/generators/add_auth/external_identities/templates/add_auth_external_transaction.rb +6 -0
  112. data/lib/generators/add_auth/external_identities/templates/create_add_auth_external_identities.rb.tt +50 -0
  113. data/lib/generators/add_auth/external_identities/templates/providers.rb +51 -0
  114. data/lib/generators/add_auth/feature_configuration.rb +21 -0
  115. data/lib/generators/add_auth/install/templates/initializer.rb +154 -46
  116. data/lib/generators/add_auth/javascript/templates/passkey.js +8 -5
  117. data/lib/generators/add_auth/mobile_sessions/mobile_sessions_generator.rb +48 -0
  118. data/lib/generators/add_auth/mobile_sessions/templates/add_add_auth_mobile_sessions.rb.tt +9 -0
  119. data/lib/generators/add_auth/mobile_sessions/templates/add_auth_mobile_handoff.rb +5 -0
  120. data/lib/generators/add_auth/mobile_sessions/templates/create_add_auth_mobile_handoffs.rb.tt +27 -0
  121. data/lib/generators/add_auth/mobile_sessions/templates/mobile.rb +8 -0
  122. data/lib/generators/add_auth/notifications/notifications_generator.rb +3 -4
  123. data/lib/generators/add_auth/notifications/templates/create_add_auth_security_events.rb.tt +3 -1
  124. data/lib/generators/add_auth/passkeys/passkeys_generator.rb +3 -21
  125. data/lib/generators/add_auth/passkeys/templates/add_add_auth_passkeys.rb.tt +7 -3
  126. data/lib/generators/add_auth/session_upgrade/session_upgrade_generator.rb +3 -1
  127. data/lib/generators/add_auth/step_up/step_up_generator.rb +5 -5
  128. data/lib/generators/add_auth/step_up/templates/add_add_auth_reauthentication.rb.tt +3 -1
  129. data/lib/tasks/add_auth.rake +18 -1
  130. metadata +101 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb4c1fc11f2a73f54a42c1bc5379f328d63e4e3cf4124f83ceaa87187ee04ed8
4
- data.tar.gz: bb0c1cf6c79e629cf3257a161c867dc0ea722596ccebe6a9b4dd609881e2be6a
3
+ metadata.gz: 1ed4ef07ed6308f6bb4b82de3e67552af3604472f38e7a379f1d9c863f76954e
4
+ data.tar.gz: 8189c0ddf517a9d06d291738e0d0d20bb383ffc9a7fdd085c9df33ad914312d4
5
5
  SHA512:
6
- metadata.gz: b9cf550a17e5736ac23be86f151571b6434949c137295c9e12489cead2ba5393d26bb2652c50469eb40775dcb85e8b937bbf1a0fbe5d2359b9d07172efa94fa3
7
- data.tar.gz: 76d3b435c7f3f6994f639a9d397cba861d1e25071a6143a121d596549c85e9736c5d4a0a54caff1bf7f05e77a8b168915e909002aef847aaaf41d406547de308
6
+ metadata.gz: 643216bb646e0ca6032f0b53f1928eb6da0b4efeffccadd8477fbb7776f6aa411c6ecf36bb4c3f785d94fa435320c04745a2565c70cef47e387f14fc534b805b
7
+ data.tar.gz: 48375790d81d186fa5cfd6f4f3ecf4672c46b2ec8602a3597c5a40b69562661212de41f5df8676a7080ef96ab36915af6dec798400aa97610b5b3c22d76be2d4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 — 2026-09-13
4
+
5
+ - Constrain JSON below 3.0 for the supported Rails decoding API; independently
6
+ installed hosts must be able to decrypt account and email proof deliveries.
7
+
8
+ - Add explicit optional password-signup confirmation with atomic provisioning and
9
+ initial session issuance. Unverified email stays unverified; required
10
+ confirmation remains the default.
11
+ - Add an independent opt-in for single-use password reset proofs to unconfirmed
12
+ password accounts, retaining account restrictions and authority revocation.
13
+ - Allow password-only lifecycle/step-up generation without enabling email sign-in.
14
+ - Expand the generated Rails initializer with executable defaults and commented
15
+ alternatives. Feature generators update their own setting in that file.
16
+
17
+ ## 0.3.0 — 2026-09-09
18
+
19
+ Optional account lifecycle, bounded Devise migration and native authentication.
20
+ The standard Rails-generator installation remains supported; enable only the
21
+ additional capabilities your host needs. The latest-0.x support policy continues.
22
+
23
+ - Add read-only Devise inventory, additive account preparation, explicit legacy
24
+ password adoption and retired-source write fences. Preserve account IDs and
25
+ reject ambiguous identifier/credential conversions.
26
+ - Add optional registration, confirmation/reconfirmation, reset, lock/unlock,
27
+ account changes and remembered sessions with shared Core policy and delivery.
28
+ - Integrate maintained provider libraries without owning their registrations,
29
+ protocol exchange, unrelated routes or provider API credentials. Preserve
30
+ identity ownership and require independent enrollment confirmation.
31
+ - Add opt-in finite native sessions, account-scoped revocation, S256-bound
32
+ single-use browser handoffs and optional nonce-bound Apple token verification.
33
+ Mobile duration settings are separate from browser settings; refresh-token
34
+ families are not part of this finite profile.
35
+ - Verify generated/ejected pages with Turbo, ordinary JavaScript without Turbo
36
+ and permitted no-JS. Add populated migration, checkpoint, compatibility
37
+ rollback, provider and native integration fixtures.
38
+
3
39
  ## 0.2.2 — 2026-09-08
4
40
 
5
41
  - Reject Solid Cache as an authentication rate-limit store: simultaneous first
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AddAuth
2
2
 
3
- **0.2.1:** password, email-link and passkey sign-in extend
3
+ Password, email-link and passkey sign-in extend
4
4
  Rails' generated authentication. AddAuth adds hardened sessions, verification
5
5
  for sensitive actions, passkey management and email recovery with an optional
6
6
  strict policy. Turnstile and reCAPTCHA integrations are available. See
@@ -9,27 +9,46 @@ strict policy. Turnstile and reCAPTCHA integrations are available. See
9
9
  AddAuth builds on top of Rails 8's built-in login system instead of
10
10
  replacing it -- it keeps using your existing `User` and `Session` models.
11
11
 
12
+ Introduced in 0.3.0: optional account lifecycle, bounded Devise migration,
13
+ provider-library integration and finite Android/iOS sessions. See the
14
+ [migration guide](https://addauthgem.com/migrating-from-devise/) before changing
15
+ an existing Devise app; do not run the Rails generator over its custom files.
16
+
12
17
  Read the [documentation](https://addauthgem.com) for setup guides, configuration
13
18
  reference and troubleshooting.
14
19
 
20
+ ## Configuration and optional signup confirmation
21
+
22
+ Version 0.4.0 adds optional password-signup confirmation and a
23
+ complete, commented [Rails initializer](lib/generators/add_auth/install/templates/initializer.rb).
24
+ Its default assignments are active; opt-in alternatives are commented out.
25
+ Required confirmation remains the default. See the [configuration guide](https://addauthgem.com/configuration/)
26
+ and [account setup](https://addauthgem.com/configuration-preview/), or the
27
+ [contributor rehearsal](CONTRIBUTING.md#optional-confirmation-integration-rehearsal)
28
+ for verification in a disposable host.
29
+
15
30
  ## Quickstart
16
31
 
32
+ These commands target 0.4.0 and are available after its
33
+ [RubyGems publication](https://rubygems.org/gems/add_auth/versions/0.4.0).
34
+ Check package availability before changing your app’s bundle.
35
+
17
36
  Install the `add_auth` gem from RubyGems through your Rails app’s Gemfile,
18
37
  then enable password and email-link sign-in. Use Ruby 3.3+ and Rails 8.0+
19
38
  with Active Record, and run the commands below from your Rails app’s root.
20
39
 
21
- **Release availability:** these commands require the published `0.2.1` package.
40
+ **Release availability:** these commands require the published `0.4.0` package.
22
41
  If it is not yet listed on [RubyGems](https://rubygems.org/gems/add_auth/versions),
23
42
  wait for publication; see [release status](https://addauthgem.com/release-status/).
24
43
  Start in your app’s development environment; use the deployment settings
25
44
  below before enabling sign-in for users.
26
45
 
27
46
  1. Keep `source "https://rubygems.org"` in your app’s Gemfile and add the
28
- 0.2 release line:
47
+ 0.4 release line:
29
48
 
30
49
  ```ruby
31
50
  # Gemfile
32
- gem "add_auth", "~> 0.2.1"
51
+ gem "add_auth", "~> 0.4.0"
33
52
  ```
34
53
 
35
54
  Bundler downloads the package from RubyGems and records the resolved
@@ -39,17 +58,26 @@ below before enabling sign-in for users.
39
58
  bundle install
40
59
  ```
41
60
 
42
- 2. If your app doesn't already have Rails' built-in login system, add it
43
- first:
61
+ 2. For a fresh app without an existing authentication system, generate
62
+ Rails' built-in login system:
44
63
 
45
64
  ```sh
46
65
  bin/rails generate authentication
47
66
  ```
48
67
 
49
- 3. Add AddAuth:
68
+ 3. Create your configuration file:
50
69
 
51
70
  ```sh
52
71
  bin/rails generate add_auth:install
72
+ ```
73
+
74
+ Open `config/initializers/add_auth.rb`. Defaults are active and optional
75
+ alternatives are commented out. This command enables no features and preserves
76
+ an existing initializer. Each feature section names its generator.
77
+
78
+ 4. Enable password and email-link sign-in:
79
+
80
+ ```sh
53
81
  bin/rails generate add_auth:email_link
54
82
  bin/rails db:migrate
55
83
  ```
@@ -58,7 +86,7 @@ below before enabling sign-in for users.
58
86
  in, plus a page where a signed-in user can see their active sessions and
59
87
  sign out of one remotely.
60
88
 
61
- 4. Set these values in `config/initializers/add_auth.rb`:
89
+ 5. Set these values in `config/initializers/add_auth.rb`:
62
90
 
63
91
  ```ruby
64
92
  AddAuth.configure do |config|
@@ -78,7 +106,7 @@ below before enabling sign-in for users.
78
106
  config.action_mailer.raise_delivery_errors = true
79
107
  ```
80
108
 
81
- 5. Create a trial account in `bin/rails console` using your own test address and
109
+ 6. Create a trial account in `bin/rails console` using your own test address and
82
110
  password, then start `bin/rails server` and visit `/sign-in`. Request a link
83
111
  for that account and open the message written under `tmp/mail`. Treat these
84
112
  local messages as credentials and delete them after testing. Raw sign-in
@@ -533,14 +561,33 @@ or recovery procedures work in production.
533
561
 
534
562
  ## Scope
535
563
 
536
- AddAuth 0.2 implements password/email/passkey sign-in, session
537
- adoption and management, purpose-bound verification, passkey recovery and
538
- strict account policy, durable security mail, and challenge adapters. Account
539
- provisioning, address confirmation and password reset remain host responsibilities.
540
- Release acceptance uses local real-database, generated-host, browser and
541
- SMTP/queue/cache tests. Hosts verify their own live providers and supported
542
- physical devices before deployment. Recovery codes and AddAuth-owned password
543
- policy remain deferred.
564
+ AddAuth implements password/email/passkey sign-in, session adoption and
565
+ management, purpose-bound verification, passkey recovery and strict account
566
+ policy, durable security mail and challenge adapters. Optional account lifecycle
567
+ adds registration, confirmation/reconfirmation, password reset, lock/unlock and
568
+ account changes. Hosts retain account eligibility, resource authorization and
569
+ provider registrations.
570
+
571
+ ## Optional integrations in 0.3.0
572
+
573
+ Enable bounded [Devise migration](https://addauthgem.com/migrating-from-devise/),
574
+ [account lifecycle](https://addauthgem.com/account-lifecycle/),
575
+ [provider sign-in](https://addauthgem.com/omniauth/) and
576
+ [Android/iOS sessions](https://addauthgem.com/mobile-authentication/) independently.
577
+ Provider protocol handling stays with maintained libraries. Native sessions use
578
+ finite expiry; the verified profile is 30 days absolute and 14 days idle, with
579
+ fresh sign-in after expiry. Refresh-token families are not included.
580
+
581
+ Migration evidence covers the documented Devise 5.0.4 source profiles, including
582
+ integer and UUID accounts; it does not establish universal Devise-module parity.
583
+ Preserve current credentials and revocations during cutover and compatible
584
+ rollback. Follow the migration guide before removing Devise or retiring columns.
585
+
586
+ Release acceptance uses local real-database, generated-host, browser,
587
+ SMTP/queue/cache and native-consumer tests. Hosts verify their own live providers
588
+ and supported physical devices before deployment. Recovery codes and multiple
589
+ realms remain deferred. See [ROADMAP.md](ROADMAP.md) for scope and progress, and
590
+ [CONTRIBUTING.md](CONTRIBUTING.md) for synthetic acceptance commands.
544
591
 
545
592
  ## Roadmap
546
593
 
data/ROADMAP.md CHANGED
@@ -4,15 +4,17 @@ AddAuth extends Rails 8's authentication generator with email-link sign-in,
4
4
  passkeys, purpose-bound reauthentication, hardened sessions and pluggable captcha.
5
5
  It reuses the host's accounts and Session model.
6
6
 
7
- **Reviewed 2026-09-07: 0.2.1 is published, and the planned v1 strategy features
8
- have shipped.** The next work makes adoption, upgrades and the future 1.0 support
9
- commitment clearer. Milestones below describe priorities, not promised dates or
10
- versions. See [release status](https://addauthgem.com/release-status/),
11
- [the changelog](CHANGELOG.md) and [the manual](https://addauthgem.com/).
7
+ **Reviewed 2026-09-13.** Check [published releases](https://github.com/taimoorq/add_auth/releases) for package availability and provenance. This checklist distinguishes verified implementation from publication.
8
+ It adds the bounded Devise migration, optional account lifecycle, provider-library
9
+ integration and finite native authentication below. Required PR and exact
10
+ release-commit CI/CodeQL, protected OIDC publication, downloaded-package comparison
11
+ and fresh-host installation passed. The matching migration and native guides are
12
+ published in [the manual](https://addauthgem.com/).
12
13
 
13
- **0.2.2 is prepared locally, not published.** It contains the counter-store
14
- correction and upgrade/operations coverage described below. The owner selected
15
- this patch before accepting the proposed 1.0 commitment.
14
+ The original strategy baseline shipped in 0.2.1; 0.2.2 supplied the counter-store
15
+ correction. The separate 1.0 support commitment remains under review. Milestones
16
+ describe priorities, not promised dates. See [release status](https://addauthgem.com/release-status/)
17
+ and [the changelog](CHANGELOG.md).
16
18
 
17
19
  An unchecked item is remaining work; deferred candidates need a scope decision
18
20
  before implementation. Checked features have passing relevant acceptance specs.
@@ -21,13 +23,18 @@ can describe tested local work; release/publication remains a separate gate. Det
21
23
  design and decision records remain in the companion private planning workspace;
22
24
  this public checklist is derived from its section 14 and stands on its own.
23
25
 
24
- ## Now — maintain 0.2 and learn from adoption
26
+ ## Now — maintain AddAuth and learn from adoption
25
27
 
26
- - [ ] **R1 · Resolve the outstanding dependency update.** Review
27
- [actions/checkout PR #1](https://github.com/taimoorq/add_auth/pull/1) against
28
- current master. Its old failed Ruby checks need current evidence; retain
29
- immutable action pins and all merge/release protections. Record a merge,
30
- replacement or reasoned deferral.
28
+ - [x] Optional password-signup confirmation, immediate transactional provisioning
29
+ and session issuance, explicit unconfirmed-reset policy, password-only lifecycle
30
+ and the complete commented Rails initializer. Local supported-matrix, stock/
31
+ adopted-host, UUID PostgreSQL and three-mode bundled/ejected acceptance passes.
32
+ Added in the 0.4.0 source; see [release availability and provenance](https://github.com/taimoorq/add_auth/releases).
33
+
34
+ - [x] **R1 · Resolve the outstanding dependency update.**
35
+ [PR #9](https://github.com/taimoorq/add_auth/pull/9) updates the immutable
36
+ checkout pin to 7.0.1 with current required checks passing. Old Dependabot
37
+ PR #1 is closed as superseded. All merge/release protections remain in force.
31
38
  - [x] **R1 · Complete the 2026-09-07 currency review.** Review Rails/Ruby support,
32
39
  authentication APIs, WebAuthn, advisories and the competitive landscape
33
40
  before the next version bump; repeat the standing review at least quarterly.
@@ -45,14 +52,14 @@ this public checklist is derived from its section 14 and stands on its own.
45
52
  quickstart, doctor and troubleshooting guidance with exact prerequisites,
46
53
  observable success and recovery steps. Keep the public manual tied to the
47
54
  published package. Upgrade, compatibility, cache and ejection guidance is
48
- prepared locally; build and browser checks pass. Publication is pending.
55
+ published for 0.2.2; build, browser and live deployment checks pass.
49
56
 
50
- The current master CI and CodeQL runs pass. The post-publication
57
+ The exact 0.2.2 release commit passed the full CI matrix and CodeQL. The post-publication
51
58
  passwordless-fixture isolation failure is fixed in
52
59
  [PR #8](https://github.com/taimoorq/add_auth/pull/8); it is not an open runtime
53
60
  defect or a reason by itself to republish 0.2.1.
54
61
 
55
- ## Next — prove upgrades, plan migrations and define the 1.0 contract
62
+ ## Roadmap — migration delivery and the future 1.0 contract
56
63
 
57
64
  - [x] **R3 · Rehearse an upgrade from published 0.2.1.** Start with a populated
58
65
  host, active sessions, passkeys, strict accounts, pending mail and customized
@@ -67,8 +74,8 @@ defect or a reason by itself to republish 0.2.1.
67
74
  whether Solid Cache meets atomic rate-limit increment/TTL requirements;
68
75
  document a tested separate store if needed. Publish only verified adapter
69
76
  support. Solid Queue 1.7.0 passes with a separate queue database. Solid
70
- Cache 1.0.10 loses concurrent first increments on PostgreSQL; the pending
71
- fix rejects it for abuse counters and documents a separate Redis store.
77
+ Cache 1.0.10 loses concurrent first increments on PostgreSQL; 0.2.2
78
+ rejects it for abuse counters and documents a separate Redis store.
72
79
  - [x] **R4 · Measure bounded maintenance and recovery.** Record workload,
73
80
  query counts, backlog drain and latency for session pages, outbox retries
74
81
  and cleanup. Use those measurements to explain batch sizing, retention,
@@ -79,51 +86,99 @@ defect or a reason by itself to republish 0.2.1.
79
86
  Core/host hooks and Results, routes, generators/ejection metadata, testing
80
87
  helpers and redacted events. Distinguish internal APIs and document
81
88
  compatibility, deprecation and migration rules. The current integration
82
- reference and proposed contract are prepared; owner acceptance of the
83
- 1.0 commitment remains the separate item below.
89
+ reference is published; the proposed 1.0 contract remains for owner
90
+ review under the separate item below.
84
91
  - [ ] **R5 · Set the 1.0 support policy.** Specify supported runtime/database
85
92
  combinations and security-supported release lines. Publish an upgrade
86
93
  guide and evidence-backed troubleshooting updates before the candidate
87
94
  freezes. The current latest-0.x policy remains in
88
95
  [SECURITY.md](SECURITY.md).
89
- - [ ] **R8 · Define Devise migration readiness.** Inventory source versions,
96
+ - [x] **R8 · Define Devise migration readiness.** Inventory source versions,
90
97
  modules, extensions and customizations; provide a read-only preflight with
91
98
  supported mappings, prerequisites and actionable blockers. Running Rails 8
92
99
  alone does not establish the authentication contracts AddAuth needs.
93
- - [ ] **R8 · Provide a path for Rails-aligned Devise apps.** Reuse verified
100
+ - [x] **R8 · Provide a path for Rails-aligned Devise apps.** Reuse verified
94
101
  account/session contracts and preserve host customizations while replacing
95
102
  remaining Devise authentication wiring. Prove password compatibility,
96
103
  account restrictions, session/token revocation and safe cutover/rollback.
97
- - [ ] **R8 · Provide a path for standard or customized Devise apps.** Guide an
104
+ - [x] **R8 · Provide a path for standard or customized Devise apps.** Guide an
98
105
  additive conversion from existing models, identifiers, password storage
99
106
  and controllers to the supported Rails contracts. Preserve account IDs and
100
107
  associations; resolve incompatible hashes, identifier collisions and
101
108
  unsupported module policies before cutover. Include older-runtime
102
109
  prerequisites and host-owned lifecycle replacements where needed.
103
- - [ ] **R8 · Rehearse and document both migration paths.** Test populated Devise
110
+ - [x] **R8 · Add optional account lifecycle support.** Registration,
111
+ confirmation/reconfirmation, password reset, lock/unlock and account
112
+ changes must preserve ownership, account policy and session revocation.
113
+ - [x] **R8 · Preserve provider sign-in through maintained libraries.** Add
114
+ optional OmniAuth integration with verified identity binding, explicit
115
+ linking/unlinking and usable recovery for provider-only accounts.
116
+ Preserve host-owned OAuth configuration, routes and other provider flows;
117
+ delegate protocol work to the selected libraries.
118
+ - [x] **R8 · Rehearse both migration paths and draft the guides.** Test populated Devise
104
119
  hosts before and after migration, including custom schema, peppered
105
120
  passwords, denied accounts, interrupted conversion and rollback. Verify
106
- supported database/runtime and Turbo/no-JS journeys, publish step-by-step
107
- guides and prove the destination works with Devise removed.
121
+ supported database/runtime, ordinary HTML with JavaScript and Turbo absent,
122
+ Turbo enhancement and permitted no-JS journeys. Draft step-by-step guides
123
+ and prove the destination works with Devise removed.
124
+ - [x] **R8 · Publish migration and native guides with the feature release.**
125
+ The 0.3.0 package, installation commands, HTTPS and deep links are verified;
126
+ the six guides now describe the published release.
127
+
128
+ Version 0.3.0 verifies preflight, optional account lifecycle and
129
+ provider-library integration across the supported Ruby/Rails matrix, including
130
+ ordinary HTML, Turbo and permitted no-JS journeys. These additions ship in 0.3.0. Migration/checkpoint/compatible-rollback tests and native client
131
+ acceptance are complete locally. The full adopter suite passes 1,330 examples
132
+ with zero failures and three existing empty scaffold examples pending. Six
133
+ published guides pass build, browser, keyboard and no-JS checks. The proposed
134
+ 1.0 commitment remains a separate gate.
135
+
136
+ ## Shipped in 0.3.0 — Android and iOS login
137
+
138
+ Mobile support ships as an optional integration in 0.3.0. These capabilities extend the
139
+ same account and session policies. The verified finite profile uses explicit
140
+ 30-day absolute and 14-day idle limits, with fresh sign-in after expiry. Renewable
141
+ refresh-token families remain a distinct optional capability, not implemented in this release.
142
+ The published 0.3.0 package and installed native-consumer contracts are verified.
143
+
144
+ - [x] **R8 · Password and provider login for native clients.** Return credentials
145
+ over authenticated HTTPS responses; preserve supported client contracts.
146
+ - [x] **R8 · Secure browser-to-app sign-in handoffs.** Short-lived, single-use
147
+ codes bound to state and a verifier, exact callback validation, and native
148
+ Apple nonce verification; no reusable bearer tokens in callback URLs.
149
+ - [x] **R8 · Device sessions and revocation.** Expiring, digested API credentials,
150
+ cookie/bearer separation, device listing, current-device and all-device
151
+ logout, and invalidation on password/account changes. Evaluate optional
152
+ renewal with rotation and replay detection as a distinct profile.
153
+ - [x] **R8 · Android/iOS migration acceptance.** Test secure client storage,
154
+ cancellation/restarts, account switching, expiry and concurrent handoffs,
155
+ plus supported old/new client combinations. Android/iOS consumers also
156
+ pass against an installed gem in an isolated Rails adopter over local HTTPS.
108
157
 
109
158
  Each change owns its tests and keeps intermediate releases usable. R3/R4 findings
110
159
  feed R5. A patch may address compatible corrections; another 0.x minor is possible
111
160
  if integration contracts change. A 0.3 release is not a prerequisite for 1.0.
112
- R8 is planned after the prepared 0.2.2 correction; its supported source profiles
113
- and delivery version will be decided during discovery, with public API impact
114
- resolved before R5 freezes. Devise migration support is not available yet.
161
+ R8 ships in 0.3.0 after the published 0.2.2 correction; its supported source profiles
162
+ are bounded to the verified Devise 5.0.4 fixtures. The selected delivery version is 0.3.0. Its pre-1.0 integration contracts
163
+ remain subject to review before R5 freezes. The bounded Devise migration guide is available for 0.3.0.
164
+
165
+ ## Delivery — 0.3.0 feature release, then the proposed 1.0 gate
115
166
 
116
- ## Delivery — 0.2.2 first, then the proposed 1.0 gate
167
+ - [x] Select 0.3.0 for the locally verified migration, lifecycle, provider and native scope.
168
+ - [x] Pass required PR and exact default-branch CI/CodeQL for 0.3.0.
169
+ - [x] Publish 0.3.0 through protected OIDC and verify the downloaded package and manual.
117
170
 
118
- - [ ] **R6 · Deliver the prepared 0.2.2 correction.** Commit and review the
119
- tested changes, pass current required GitHub checks, publish through
120
- protected OIDC, verify the registry package in a fresh host and publish
121
- the matching manual. Preserve the existing latest-0.x support policy.
171
+ - [x] **R6 · Deliver the 0.2.2 correction.**
172
+ [PR #10](https://github.com/taimoorq/add_auth/pull/10) passed all required
173
+ checks, followed by exact default-branch CI and protected OIDC publication.
174
+ The downloaded package checksum and all 121 files match the reviewed
175
+ candidate; fresh-host installation and the matching public manual pass.
176
+ The existing latest-0.x support policy remains in effect.
122
177
 
123
178
  - [ ] **R6 · Finish the adoption and compatibility review.** R1–R5 findings are
124
179
  completed or explicitly dispositioned, the supported API is accepted, and
125
180
  the published-package upgrade and documented operations profile pass.
126
- - [ ] **R6 · Verify the exact candidate.** Preserve the complete local
181
+ - [ ] **R6 · Verify the exact 1.0 candidate.** Preserve the complete local
127
182
  Ruby/Rails, SQLite/PostgreSQL, generated/ejected browser and operations
128
183
  acceptance; resolve release-blocking security and upgrade findings.
129
184
  Recheck dependency currency and pass required GitHub CI/CodeQL controls.
@@ -160,19 +215,19 @@ compatible extension could ship in 1.x after an explicit scope decision.
160
215
  before implementation; codes must not silently satisfy passkey-only purposes.
161
216
  - [ ] **F2 · Rails-native password hashing integration.** Recheck available Rails
162
217
  support and real adopter needs before adding an adapter or changing a default.
163
- - [ ] **F3 · Account lifecycle helpers.** Evaluate registration, confirmation,
164
- reset, lockout and password policy individually against repeated needs.
165
- Current host password integration already ships.
218
+ - [ ] **F3 · Account lifecycle helpers.** The bounded migration requirements
219
+ are promoted to R8 above; broader module parity remains outside this scope.
166
220
  - [ ] **F4 · Multiple realms.** Require a concrete identity/session-isolation need
167
221
  and migration design before adding models, cookies or routing APIs.
168
- - [ ] **F4 · API/token authentication.** Decide separately from browser sessions
169
- and realms; require a real non-browser client use case.
222
+ - [ ] **F4 · API/token authentication.** Android/iOS login shipped under R8
223
+ above; multiple realms and general API-key management remain separate.
170
224
  - [ ] **F5 · Standalone test helpers or challenge adapters.** Extract only if
171
225
  independent demand and maintenance capacity justify another package;
172
226
  retain one implementation per concern.
173
227
 
174
- Social/OIDC, SMS/TOTP and enterprise or cross-origin WebAuthn are outside the
175
- current roadmap. Account roles, invitations, authorization and email branding
228
+ Optional provider-library integration ships under R8 in 0.3.0. SMS/TOTP and enterprise
229
+ or cross-origin WebAuthn remain outside the current roadmap. Account roles,
230
+ invitations, authorization and email branding
176
231
  stay with the host.
177
232
 
178
233
  ## Shipped — 0.2.1 baseline
data/SECURITY.md CHANGED
@@ -34,6 +34,14 @@ mailing list, or social media) until a fix has been released.
34
34
  replacement handling.
35
35
  - Bounded session listing and authentication-history cleanup, including account
36
36
  scoping, eligibility rechecks and active-delivery-lease protection.
37
+ - Optional account lifecycle and migration boundaries: credential
38
+ profile selection, source-write fencing, address ownership, account proofs,
39
+ policy and revocation. These capabilities are introduced in 0.3.0; 0.4.0 adds
40
+ optional confirmation, atomic signup provisioning/session issuance and the
41
+ independent unconfirmed-password-reset policy.
42
+ - Optional provider identity binding and mobile sessions/handoffs, including
43
+ nonce/client/purpose binding, exact callbacks, single use and bearer/cookie
44
+ separation. Provider protocol cryptography remains library-owned.
37
45
  - The generated controllers and views this gem ships,
38
46
  including their CSRF, enumeration-safety, and Turbo-Stream behavior.
39
47
  - The GitHub Actions release pipeline (`.github/workflows/push_gem.yml`) and
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AddAuth
4
+ class AccountsController < ::ApplicationController
5
+ include Rails::AuthenticationPages
6
+
7
+ skip_before_action :require_authentication, raise: false
8
+ layout "add_auth/authentication"
9
+ helper AddAuth::SignInsHelper
10
+ before_action :require_feature
11
+ before_action :private_response
12
+ before_action :require_add_auth_authentication, only: %i[edit_email change_email edit_password change_password confirm_deletion destroy]
13
+ protect_from_forgery with: :exception
14
+ rescue_from AddAuth::Error, "ActiveJob::EnqueueError", with: :service_unavailable
15
+
16
+ def new = page("register")
17
+ def check_email = page("check_email")
18
+
19
+ def create
20
+ admitted = intake(:register)
21
+ return intake_failure(admitted) if admitted.is_a?(Symbol)
22
+ accounts = Rails::Runtime.accounts
23
+ result = accounts.register(identifier: admitted, password: params[:password], profile: registration_profile,
24
+ replacing: add_auth_replacement_session, **add_auth_session_hints)
25
+ if result.success?
26
+ if result.grant
27
+ destination = after_authentication_url
28
+ add_auth_accept(result.grant)
29
+ redirect_to destination, status: :see_other
30
+ else
31
+ redirect_to accounts.registration_next_path, status: :see_other
32
+ end
33
+ else
34
+ @error = "Check your email and choose a password that meets this application's requirements."
35
+ page("register", status: :unprocessable_entity)
36
+ end
37
+ end
38
+
39
+ def request_form
40
+ @purpose = purpose
41
+ page("request")
42
+ end
43
+
44
+ def request_proof
45
+ @purpose = purpose
46
+ admitted = intake(:account_request)
47
+ return intake_failure(admitted) if %i[challenge_rejected challenge_unavailable].include?(admitted)
48
+ Rails::Runtime.enqueue_account_request(identifier: admitted, purpose: @purpose) unless admitted.is_a?(Symbol)
49
+ redirect_to "/account/check-email", status: :see_other
50
+ end
51
+
52
+ def proof
53
+ @purpose = purpose
54
+ result = Rails::Runtime.accounts.preview(token: params[:token], purpose: @purpose)
55
+ page(result.success? ? "proof" : "invalid_proof")
56
+ end
57
+
58
+ def consume
59
+ @purpose = purpose
60
+ admitted = Rails::Runtime.intake.anonymous(ip: request.remote_ip, action: :account_consume)
61
+ return intake_failure(admitted) if admitted.is_a?(Symbol)
62
+ result = Rails::Runtime.accounts.consume(token: params[:token], purpose: @purpose, password: params[:password])
63
+ if result.success?
64
+ redirect_to "/sign-in", status: :see_other, notice: "Your account has been updated. Sign in to start a new session."
65
+ else
66
+ @error = "This request could not be completed. Check the password requirements or request a new link."
67
+ page("proof", status: :unprocessable_entity)
68
+ end
69
+ end
70
+
71
+ def edit_email = page("email")
72
+ def edit_password = page("password")
73
+ def confirm_deletion = page("delete")
74
+
75
+ def destroy
76
+ result = Rails::Runtime.accounts.delete_account(user: Current.user, session: Current.session)
77
+ if result.success?
78
+ terminate_session
79
+ redirect_to "/sign-in", status: :see_other, notice: "Your account was deleted."
80
+ elsif result.reason == :elevation_required
81
+ redirect_to "/reauthenticate?purpose=delete_account", status: :see_other
82
+ else
83
+ @error = "Your account could not be deleted. Contact support if this continues."
84
+ page("delete", status: :unprocessable_entity)
85
+ end
86
+ end
87
+
88
+ def change_password
89
+ result = Rails::Runtime.accounts.change_password(user: Current.user, session: Current.session, password: params[:password])
90
+ if result.success?
91
+ terminate_session
92
+ redirect_to "/sign-in", status: :see_other, notice: "Your password was changed. Sign in with your new password."
93
+ elsif result.reason == :elevation_required
94
+ redirect_to "/reauthenticate?purpose=change_password", status: :see_other
95
+ else
96
+ @error = "This password cannot be used. Check the password requirements and try again."
97
+ page("password", status: :unprocessable_entity)
98
+ end
99
+ end
100
+
101
+ def change_email
102
+ result = Rails::Runtime.accounts.change_email(user: Current.user, session: Current.session, identifier: params[:email_address])
103
+ if result.success?
104
+ redirect_to "/account/check-email", status: :see_other
105
+ elsif result.reason == :elevation_required
106
+ redirect_to "/reauthenticate?purpose=change_email", status: :see_other
107
+ else
108
+ @error = "This email address cannot be used. Check it and try again."
109
+ page("email", status: :unprocessable_entity)
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ # Host controllers may override with an explicit Rails parameter allowlist.
116
+ # The default does not forward arbitrary account/role parameters.
117
+ def registration_profile = {}
118
+
119
+ def require_feature
120
+ head :not_found unless AddAuth.configuration.lifecycle.enabled
121
+ end
122
+
123
+ def purpose
124
+ Core::AccountLifecycle::PURPOSES.include?(params[:purpose]) ? params[:purpose] : "confirm"
125
+ end
126
+
127
+ def intake(action)
128
+ Rails::Runtime.intake.call(identifier: params[:email_address], ip: request.remote_ip, action: action, challenge_token: challenge_token)
129
+ end
130
+
131
+ def authentication_partial(partial) = "add_auth/accounts/#{partial}"
132
+
133
+ def service_unavailable
134
+ @error = "Account requests are temporarily unavailable. Please try again shortly."
135
+ response.set_header("Retry-After", "60")
136
+ page("unavailable", status: :service_unavailable)
137
+ end
138
+
139
+ def intake_failure(reason)
140
+ @error = "This request could not be verified. Please try again shortly."
141
+ status = {rate_limited: :too_many_requests, challenge_unavailable: :service_unavailable}.fetch(reason, :unprocessable_entity)
142
+ response.set_header("Retry-After", "300") if %i[too_many_requests service_unavailable].include?(status)
143
+ page("unavailable", status: status)
144
+ end
145
+ end
146
+ end
@@ -20,7 +20,8 @@ module AddAuth
20
20
  end
21
21
 
22
22
  def boot
23
- source = 'if (!window.Turbo) await import("/add_auth/turbo.js"); await import("/add_auth/challenge.js");'
23
+ source = Rails::Runtime.config.turbo_enabled ? 'if (!window.Turbo) await import("/add_auth/turbo.js");' : ""
24
+ source += ' await import("/add_auth/challenge.js");'
24
25
  source += ' await import("/add_auth/passkey.js");' if Rails::Runtime.config.passkeys.enabled
25
26
  render body: source, content_type: "text/javascript"
26
27
  end