authnz_eleven 0.1.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 (237) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +384 -0
  5. data/lib/authnz_eleven/identity.rb +210 -0
  6. data/lib/authnz_eleven/principal.rb +201 -0
  7. data/lib/authnz_eleven/principals.rb +111 -0
  8. data/lib/authnz_eleven/version.rb +5 -0
  9. data/lib/authnz_eleven/wizard.rb +570 -0
  10. data/lib/authnz_eleven.rb +15 -0
  11. data/lib/generators/authnz_eleven/USAGE +26 -0
  12. data/lib/generators/authnz_eleven/authnz_eleven_generator.rb +3040 -0
  13. data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.LICENSE +22 -0
  14. data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.txt +558 -0
  15. data/lib/generators/authnz_eleven/templates/config/initializers/authentication.rb.tt +275 -0
  16. data/lib/generators/authnz_eleven/templates/config/initializers/omniauth.rb.tt +9 -0
  17. data/lib/generators/authnz_eleven/templates/config/initializers/team_slug.rb.tt +24 -0
  18. data/lib/generators/authnz_eleven/templates/config/initializers/webauthn.rb.tt +14 -0
  19. data/lib/generators/authnz_eleven/templates/config/locales/authentication.en.yml.tt +11 -0
  20. data/lib/generators/authnz_eleven/templates/controllers/active_teams_controller.rb.tt +6 -0
  21. data/lib/generators/authnz_eleven/templates/controllers/admin/dashboard_controller.rb.tt +4 -0
  22. data/lib/generators/authnz_eleven/templates/controllers/admin/sessions_controller.rb.tt +17 -0
  23. data/lib/generators/authnz_eleven/templates/controllers/admin/users_controller.rb.tt +91 -0
  24. data/lib/generators/authnz_eleven/templates/controllers/admin_controller.rb.tt +6 -0
  25. data/lib/generators/authnz_eleven/templates/controllers/base_controller.rb.tt +11 -0
  26. data/lib/generators/authnz_eleven/templates/controllers/concerns/api_token_authentication.rb.tt +21 -0
  27. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/easy_dev_login.rb.tt +47 -0
  28. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/registering.rb.tt +25 -0
  29. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/sudoable.rb.tt +117 -0
  30. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/webauthn_ceremony.rb.tt +51 -0
  31. data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication.rb.tt +421 -0
  32. data/lib/generators/authnz_eleven/templates/controllers/concerns/authorization.rb.tt +48 -0
  33. data/lib/generators/authnz_eleven/templates/controllers/concerns/captchable.rb.tt +11 -0
  34. data/lib/generators/authnz_eleven/templates/controllers/impersonations_controller.rb.tt +8 -0
  35. data/lib/generators/authnz_eleven/templates/controllers/invitations_controller.rb.tt +65 -0
  36. data/lib/generators/authnz_eleven/templates/controllers/settings/api_tokens_controller.rb.tt +54 -0
  37. data/lib/generators/authnz_eleven/templates/controllers/settings/authentications/events_controller.rb.tt +5 -0
  38. data/lib/generators/authnz_eleven/templates/controllers/settings/dashboard_controller.rb.tt +4 -0
  39. data/lib/generators/authnz_eleven/templates/controllers/settings/email_verifications_controller.rb.tt +15 -0
  40. data/lib/generators/authnz_eleven/templates/controllers/settings/emails_controller.rb.tt +93 -0
  41. data/lib/generators/authnz_eleven/templates/controllers/settings/invitations_controller.rb.tt +61 -0
  42. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/authenticators_controller.rb.tt +87 -0
  43. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/recovery_codes_controller.rb.tt +27 -0
  44. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/security_keys_controller.rb.tt +86 -0
  45. data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/sms_controller.rb.tt +84 -0
  46. data/lib/generators/authnz_eleven/templates/controllers/settings/omniauth_identities_controller.rb.tt +27 -0
  47. data/lib/generators/authnz_eleven/templates/controllers/settings/passkeys_controller.rb.tt +70 -0
  48. data/lib/generators/authnz_eleven/templates/controllers/settings/passwords_controller.rb.tt +74 -0
  49. data/lib/generators/authnz_eleven/templates/controllers/settings/phone_verifications_controller.rb.tt +39 -0
  50. data/lib/generators/authnz_eleven/templates/controllers/settings/phones_controller.rb.tt +88 -0
  51. data/lib/generators/authnz_eleven/templates/controllers/settings/sessions_controller.rb.tt +29 -0
  52. data/lib/generators/authnz_eleven/templates/controllers/settings/users_controller.rb.tt +42 -0
  53. data/lib/generators/authnz_eleven/templates/controllers/settings_controller.rb.tt +5 -0
  54. data/lib/generators/authnz_eleven/templates/controllers/teams_controller.rb.tt +38 -0
  55. data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passkeys_controller.rb.tt +36 -0
  56. data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passwords_controller.rb.tt +28 -0
  57. data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments_controller.rb.tt +14 -0
  58. data/lib/generators/authnz_eleven/templates/controllers/users/easy_dev_logins_controller.rb.tt +19 -0
  59. data/lib/generators/authnz_eleven/templates/controllers/users/email_verifications_controller.rb.tt +36 -0
  60. data/lib/generators/authnz_eleven/templates/controllers/users/magic_link_controller.rb.tt +53 -0
  61. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/recovery_codes_controller.rb.tt +30 -0
  62. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/security_keys_controller.rb.tt +34 -0
  63. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/sms_controller.rb.tt +45 -0
  64. data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/totps_controller.rb.tt +42 -0
  65. data/lib/generators/authnz_eleven/templates/controllers/users/omniauth/registrations_controller.rb.tt +26 -0
  66. data/lib/generators/authnz_eleven/templates/controllers/users/omniauth_controller.rb.tt +139 -0
  67. data/lib/generators/authnz_eleven/templates/controllers/users/passkeys/sessions_controller.rb.tt +18 -0
  68. data/lib/generators/authnz_eleven/templates/controllers/users/password_resets_controller.rb.tt +64 -0
  69. data/lib/generators/authnz_eleven/templates/controllers/users/registrations/email_verifications_controller.rb.tt +53 -0
  70. data/lib/generators/authnz_eleven/templates/controllers/users/registrations/phone_verifications_controller.rb.tt +37 -0
  71. data/lib/generators/authnz_eleven/templates/controllers/users/registrations_controller.rb.tt +112 -0
  72. data/lib/generators/authnz_eleven/templates/controllers/users/sessions_controller.rb.tt +52 -0
  73. data/lib/generators/authnz_eleven/templates/controllers/users/sms_sessions_controller.rb.tt +53 -0
  74. data/lib/generators/authnz_eleven/templates/controllers/users/sudos_controller.rb.tt +42 -0
  75. data/lib/generators/authnz_eleven/templates/erb/admin/dashboard/index.html.erb.tt +9 -0
  76. data/lib/generators/authnz_eleven/templates/erb/admin/sessions/index.html.erb.tt +27 -0
  77. data/lib/generators/authnz_eleven/templates/erb/admin/users/index.html.erb.tt +32 -0
  78. data/lib/generators/authnz_eleven/templates/erb/admin/users/show.html.erb.tt +70 -0
  79. data/lib/generators/authnz_eleven/templates/erb/invitations/show.html.erb.tt +9 -0
  80. data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/create.html.erb.tt +7 -0
  81. data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/index.html.erb.tt +29 -0
  82. data/lib/generators/authnz_eleven/templates/erb/settings/authentications/events/index.html.erb.tt +18 -0
  83. data/lib/generators/authnz_eleven/templates/erb/settings/dashboard/index.html.erb.tt +75 -0
  84. data/lib/generators/authnz_eleven/templates/erb/settings/emails/edit.html.erb.tt +68 -0
  85. data/lib/generators/authnz_eleven/templates/erb/settings/invitations/new.html.erb.tt +31 -0
  86. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/authenticators/new.html.erb.tt +57 -0
  87. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/create.html.erb.tt +14 -0
  88. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/index.html.erb.tt +13 -0
  89. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/edit.html.erb.tt +14 -0
  90. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/index.html.erb.tt +20 -0
  91. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/new.html.erb.tt +17 -0
  92. data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/sms/new.html.erb.tt +35 -0
  93. data/lib/generators/authnz_eleven/templates/erb/settings/omniauth_identities/index.html.erb.tt +28 -0
  94. data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/edit.html.erb.tt +14 -0
  95. data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/index.html.erb.tt +21 -0
  96. data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/new.html.erb.tt +13 -0
  97. data/lib/generators/authnz_eleven/templates/erb/settings/passwords/edit.html.erb.tt +68 -0
  98. data/lib/generators/authnz_eleven/templates/erb/settings/phone_verifications/new.html.erb.tt +22 -0
  99. data/lib/generators/authnz_eleven/templates/erb/settings/phones/edit.html.erb.tt +58 -0
  100. data/lib/generators/authnz_eleven/templates/erb/settings/sessions/index.html.erb.tt +29 -0
  101. data/lib/generators/authnz_eleven/templates/erb/settings/users/show.html.erb.tt +28 -0
  102. data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_nav.html.erb.tt +24 -0
  103. data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_sudo_challenge.html.erb.tt +43 -0
  104. data/lib/generators/authnz_eleven/templates/erb/shared/_captcha.html.erb.tt +8 -0
  105. data/lib/generators/authnz_eleven/templates/erb/teams/index.html.erb.tt +34 -0
  106. data/lib/generators/authnz_eleven/templates/erb/teams/new.html.erb.tt +22 -0
  107. data/lib/generators/authnz_eleven/templates/erb/teams/show.html.erb.tt +5 -0
  108. data/lib/generators/authnz_eleven/templates/erb/user_mailer/email_change_verification.html.erb.tt +7 -0
  109. data/lib/generators/authnz_eleven/templates/erb/user_mailer/invitation.html.erb.tt +11 -0
  110. data/lib/generators/authnz_eleven/templates/erb/user_mailer/magic_link.html.erb.tt +5 -0
  111. data/lib/generators/authnz_eleven/templates/erb/user_mailer/password_reset.html.erb.tt +6 -0
  112. data/lib/generators/authnz_eleven/templates/erb/user_mailer/registration_email_verification.html.erb.tt +7 -0
  113. data/lib/generators/authnz_eleven/templates/erb/user_mailer/security_notification.html.erb.tt +15 -0
  114. data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passkeys/new.html.erb.tt +24 -0
  115. data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passwords/new.html.erb.tt +21 -0
  116. data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/show.html.erb.tt +21 -0
  117. data/lib/generators/authnz_eleven/templates/erb/users/email_verifications/show.html.erb.tt +9 -0
  118. data/lib/generators/authnz_eleven/templates/erb/users/magic_link/edit.html.erb.tt +9 -0
  119. data/lib/generators/authnz_eleven/templates/erb/users/magic_link/new.html.erb.tt +22 -0
  120. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/recovery_codes/new.html.erb.tt +20 -0
  121. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/security_keys/new.html.erb.tt +17 -0
  122. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/sms/new.html.erb.tt +25 -0
  123. data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/totps/new.html.erb.tt +21 -0
  124. data/lib/generators/authnz_eleven/templates/erb/users/omniauth/registrations/new.html.erb.tt +28 -0
  125. data/lib/generators/authnz_eleven/templates/erb/users/password_resets/edit.html.erb.tt +26 -0
  126. data/lib/generators/authnz_eleven/templates/erb/users/password_resets/new.html.erb.tt +20 -0
  127. data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/new.html.erb.tt +11 -0
  128. data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/show.html.erb.tt +34 -0
  129. data/lib/generators/authnz_eleven/templates/erb/users/registrations/new.html.erb.tt +87 -0
  130. data/lib/generators/authnz_eleven/templates/erb/users/registrations/phone_verifications/new.html.erb.tt +46 -0
  131. data/lib/generators/authnz_eleven/templates/erb/users/sessions/new.html.erb.tt +88 -0
  132. data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/edit.html.erb.tt +30 -0
  133. data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/new.html.erb.tt +22 -0
  134. data/lib/generators/authnz_eleven/templates/erb/users/sudos/new.html.erb.tt +9 -0
  135. data/lib/generators/authnz_eleven/templates/javascript/controllers/webauthn_controller.js.tt +142 -0
  136. data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_deferred_job.rb.tt +9 -0
  137. data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_mail_delivery_job.rb.tt +3 -0
  138. data/lib/generators/authnz_eleven/templates/jobs/sms_delivery_job.rb.tt +11 -0
  139. data/lib/generators/authnz_eleven/templates/lib/captcha/null.rb.tt +9 -0
  140. data/lib/generators/authnz_eleven/templates/lib/captcha/turnstile.rb.tt +39 -0
  141. data/lib/generators/authnz_eleven/templates/lib/captcha.rb.tt +15 -0
  142. data/lib/generators/authnz_eleven/templates/lib/sms/log.rb.tt +8 -0
  143. data/lib/generators/authnz_eleven/templates/lib/sms/twilio.rb.tt +70 -0
  144. data/lib/generators/authnz_eleven/templates/lib/sms.rb.tt +15 -0
  145. data/lib/generators/authnz_eleven/templates/mailers/user_mailer.rb.tt +98 -0
  146. data/lib/generators/authnz_eleven/templates/migrations/add_guest_to_users_migration.rb.tt +6 -0
  147. data/lib/generators/authnz_eleven/templates/migrations/create_api_tokens_migration.rb.tt +14 -0
  148. data/lib/generators/authnz_eleven/templates/migrations/create_events_migration.rb.tt +26 -0
  149. data/lib/generators/authnz_eleven/templates/migrations/create_invitations_migration.rb.tt +20 -0
  150. data/lib/generators/authnz_eleven/templates/migrations/create_memberships_migration.rb.tt +12 -0
  151. data/lib/generators/authnz_eleven/templates/migrations/create_omniauth_identities_migration.rb.tt +16 -0
  152. data/lib/generators/authnz_eleven/templates/migrations/create_password_histories_migration.rb.tt +11 -0
  153. data/lib/generators/authnz_eleven/templates/migrations/create_pending_registrations_migration.rb.tt +31 -0
  154. data/lib/generators/authnz_eleven/templates/migrations/create_recovery_codes_migration.rb.tt +11 -0
  155. data/lib/generators/authnz_eleven/templates/migrations/create_sessions_migration.rb.tt +19 -0
  156. data/lib/generators/authnz_eleven/templates/migrations/create_teams_migration.rb.tt +9 -0
  157. data/lib/generators/authnz_eleven/templates/migrations/create_users_migration.rb.tt +70 -0
  158. data/lib/generators/authnz_eleven/templates/migrations/create_webauthn_credentials_migration.rb.tt +16 -0
  159. data/lib/generators/authnz_eleven/templates/models/api_token.rb.tt +24 -0
  160. data/lib/generators/authnz_eleven/templates/models/concerns/adminable.rb.tt +15 -0
  161. data/lib/generators/authnz_eleven/templates/models/concerns/authenticatable.rb.tt +151 -0
  162. data/lib/generators/authnz_eleven/templates/models/concerns/bannable.rb.tt +24 -0
  163. data/lib/generators/authnz_eleven/templates/models/concerns/deadboltable.rb.tt +39 -0
  164. data/lib/generators/authnz_eleven/templates/models/concerns/email_verifiable.rb.tt +52 -0
  165. data/lib/generators/authnz_eleven/templates/models/concerns/eventable.rb.tt +22 -0
  166. data/lib/generators/authnz_eleven/templates/models/concerns/guestable.rb.tt +30 -0
  167. data/lib/generators/authnz_eleven/templates/models/concerns/invitable.rb.tt +12 -0
  168. data/lib/generators/authnz_eleven/templates/models/concerns/magic_linkable.rb.tt +34 -0
  169. data/lib/generators/authnz_eleven/templates/models/concerns/password_historical.rb.tt +28 -0
  170. data/lib/generators/authnz_eleven/templates/models/concerns/password_policy.rb.tt +38 -0
  171. data/lib/generators/authnz_eleven/templates/models/concerns/password_rotatable.rb.tt +15 -0
  172. data/lib/generators/authnz_eleven/templates/models/concerns/phone_verifiable.rb.tt +50 -0
  173. data/lib/generators/authnz_eleven/templates/models/concerns/principals.rb.tt +67 -0
  174. data/lib/generators/authnz_eleven/templates/models/concerns/recoverable.rb.tt +19 -0
  175. data/lib/generators/authnz_eleven/templates/models/concerns/second_factor.rb.tt +52 -0
  176. data/lib/generators/authnz_eleven/templates/models/concerns/sms_challengeable.rb.tt +38 -0
  177. data/lib/generators/authnz_eleven/templates/models/concerns/team_member.rb.tt +20 -0
  178. data/lib/generators/authnz_eleven/templates/models/current.rb.tt +28 -0
  179. data/lib/generators/authnz_eleven/templates/models/event.rb.tt +32 -0
  180. data/lib/generators/authnz_eleven/templates/models/invitation.rb.tt +122 -0
  181. data/lib/generators/authnz_eleven/templates/models/membership.rb.tt +16 -0
  182. data/lib/generators/authnz_eleven/templates/models/omniauth_identity.rb.tt +12 -0
  183. data/lib/generators/authnz_eleven/templates/models/password_history.rb.tt +3 -0
  184. data/lib/generators/authnz_eleven/templates/models/pending_registration.rb.tt +216 -0
  185. data/lib/generators/authnz_eleven/templates/models/recovery_code.rb.tt +41 -0
  186. data/lib/generators/authnz_eleven/templates/models/session.rb.tt +46 -0
  187. data/lib/generators/authnz_eleven/templates/models/team.rb.tt +12 -0
  188. data/lib/generators/authnz_eleven/templates/models/user.rb.tt +59 -0
  189. data/lib/generators/authnz_eleven/templates/models/webauthn_credential.rb.tt +12 -0
  190. data/lib/generators/authnz_eleven/templates/tasks/authnz_eleven.rake.tt +41 -0
  191. data/lib/generators/authnz_eleven/templates/test_unit/controllers/account_existence_parity_test.rb.tt +158 -0
  192. data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/access_test.rb.tt +139 -0
  193. data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/multi_factor_authentication_test.rb.tt +65 -0
  194. data/lib/generators/authnz_eleven/templates/test_unit/controllers/api_token_authentication_test.rb.tt +45 -0
  195. data/lib/generators/authnz_eleven/templates/test_unit/controllers/credential_enrollment_test.rb.tt +84 -0
  196. data/lib/generators/authnz_eleven/templates/test_unit/controllers/guest_access_test.rb.tt +113 -0
  197. data/lib/generators/authnz_eleven/templates/test_unit/controllers/invitations_controller_test.rb.tt +230 -0
  198. data/lib/generators/authnz_eleven/templates/test_unit/controllers/passkey_ceremony_test.rb.tt +126 -0
  199. data/lib/generators/authnz_eleven/templates/test_unit/controllers/password_rotation_test.rb.tt +67 -0
  200. data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_key_ceremony_test.rb.tt +218 -0
  201. data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_notifications_test.rb.tt +39 -0
  202. data/lib/generators/authnz_eleven/templates/test_unit/controllers/session_lifecycle_test.rb.tt +102 -0
  203. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/api_tokens_controller_test.rb.tt +61 -0
  204. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/authentications/events_controller_test.rb.tt +17 -0
  205. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/email_verifications_controller_test.rb.tt +39 -0
  206. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/invitations_controller_test.rb.tt +157 -0
  207. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/management_test.rb.tt +256 -0
  208. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/multi_factor_authentication/management_test.rb.tt +222 -0
  209. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/omniauth_identities_controller_test.rb.tt +57 -0
  210. data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/phone_verifications_controller_test.rb.tt +146 -0
  211. data/lib/generators/authnz_eleven/templates/test_unit/controllers/teams_controller_test.rb.tt +83 -0
  212. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/email_verifications_controller_test.rb.tt +74 -0
  213. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/magic_link_controller_test.rb.tt +90 -0
  214. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/multi_factor_authentication/challenge_test.rb.tt +143 -0
  215. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth/registrations_controller_test.rb.tt +138 -0
  216. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth_controller_test.rb.tt +97 -0
  217. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/password_resets_controller_test.rb.tt +58 -0
  218. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/email_verifications_controller_test.rb.tt +102 -0
  219. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/phone_verifications_controller_test.rb.tt +69 -0
  220. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations_controller_test.rb.tt +180 -0
  221. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sessions_controller_test.rb.tt +124 -0
  222. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sms_sessions_controller_test.rb.tt +111 -0
  223. data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sudos_controller_test.rb.tt +110 -0
  224. data/lib/generators/authnz_eleven/templates/test_unit/fixtures/memberships.yml.tt +13 -0
  225. data/lib/generators/authnz_eleven/templates/test_unit/fixtures/teams.yml.tt +2 -0
  226. data/lib/generators/authnz_eleven/templates/test_unit/fixtures/users.yml.tt +68 -0
  227. data/lib/generators/authnz_eleven/templates/test_unit/mailers/user_mailer_test.rb.tt +88 -0
  228. data/lib/generators/authnz_eleven/templates/test_unit/models/api_token_test.rb.tt +38 -0
  229. data/lib/generators/authnz_eleven/templates/test_unit/models/password_history_test.rb.tt +51 -0
  230. data/lib/generators/authnz_eleven/templates/test_unit/models/session_test.rb.tt +16 -0
  231. data/lib/generators/authnz_eleven/templates/test_unit/models/sms_challengeable_test.rb.tt +108 -0
  232. data/lib/generators/authnz_eleven/templates/test_unit/models/user/guestable_test.rb.tt +78 -0
  233. data/lib/generators/authnz_eleven/templates/test_unit/models/user/recovery_codes_test.rb.tt +32 -0
  234. data/lib/generators/authnz_eleven/templates/test_unit/models/user/sign_in_methods_test.rb.tt +56 -0
  235. data/lib/generators/authnz_eleven/templates/test_unit/models/user_test.rb.tt +145 -0
  236. data/lib/generators/authnz_eleven/wizard_generator.rb +52 -0
  237. metadata +309 -0
@@ -0,0 +1,14 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+
3
+ <h1>Rename passkey</h1>
4
+
5
+ <%%= form_with url: <%= identity.settings_helper_prefix %>_passkey_path(@passkey), method: :patch do |form| %>
6
+ <div>
7
+ <%%= form.label :name %><br>
8
+ <%%= form.text_field :name, value: @passkey.name, required: true, autofocus: true %>
9
+ </div>
10
+
11
+ <div><%%= form.submit "Save" %></div>
12
+ <%% end %>
13
+
14
+ <p><%%= link_to "Back to passkeys", <%= identity.settings_helper_prefix %>_passkeys_path %></p>
@@ -0,0 +1,21 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+
3
+ <h1>Passkeys</h1>
4
+
5
+ <p>Passkeys let you sign in without a password, using your device's screen lock,
6
+ a fingerprint, or a security key.</p>
7
+
8
+ <ul>
9
+ <%% @passkeys.each do |passkey| %>
10
+ <li>
11
+ <%%= passkey.name %>
12
+ <%%= link_to "Rename", edit_<%= identity.settings_helper_prefix %>_passkey_path(passkey) %>
13
+ <%%= button_to "Remove", <%= identity.settings_helper_prefix %>_passkey_path(passkey), method: :delete,
14
+ data: {turbo_confirm: "Remove this passkey?"} %>
15
+ </li>
16
+ <%% end %>
17
+ </ul>
18
+
19
+ <p><%%= link_to "Add a passkey", new_<%= identity.settings_helper_prefix %>_passkey_path %></p>
20
+
21
+ <p><%%= link_to "Back to settings", <%= identity.settings_helper_prefix %>_path %></p>
@@ -0,0 +1,13 @@
1
+ <h1>Add a passkey</h1>
2
+
3
+ <p>Follow your browser's prompt to create a passkey with your device screen lock,
4
+ a fingerprint, or a security key.</p>
5
+
6
+ <div data-controller="webauthn"
7
+ data-webauthn-challenge-url-value="<%%= new_<%= identity.settings_helper_prefix %>_passkey_path(format: :json) %>"
8
+ data-webauthn-verification-url-value="<%%= <%= identity.settings_helper_prefix %>_passkeys_path %>">
9
+ <button data-webauthn-target="button" data-action="webauthn#createCredential">Create passkey</button>
10
+ <p data-webauthn-target="error" style="color: red" hidden></p>
11
+ </div>
12
+
13
+ <p><%%= link_to "Back to passkeys", <%= identity.settings_helper_prefix %>_passkeys_path %></p>
@@ -0,0 +1,68 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <%- if password_nullable? -%>
5
+ <h1><%%= @user.password_set? ? "Change your password" : "Add a password" %></h1>
6
+ <%- else -%>
7
+ <h1>Change your password</h1>
8
+ <%- end -%>
9
+
10
+ <%% if @user.errors.any? %>
11
+ <ul style="color: red">
12
+ <%% @user.errors.full_messages.each do |message| %>
13
+ <li><%%= message %></li>
14
+ <%% end %>
15
+ </ul>
16
+ <%% end %>
17
+
18
+ <%- if password_nullable? -%>
19
+ <%% if @user.password_set? %>
20
+ <%- end -%>
21
+ <%%= form_with url: <%= identity.settings_helper_prefix %>_password_path, method: :patch do |form| %>
22
+ <div>
23
+ <%%= form.label :password_challenge, "Current password" %><br>
24
+ <%%= form.password_field :password_challenge, autocomplete: "current-password" %>
25
+ </div>
26
+
27
+ <div>
28
+ <%%= form.label :password, "New password" %><br>
29
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
30
+ </div>
31
+
32
+ <div>
33
+ <%%= form.label :password_confirmation %><br>
34
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
35
+ </div>
36
+
37
+ <div><%%= form.submit "Change password" %></div>
38
+ <%% end %>
39
+ <%- if password_nullable? -%>
40
+ <%% else %>
41
+ <%%= form_with url: <%= identity.settings_helper_prefix %>_password_path, method: :post do |form| %>
42
+ <div>
43
+ <%%= form.label :password %><br>
44
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
45
+ </div>
46
+
47
+ <div>
48
+ <%%= form.label :password_confirmation %><br>
49
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
50
+ </div>
51
+
52
+ <div><%%= form.submit "Set password" %></div>
53
+ <%% end %>
54
+ <%% end %>
55
+ <%- end -%>
56
+ <%- if password_removable? -%>
57
+
58
+ <%%# Never offer removal when the password is the account's last sign-in method. %>
59
+ <%% if @user.password_set? && !@user.last_sign_in_method?(:password) %>
60
+ <hr>
61
+ <p>
62
+ You can sign in without a password. Removing it will prevent phishing.
63
+ </p>
64
+ <%%= button_to "Remove my password", <%= identity.settings_helper_prefix %>_password_path, method: :delete %>
65
+ <%% end %>
66
+ <%- end -%>
67
+
68
+ <p><%%= link_to "Back to settings", <%= identity.settings_helper_prefix %>_path %></p>
@@ -0,0 +1,22 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Verify your phone</h1>
5
+
6
+ <p>
7
+ Enter the six-digit code we texted to <%%= <%= identity.current_class %>.user.pending_phone %> to confirm the number is yours.
8
+ </p>
9
+
10
+ <%%= form_with url: <%= identity.settings_helper_prefix %>_phone_verification_path, method: :patch do |form| %>
11
+ <div>
12
+ <%%= form.label :code, "Verification code" %><br>
13
+ <%%= form.text_field :code, required: true, autocomplete: "one-time-code",
14
+ inputmode: "numeric", pattern: "[0-9]*", autofocus: true %>
15
+ </div>
16
+
17
+ <div><%%= form.submit "Verify phone" %></div>
18
+ <%% end %>
19
+
20
+ <%%= button_to "Send me a code", <%= identity.settings_helper_prefix %>_phone_verification_path, method: :post %>
21
+
22
+ <p><%%= link_to "Back to settings", <%= identity.settings_helper_prefix %>_path %></p>
@@ -0,0 +1,58 @@
1
+ <%- optional = principals.phone.optional? -%>
2
+ <%- field_label = optional ? %q(phone ? "New phone" : "Phone") : %q("New phone") -%>
3
+ <%- submit_label = optional ? %q(phone ? "Change phone" : "Add a phone") : %q("Change phone") -%>
4
+ <p style="color: green"><%%= notice %></p>
5
+ <p style="color: red"><%%= alert %></p>
6
+
7
+ <%- if optional -%>
8
+ <%% phone = <%= identity.current_class %>.user.phone_in_database.presence %>
9
+
10
+ <h1><%%= phone ? "Change your phone" : "Add a phone" %></h1>
11
+
12
+ <%% if phone %>
13
+ <p>Current phone: <%%= phone %></p>
14
+ <%% end %>
15
+ <%- else -%>
16
+ <h1>Change your phone</h1>
17
+
18
+ <p>Current phone: <%%= <%= identity.current_class %>.user.phone_in_database %></p>
19
+ <%- end -%>
20
+ <%- if phone_verifiable? -%>
21
+ <%% if <%= identity.current_class %>.user.pending_phone_in_database.present? %>
22
+ <p>Unconfirmed phone: <%%= <%= identity.current_class %>.user.pending_phone_in_database %></p>
23
+ <%% end %>
24
+ <%- end -%>
25
+
26
+ <%% if @user.errors.any? %>
27
+ <ul style="color: red">
28
+ <%% @user.errors.full_messages.each do |message| %>
29
+ <li><%%= message %></li>
30
+ <%% end %>
31
+ </ul>
32
+ <%% end %>
33
+
34
+ <%%= form_with url: <%= identity.settings_helper_prefix %>_phone_path, method: :patch do |form| %>
35
+ <div>
36
+ <%%= form.label :phone, <%= field_label %> %><br>
37
+ <%%= form.telephone_field :phone, value: <%= phone_verifiable? ? "#{identity.current_class}.user.pending_phone || " : "" %><%= identity.current_class %>.user.phone<%= principals.phone.required? ? ", required: true" : "" %>, autocomplete: "tel", placeholder: "(415) 826-4001" %>
38
+ <%- if optional -%>
39
+ <%% if phone %><small>Leave blank to remove your phone.</small><%% end %>
40
+ <%- end -%>
41
+ </div>
42
+ <%- if sudoable? -%>
43
+
44
+ <%%= render "<%= identity.sudo_partial %>", form:, submit: <%= submit_label %> %>
45
+ <%- else -%>
46
+ <%- if password? -%>
47
+
48
+ <div>
49
+ <%%= form.label :password_challenge, "Current password" %><br>
50
+ <%%= form.password_field :password_challenge, autocomplete: "current-password" %>
51
+ </div>
52
+ <%- end -%>
53
+
54
+ <div><%%= form.submit <%= submit_label %> %></div>
55
+ <%- end -%>
56
+ <%% end %>
57
+
58
+ <p><%%= link_to "Back to settings", <%= identity.settings_helper_prefix %>_path %></p>
@@ -0,0 +1,29 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Your sessions</h1>
5
+
6
+ <table>
7
+ <thead>
8
+ <tr><th>Device</th><th>Signed in with</th><th>IP address</th><th>Signed in</th><th></th></tr>
9
+ </thead>
10
+ <tbody>
11
+ <%% @sessions.each do |session| %>
12
+ <tr>
13
+ <td><%%= session.user_agent %></td>
14
+ <td><%%= session.via&.humanize %></td>
15
+ <td><%%= session.ip_address %></td>
16
+ <td><%%= session.created_at %></td>
17
+ <td>
18
+ <%% if session == <%= identity.current_class %>.session %>
19
+ This session
20
+ <%% else %>
21
+ <%%= button_to "Sign out", <%= identity.settings_helper_prefix %>_session_path(session), method: :delete %>
22
+ <%% end %>
23
+ </td>
24
+ </tr>
25
+ <%% end %>
26
+ </tbody>
27
+ </table>
28
+
29
+ <p><%%= link_to "Back to settings", <%= identity.settings_helper_prefix %>_path %></p>
@@ -0,0 +1,28 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Delete your account</h1>
5
+
6
+ <p>This permanently deletes your account and signs out every session. It can't be undone.</p>
7
+
8
+ <%%= form_with url: <%= identity.settings_helper_prefix %>_user_path, method: :delete do |form| %>
9
+ <div>
10
+ <%%= form.label :confirmation, "Type DELETE ACCOUNT to confirm" %><br>
11
+ <%%= form.text_field :confirmation, required: true, autocomplete: "off" %>
12
+ </div>
13
+
14
+ <%- if sudoable? -%>
15
+ <%%= render "<%= identity.sudo_partial %>", form:, submit: "Delete my account" %>
16
+ <%- else -%>
17
+ <%- if password? -%>
18
+ <div>
19
+ <%%= form.label :password_challenge, "Current password" %><br>
20
+ <%%= form.password_field :password_challenge, required: true, autocomplete: "current-password" %>
21
+ </div>
22
+
23
+ <%- end -%>
24
+ <div><%%= form.submit "Delete my account" %></div>
25
+ <%- end -%>
26
+ <%% end %>
27
+
28
+ <p><%%= link_to "Back to settings", <%= identity.settings_helper_prefix %>_path %></p>
@@ -0,0 +1,24 @@
1
+ <nav>
2
+ <%- if impersonatable? -%>
3
+ <%% if impersonating? %>
4
+ <span>Impersonating <%%= <%= principals.display_expression("#{identity.current_class}.user") %> %>.</span>
5
+ <%%= button_to "Stop impersonating", <%= identity.nested_helper("impersonation_path") %>, method: :delete %>
6
+ <%% end %>
7
+ <%- end -%>
8
+ <%% if authenticated? %>
9
+ <span><%- if teams? -%><%% if <%= identity.current_class %>.team %><%%= <%= identity.current_class %>.team.name %>/<%% end %><%- end -%><%%= <%= principals.display_expression("#{identity.current_class}.user") %> %></span>
10
+ <%%= link_to "Settings", <%= identity.settings_helper_prefix %>_path %>
11
+ <%- if teams? -%>
12
+ <%%= link_to "Teams", <%= identity.nested_helper("teams_path") %> %>
13
+ <%- end -%>
14
+ <%- if admin_dashboard? -%>
15
+ <%% if <%= identity.current_class %>.user.admin? %><%%= link_to "Admin", <%= admin_helper("root_path") %> %><%% end %>
16
+ <%- end -%>
17
+ <%%= button_to "Sign out", <%= identity.route_scope %>_sign_out_path, method: :delete %>
18
+ <%% else %>
19
+ <%%= link_to "Sign in", <%= identity.route_scope %>_sign_in_path %>
20
+ <%- if typed_registration? -%>
21
+ <%%= link_to "Sign up", <%= identity.route_scope %>_sign_up_path %>
22
+ <%- end -%>
23
+ <%% end %>
24
+ </nav>
@@ -0,0 +1,43 @@
1
+ <%- if single_sudo_bar? -%>
2
+ <div>
3
+ <%%= form.label :sudo_password, "Current password" %><br>
4
+ <%%= form.password_field :sudo_password, required: true, autocomplete: "current-password" %>
5
+ </div>
6
+
7
+ <div><%%= form.submit submit %></div>
8
+ <%- else -%>
9
+ <%%# Keep each strategy's submit button in its branch: WebAuthn must intercept it and submit only after obtaining an assertion. %>
10
+ <%% case sudo_strategy %>
11
+ <%- if sudo_bars.include?(:webauthn) -%>
12
+ <%% when :webauthn %>
13
+ <div data-controller="webauthn"
14
+ data-webauthn-challenge-url-value="<%%= new_<%= identity.route_scope %>_sudo_path(format: :json) %>">
15
+ <%%= form.hidden_field :sudo_credential, data: { webauthn_target: "field" } %>
16
+ <%%= form.button submit, type: "button",
17
+ data: { webauthn_target: "button", action: "webauthn#getCredential" } %>
18
+ <p data-webauthn-target="error" style="color: red" hidden></p>
19
+ </div>
20
+ <%- end -%>
21
+ <%- if sudo_bars.include?(:password) -%>
22
+ <%% when :password %>
23
+ <div>
24
+ <%%= form.label :sudo_password, "Current password" %><br>
25
+ <%%= form.password_field :sudo_password, required: true, autocomplete: "current-password" %>
26
+ </div>
27
+
28
+ <div><%%= form.submit submit %></div>
29
+ <%- end -%>
30
+ <%- if sudo_bars.include?(:totp) -%>
31
+ <%% when :totp %>
32
+ <div>
33
+ <%%= form.label :sudo_code, "Code from your authenticator app" %><br>
34
+ <%%= form.text_field :sudo_code, required: true, autocomplete: "one-time-code", inputmode: "numeric" %>
35
+ </div>
36
+
37
+ <div><%%= form.submit submit %></div>
38
+ <%- end -%>
39
+ <%%# This branch intentionally permits the request when the account has no challengeable factor. %>
40
+ <%% else %>
41
+ <div><%%= form.submit submit %></div>
42
+ <%% end %>
43
+ <%- end -%>
@@ -0,0 +1,8 @@
1
+ <%% if Captcha.site_key %>
2
+ <div class="cf-turnstile"
3
+ data-sitekey="<%%= Captcha.site_key %>"
4
+ <%%# Turnstile writes this field; its name must match Captchable's parameter, and forms using this partial must disable Turbo so rejected one-use tokens are replaced. %>
5
+ data-response-field-name="captcha_token"></div>
6
+ <%%= javascript_include_tag "https://challenges.cloudflare.com/turnstile/v0/api.js",
7
+ async: true, defer: true %>
8
+ <%% end %>
@@ -0,0 +1,34 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Your teams</h1>
5
+ <%- if session_carries_team? -%>
6
+
7
+ <%% if <%= identity.current_class %>.user.on_a_team? %>
8
+ <%%= form_with url: <%= identity.nested_helper("active_team_path") %>, method: :post do |form| %>
9
+ <%% @teams.each do |team| %>
10
+ <div>
11
+ <label>
12
+ <%%= radio_button_tag "team_id", team.id %>
13
+ <%%= team.name %>
14
+ </label>
15
+ </div>
16
+ <%% end %>
17
+
18
+ <div><%%= form.submit "Continue" %></div>
19
+ <%% end %>
20
+ <%% end %>
21
+ <%- else -%>
22
+
23
+ <ul>
24
+ <%% @teams.each do |team| %>
25
+ <%- if scope_teams? -%>
26
+ <li><%%= link_to team.name, <%= team_root_helper %>(team_id: team) %></li>
27
+ <%- else -%>
28
+ <li><%%= link_to team.name, root_path(script_name: "/#{team.id}") %></li>
29
+ <%- end -%>
30
+ <%% end %>
31
+ </ul>
32
+ <%- end -%>
33
+
34
+ <%%= link_to "Start a new team", new_<%= identity.nested_helper("team_path") %> %>
@@ -0,0 +1,22 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Start a new team</h1>
4
+
5
+ <%%= form_with model: @team, url: <%= identity.nested_helper("teams_path") %> do |form| %>
6
+ <%% if @team.errors.any? %>
7
+ <div style="color: red">
8
+ <%% @team.errors.full_messages.each do |message| %>
9
+ <div><%%= message %></div>
10
+ <%% end %>
11
+ </div>
12
+ <%% end %>
13
+
14
+ <div>
15
+ <%%= form.label :name %>
16
+ <%%= form.text_field :name, required: true, autofocus: true %>
17
+ </div>
18
+
19
+ <div><%%= form.submit "Create team" %></div>
20
+ <%% end %>
21
+
22
+ <%%= link_to "Back", <%= identity.nested_helper("teams_path") %> %>
@@ -0,0 +1,5 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+
3
+ <h1><%%= <%= identity.current_class %>.team.name %></h1>
4
+
5
+ <%%= link_to "Your teams", <%= identity.nested_helper("teams_path") %> %>
@@ -0,0 +1,7 @@
1
+ <p>Hi,</p>
2
+
3
+ <p>Confirm your new email address by clicking the link below.</p>
4
+
5
+ <p><%%= link_to "Confirm new address", <%= identity.route_scope %>_verify_email_change_url(sid: @signed_id) %></p>
6
+
7
+ <p>If you didn't ask for this, you can ignore this message.</p>
@@ -0,0 +1,11 @@
1
+ <p>Hi,</p>
2
+
3
+ <%- if teams? -%>
4
+ <p><%%= @invitation.inviter.<%= principals.display.column %> %> has invited you to join their team. Use the link
5
+ below to accept:</p>
6
+ <%- else -%>
7
+ <p><%%= @invitation.inviter.<%= principals.display.column %> %> has invited you to <%%= <%= identity.config_reference %>.app_name %>.
8
+ Use the link below to accept:</p>
9
+ <%- end -%>
10
+
11
+ <p><%%= link_to "Accept invitation", <%= identity.namespaced? ? "#{identity.route_scope}_accept_invitation" : "accept_invitation" %>_url(token: @signed_id) %></p>
@@ -0,0 +1,5 @@
1
+ <p>Hi,</p>
2
+
3
+ <p>Use the link below to sign in. It expires in 20 minutes.</p>
4
+
5
+ <p><%%= link_to "Sign in", <%= identity.route_scope %>_magic_link_url(sid: @signed_id) %></p>
@@ -0,0 +1,6 @@
1
+ <p>Hi,</p>
2
+
3
+ <p>Someone requested a password reset for your account. If that was you, use the
4
+ link below (it expires in 20 minutes). If not, you can ignore this email.</p>
5
+
6
+ <p><%%= link_to "Reset password", <%= identity.route_scope %>_reset_password_url(sid: @signed_id) %></p>
@@ -0,0 +1,7 @@
1
+ <p>Hi,</p>
2
+
3
+ <p>Confirm this email address to finish signing up.</p>
4
+
5
+ <p><%%= link_to "Confirm email", <%= identity.route_scope %>_verify_email_url(sid: @signed_id) %></p>
6
+
7
+ <p>If you didn't ask for this, you can ignore this message.</p>
@@ -0,0 +1,15 @@
1
+ <h1><%%= @subject %></h1>
2
+
3
+ <p>This security change was made to your account.</p>
4
+
5
+ <%% if @occurred_at %>
6
+ <p>When: <%%= @occurred_at %></p>
7
+ <%% end %>
8
+ <%% if @ip_address.present? %>
9
+ <p>IP address: <%%= @ip_address %></p>
10
+ <%% end %>
11
+ <%% if @user_agent.present? %>
12
+ <p>Device: <%%= @user_agent %></p>
13
+ <%% end %>
14
+
15
+ <p>If this wasn't you, secure your account and contact support.</p>
@@ -0,0 +1,24 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Create a passkey</h1>
4
+
5
+ <p>Follow your browser's prompt to create a passkey with your device screen lock,
6
+ a fingerprint, or a security key.</p>
7
+
8
+ <%% if @registration.errors.any? %>
9
+ <ul style="color: red">
10
+ <%% @registration.errors.full_messages.each do |message| %>
11
+ <li><%%= message %></li>
12
+ <%% end %>
13
+ </ul>
14
+ <%% end %>
15
+
16
+ <%%= form_with url: <%= identity.route_scope %>_registration_passkey_path, method: :post, data: { turbo: false } do |form| %>
17
+ <div data-controller="webauthn"
18
+ data-webauthn-challenge-url-value="<%%= <%= identity.route_scope %>_new_registration_passkey_path(format: :json) %>">
19
+ <%%= form.hidden_field :credential, data: { webauthn_target: "field" } %>
20
+ <%%= form.button "Create passkey", type: "button",
21
+ data: { webauthn_target: "button", action: "webauthn#createCredential" } %>
22
+ <p data-webauthn-target="error" style="color: red" hidden></p>
23
+ </div>
24
+ <%% end %>
@@ -0,0 +1,21 @@
1
+ <h1>Choose a password</h1>
2
+
3
+ <%% if @registration.errors.any? %>
4
+ <ul style="color: red">
5
+ <%% @registration.errors.full_messages.each do |message| %>
6
+ <li><%%= message %></li>
7
+ <%% end %>
8
+ </ul>
9
+ <%% end %>
10
+
11
+ <%%= form_with url: <%= identity.route_scope %>_registration_password_path do |form| %>
12
+ <div>
13
+ <%%= form.label :password %><br>
14
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
15
+ </div>
16
+ <div>
17
+ <%%= form.label :password_confirmation %><br>
18
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
19
+ </div>
20
+ <div><%%= form.submit "Finish setting up my account" %></div>
21
+ <%% end %>
@@ -0,0 +1,21 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Finish setting up your account</h1>
4
+
5
+ <%- if pre_account_password_enrollment? -%>
6
+ <p><%%= link_to "Choose a password", <%= identity.route_scope %>_new_registration_password_path %></p>
7
+ <%- end -%>
8
+ <%- if pre_account_passkey_enrollment? -%>
9
+ <p><%%= link_to "Create a passkey", <%= identity.route_scope %>_new_registration_passkey_path %></p>
10
+ <%- end -%>
11
+ <%- if pre_account_provider_enrollment? -%>
12
+ <%%# Connecting a provider is the only way to finish this sign-up, so add a button
13
+ per configured provider here, each with ?registration_intent=1 in its URL as below. %>
14
+ <%% if Rails.env.development? %>
15
+ <fieldset style="color: gray; margin-top: 1em;">
16
+ <legend><em>Development</em></legend>
17
+ <%%# OmniAuth returns query params, as request.env["omniauth.params"]. %>
18
+ <%%= button_to "Continue with Developer", "<%= omniauth_path_prefix %>/developer?registration_intent=1", data: { turbo: false } %>
19
+ </fieldset>
20
+ <%% end %>
21
+ <%- end -%>
@@ -0,0 +1,9 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Confirm your email</h1>
5
+
6
+ <p>Confirm <%%= @<%= identity.singular %>.pending_email %> as your new email address.</p>
7
+
8
+ <%%# Confirm with a non-GET request so mail scanners cannot apply the change. %>
9
+ <%%= button_to "Confirm my email", <%= identity.route_scope %>_verify_email_change_path(sid: params[:sid]), method: :patch %>
@@ -0,0 +1,9 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Sign in</h1>
5
+
6
+ <p>Your sign-in link is ready.</p>
7
+
8
+ <%%# Keep session creation behind a non-GET request because mail scanners and link previewers may follow the link. %>
9
+ <%%= button_to "Sign me in", <%= identity.route_scope %>_magic_link_path(sid: params[:sid]), method: :patch %>
@@ -0,0 +1,22 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+ <p style="color: red"><%%= alert %></p>
3
+
4
+ <h1>Sign in with a magic link</h1>
5
+
6
+ <p>Enter your email and we'll send you a link to sign in without a password.</p>
7
+
8
+ <%%= form_with url: <%= identity.route_scope %>_magic_link_path, method: :post<%= captcha_form_options %> do |form| %>
9
+ <div>
10
+ <%%= form.label :email %><br>
11
+ <%%= form.email_field :email, required: true, autofocus: true, autocomplete: "email" %>
12
+ </div>
13
+
14
+ <%- if captchable? -%>
15
+
16
+ <%%= render "shared/captcha" %>
17
+ <%- end -%>
18
+
19
+ <div><%%= form.submit "Send magic link" %></div>
20
+ <%% end %>
21
+
22
+ <p><%%= link_to "Back to sign in", <%= identity.route_scope %>_sign_in_path %></p>
@@ -0,0 +1,20 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Enter a recovery code</h1>
4
+
5
+ <p>Enter one of the recovery codes you saved when you set up two-factor authentication.</p>
6
+
7
+ <%%= form_with url: <%= identity.route_scope %>_mfa_challenge_recovery_codes_path, method: :post do |form| %>
8
+ <div>
9
+ <%%= form.label :code %><br>
10
+ <%%= form.text_field :code, required: true, autofocus: true %>
11
+ </div>
12
+
13
+ <div><%%= form.submit "Verify" %></div>
14
+ <%% end %>
15
+
16
+ <%- second_factor_alternatives(:recovery_codes).each do |clause, label, path| -%>
17
+ <%% if <%= clause %> %>
18
+ <p><%%= link_to "<%= label %>", <%= path %> %></p>
19
+ <%% end %>
20
+ <%- end -%>
@@ -0,0 +1,17 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Use your security key</h1>
4
+
5
+ <div data-controller="webauthn"
6
+ data-webauthn-challenge-url-value="<%%= new_<%= identity.route_scope %>_mfa_challenge_security_keys_path(format: :json) %>"
7
+ data-webauthn-verification-url-value="<%%= <%= identity.route_scope %>_mfa_challenge_security_keys_path %>">
8
+ <button data-webauthn-target="button" data-action="webauthn#getCredential">Use security key</button>
9
+ <p data-webauthn-target="error" style="color: red" hidden></p>
10
+ </div>
11
+
12
+ <%- second_factor_alternatives(:security_keys).each do |clause, label, path| -%>
13
+ <%% if <%= clause %> %>
14
+ <p><%%= link_to "<%= label %>", <%= path %> %></p>
15
+ <%% end %>
16
+ <%- end -%>
17
+ <p><%%= link_to "Use a recovery code instead", new_<%= identity.route_scope %>_mfa_challenge_recovery_codes_path %></p>