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,45 @@
1
+ class <%= identity.module_name %>::MultiFactorAuthentication::Challenge::SmsController < <%= identity.controller_superclass %>
2
+ require_unauthenticated_access
3
+ rate_limit(**<%= identity.config_reference %>.rate_limits.sms_second_factor, only: :create,
4
+ with: -> { redirect_to new_<%= identity.route_scope %>_mfa_challenge_sms_path, alert: "Too many codes requested. Please try again later." })
5
+ before_action :set_user
6
+ before_action :require_sms_enrolled
7
+
8
+ def new
9
+ @user.issue_sms_challenge unless @user.sms_challenge?
10
+ end
11
+
12
+ def create
13
+ @user.issue_sms_challenge
14
+
15
+ redirect_to new_<%= identity.route_scope %>_mfa_challenge_sms_path, notice: "We sent you another code."
16
+ end
17
+
18
+ def update
19
+ unless @user.verify_sms_challenge(params[:code])
20
+ flash.now[:alert] = "That code is incorrect or has expired."
21
+ return render :new, status: :unprocessable_entity
22
+ end
23
+
24
+ <%- if rememberable? -%>
25
+ start_new_session_for(@user, via: :second_factor, remember: session.delete(:<%= session_key(:remember_me) %>))
26
+ <%- else -%>
27
+ start_new_session_for(@user, via: :second_factor)
28
+ <%- end -%>
29
+ redirect_to after_authentication_url, status: :see_other
30
+ end
31
+
32
+ private
33
+
34
+ def set_user
35
+ @user = <%= identity.class_name %>.find_by_token_for!(:authentication_challenge, session[:<%= session_key(:challenge_token) %>])
36
+ rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveRecord::RecordNotFound
37
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: "Your sign-in challenge has expired. Please sign in again."
38
+ end
39
+
40
+ def require_sms_enrolled
41
+ return if @user.sms_enrolled_at?
42
+
43
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: "Please sign in again."
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ class <%= identity.module_name %>::MultiFactorAuthentication::Challenge::TotpsController < <%= identity.controller_superclass %>
2
+ require_unauthenticated_access
3
+ rate_limit(**<%= identity.config_reference %>.rate_limits.totp_challenge, only: :create,
4
+ with: -> { redirect_to new_<%= identity.route_scope %>_mfa_challenge_totp_path, alert: "Too many attempts. Please try again later." })
5
+ before_action :set_user
6
+ before_action :require_totp_enrolled
7
+
8
+ def new
9
+ end
10
+
11
+ def create
12
+ totp = ROTP::TOTP.new(@user.totp_secret, issuer: <%= identity.config_reference %>.app_name)
13
+ used_at = totp.verify(params[:code], drift_behind: <%= identity.config_reference %>.second_factor.totp_drift_behind,
14
+ after: @user.totp_last_used_at)
15
+
16
+ if used_at
17
+ @user.update!(totp_last_used_at: Time.at(used_at))
18
+ <%- if rememberable? -%>
19
+ start_new_session_for(@user, via: :second_factor, remember: session.delete(:<%= session_key(:remember_me) %>))
20
+ <%- else -%>
21
+ start_new_session_for(@user, via: :second_factor)
22
+ <%- end -%>
23
+ redirect_to after_authentication_url, status: :see_other
24
+ else
25
+ redirect_to new_<%= identity.route_scope %>_mfa_challenge_totp_path, alert: "That code is incorrect."
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def set_user
32
+ @user = <%= identity.class_name %>.find_by_token_for!(:authentication_challenge, session[:<%= session_key(:challenge_token) %>])
33
+ rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveRecord::RecordNotFound
34
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: "Your sign-in challenge has expired. Please sign in again."
35
+ end
36
+
37
+ def require_totp_enrolled
38
+ return if @user.totp_enrolled_at?
39
+
40
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: "Please sign in again."
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ class <%= identity.module_name %>::Omniauth::RegistrationsController < <%= identity.controller_superclass %>
2
+ include <%= identity.authentication_concern %>::Registering
3
+
4
+ require_unauthenticated_access
5
+ before_action :set_registration
6
+
7
+ def new
8
+ end
9
+
10
+ def create
11
+ return render :new, status: :unprocessable_entity unless @registration.update(profile_params)
12
+
13
+ advance(@registration)
14
+ end
15
+
16
+ private
17
+
18
+ def set_registration
19
+ @registration = <%= identity.pending_registration_class %>.unexpired.find_by(id: session[:<%= pending_registration_session_key %>])
20
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: "That took too long. Please sign in again." unless @registration&.provider
21
+ end
22
+
23
+ def profile_params
24
+ params.permit(<%= omniauth_registration_permits %>)
25
+ end
26
+ end
@@ -0,0 +1,139 @@
1
+ class <%= identity.module_name %>::OmniauthController < <%= identity.controller_superclass %>
2
+ <%- if pending_registration_holds_provider? -%>
3
+ include <%= identity.authentication_concern %>::Registering
4
+
5
+ <%- end -%>
6
+ allow_unauthenticated_access
7
+ skip_before_action :verify_authenticity_token
8
+
9
+ def create
10
+ identity = <%= identity.omniauth_identity_class %>.find_by(provider: omniauth.provider, uid: omniauth.uid)
11
+
12
+ return link_identity(identity) if authenticated?
13
+ return sign_in(identity) if identity
14
+ <%- if pre_account_provider_enrollment? -%>
15
+ return complete_pending_registration if request.env.dig("omniauth.params", "registration_intent")
16
+ <%- end -%>
17
+
18
+ register
19
+ end
20
+
21
+ def failure
22
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: params[:message]
23
+ end
24
+
25
+ private
26
+
27
+ def sign_in(identity)
28
+ user = identity.<%= identity.singular %>
29
+ <%= sign_in_call("user", via: "identity.via") %>
30
+ redirect_to after_authentication_url, status: :see_other
31
+ end
32
+ <%- if pre_account_provider_enrollment? -%>
33
+
34
+ def complete_pending_registration
35
+ @registration = <%= identity.pending_registration_class %>.unexpired.find_by(id: session[:<%= pending_registration_session_key %>])
36
+ unless @registration
37
+ return redirect_to <%= identity.route_scope %>_sign_up_path, alert: "That sign-up has expired. Please start again."
38
+ end
39
+
40
+ @registration.update!(provider: omniauth.provider, uid: omniauth.uid)
41
+ advance(@registration)
42
+ end
43
+
44
+ def render_invalid_registration
45
+ redirect_to <%= credential_enrollment_path %>, alert: @registration.errors.full_messages.to_sentence
46
+ end
47
+ <%- end -%>
48
+
49
+ def link_identity(existing)
50
+ if existing && existing.<%= identity.foreign_key %> != <%= identity.current_class %>.user.id
51
+ return redirect_to <%= identity.settings_helper_prefix %>_omniauth_identities_path,
52
+ alert: "That #{provider_name} account is already linked to another account."
53
+ end
54
+
55
+ if existing
56
+ return redirect_to <%= identity.settings_helper_prefix %>_omniauth_identities_path,
57
+ notice: "That account is already linked."
58
+ end
59
+
60
+ <%= identity.current_class %>.user.omniauth_identities.create!(provider: omniauth.provider, uid: omniauth.uid)
61
+ <%- if security_notifications? -%>
62
+ <%= identity.mailer_class %>.send_security_notification(:connected_account_linked)
63
+ <%- end -%>
64
+ redirect_to <%= identity.settings_helper_prefix %>_omniauth_identities_path, notice: "Linked.", status: :see_other
65
+ end
66
+
67
+ <%- if email_verifiable? -%>
68
+ # A provider's email address is taken as verified. Nothing reverifies it.
69
+ <%- end -%>
70
+ def register
71
+ <%- if principals.email&.required? -%>
72
+ return redirect_to <%= identity.route_scope %>_sign_in_path, alert: no_address_alert if omniauth.info.email.blank?
73
+
74
+ <%- end -%>
75
+ <%- if invite_only? -%>
76
+ unless pending_invitation_for(omniauth.info.email)
77
+ return redirect_to <%= identity.route_scope %>_sign_in_path, alert: "Registration is invite-only."
78
+ end
79
+
80
+ <%- end -%>
81
+ <%- if omniauth_registration? -%>
82
+ registration = <%= identity.pending_registration_class %>.new(provider: omniauth.provider, uid: omniauth.uid<%= ", email: omniauth.info.email" if principals.email %>)
83
+ <%- if email_verifiable? -%>
84
+ registration.email_verified_at = Time.current if registration.email.present?
85
+ <%- end -%>
86
+ <%- if invitable? -%>
87
+ registration.invitation = pending_invitation_for(omniauth.info.email)
88
+ <%- end -%>
89
+ registration.save!(validate: false)
90
+ session[:<%= pending_registration_session_key %>] = registration.id
91
+
92
+ advance(registration)
93
+ <%- else -%>
94
+ # Do not look up an account by the provider-reported email: it is a claim and could link an attacker to another account.
95
+ user = <%= identity.class_name %>.new<%= "(email: omniauth.info.email)" if principals.email %>
96
+ user.omniauth_identities.build(provider: omniauth.provider, uid: omniauth.uid)
97
+
98
+ case save_account(user)
99
+ when :ok
100
+ <%- if invitable? -%>
101
+ pending_invitation_for(omniauth.info.email)&.accept(user)
102
+ <%- end -%>
103
+ <%= sign_in_call("user", via: "provider_via") %>
104
+ redirect_to after_authentication_url, status: :see_other
105
+ when :taken
106
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: taken_alert
107
+ else
108
+ redirect_to <%= identity.route_scope %>_sign_in_path, alert: no_address_alert
109
+ end
110
+ <%- end -%>
111
+ end
112
+ <%- if !omniauth_registration? -%>
113
+
114
+ def save_account(user)
115
+ return :ok if user.save
116
+
117
+ user.errors.of_kind?(:email, :taken) ? :taken : :invalid
118
+ rescue ActiveRecord::RecordNotUnique
119
+ :taken
120
+ end
121
+
122
+ def taken_alert
123
+ "An account already uses that email address. Sign in, then link #{provider_name} from your settings."
124
+ end
125
+ <%- end -%>
126
+ <%- if !omniauth_registration? || principals.email&.required? -%>
127
+
128
+ def no_address_alert
129
+ "That provider didn't share an email address, try signing up another way."
130
+ end
131
+ <%- end -%>
132
+
133
+ def provider_via = <%= identity.omniauth_identity_class %>.via(omniauth.provider)
134
+ def provider_name = provider_via.to_s.titleize
135
+
136
+ def omniauth
137
+ request.env["omniauth.auth"]
138
+ end
139
+ end
@@ -0,0 +1,18 @@
1
+ class <%= identity.module_name %>::Passkeys::SessionsController < <%= identity.controller_superclass %>
2
+ require_unauthenticated_access
3
+
4
+ def new
5
+ # Omit an allow-list so discoverable passkeys can identify their owner; require user verification because this is passwordless sign-in.
6
+ issue_webauthn_challenge(WebAuthn::Credential.options_for_get(user_verification: "required"))
7
+ end
8
+
9
+ def create
10
+ passkey = verify_webauthn_assertion(params.expect(credential: {}), from: <%= identity.webauthn_credential_class %><%= ".passkeys" if passkey_and_security_keys? %>)
11
+ user = passkey.<%= identity.singular %>
12
+
13
+ <%= sign_in_call("user", via: :passkey) %>
14
+ render json: { status: "ok", location: after_authentication_url }, status: :created
15
+ rescue WebAuthn::Error, ActiveRecord::RecordNotFound
16
+ render json: { error: "We couldn't verify that passkey. Please try again." }, status: :unprocessable_entity
17
+ end
18
+ end
@@ -0,0 +1,64 @@
1
+ class <%= identity.module_name %>::PasswordResetsController < <%= identity.controller_superclass %>
2
+ require_unauthenticated_access
3
+ rate_limit(**<%= identity.config_reference %>.rate_limits.password_reset, only: :create,
4
+ with: -> { redirect_to root_path, alert: "Too many reset links requested. Please try again later." })
5
+ before_action :set_user, only: %i[edit update]
6
+
7
+ def new
8
+ end
9
+
10
+ def edit
11
+ end
12
+
13
+ def create
14
+ <%- if captchable? -%>
15
+ unless captcha_verified?
16
+ flash.now[:alert] = "Captcha verification failed. Please try again."
17
+ return render :new, status: :unprocessable_entity
18
+ end
19
+
20
+ <%- end -%>
21
+ <%- if coy? -%>
22
+ <%= deferred_call(identity.class_name, :request_password_reset, "params[:email].to_s") %>
23
+ redirect_to <%= identity.route_scope %>_sign_in_path, notice: "Check your email for password reset instructions."
24
+ <%- else -%>
25
+ if <%= identity.class_name %>.request_password_reset(params[:email])
26
+ redirect_to <%= identity.route_scope %>_sign_in_path, notice: "Check your email for password reset instructions."
27
+ else
28
+ redirect_to <%= identity.route_scope %>_forgot_password_path, alert: "No<%= email_verifiable? ? " verified" : "" %> account was found for that email."
29
+ end
30
+ <%- end -%>
31
+ end
32
+
33
+ def update
34
+ if @user.update(user_params)
35
+ <%- if deadboltable? -%>
36
+ @user.undeadbolt!
37
+ <%- end -%>
38
+ <%- if security_notifications? -%>
39
+ <%= identity.mailer_class %>.send_security_notification(:password_reset, to: @user.email)
40
+ <%- end -%>
41
+ redirect_to <%= identity.route_scope %>_sign_in_path, notice: "Your password has been reset. Please sign in.",
42
+ status: :see_other
43
+ else
44
+ render :edit, status: :unprocessable_entity
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def set_user
51
+ @user = <%= identity.class_name %>.find_by_token_for(:password_reset, params.expect(:sid))
52
+
53
+ redirect_to_rejection unless @user
54
+ end
55
+
56
+ def redirect_to_rejection
57
+ redirect_to <%= identity.route_scope %>_forgot_password_path, alert: "That password reset link is invalid or has expired.",
58
+ status: :see_other
59
+ end
60
+
61
+ def user_params
62
+ params.permit(:password, :password_confirmation)
63
+ end
64
+ end
@@ -0,0 +1,53 @@
1
+ class <%= identity.module_name %>::Registrations::EmailVerificationsController < <%= identity.controller_superclass %>
2
+ include <%= identity.authentication_concern %>::Registering
3
+
4
+ allow_unauthenticated_access
5
+ rate_limit(**<%= identity.config_reference %>.rate_limits.email_verification, only: :create,
6
+ with: -> { redirect_to <%= identity.route_scope %>_new_verify_email_path, alert: "Too many links requested. Please try again later." })
7
+ before_action :set_pending_registration, only: [ :new, :create ]
8
+ before_action :set_claimed_registration, only: [ :show, :update ]
9
+
10
+ def new
11
+ end
12
+
13
+ def create
14
+ <%= deferred_call("@registration", :request_email_verification) %>
15
+ redirect_to <%= identity.route_scope %>_new_verify_email_path, notice: "We sent you another link."
16
+ end
17
+
18
+ def show
19
+ end
20
+
21
+ def update
22
+ <%- if principals.username -%>
23
+ @registration.username = params[:username] if params[:username]
24
+ <%- end -%>
25
+ @registration.email_verified_at = Time.current
26
+ return render :show, status: :unprocessable_entity unless @registration.save
27
+
28
+ advance(@registration)
29
+ end
30
+
31
+ private
32
+
33
+ def set_pending_registration
34
+ @registration = <%= identity.pending_registration_class %>.unexpired.find_by(id: session[:<%= pending_registration_session_key %>])
35
+ redirect_to <%= registration_entry_path %> unless @registration
36
+ end
37
+
38
+ def set_claimed_registration
39
+ @registration = <%= identity.pending_registration_class %>.unexpired.find_by_token_for(
40
+ :registration_email_verification, params.expect(:sid)
41
+ )
42
+
43
+ redirect_to_rejection unless @registration && session[:<%= pending_registration_session_key %>] == @registration.id
44
+ end
45
+
46
+ def render_invalid_registration
47
+ render :show, status: :unprocessable_entity
48
+ end
49
+
50
+ def redirect_to_rejection
51
+ redirect_to <%= registration_entry_path %>, alert: "That confirmation link is invalid or has expired."
52
+ end
53
+ end
@@ -0,0 +1,37 @@
1
+ class <%= identity.module_name %>::Registrations::PhoneVerificationsController < <%= identity.controller_superclass %>
2
+ include <%= identity.authentication_concern %>::Registering
3
+
4
+ allow_unauthenticated_access
5
+ rate_limit(**<%= identity.config_reference %>.rate_limits.phone_verification, only: :create,
6
+ with: -> { redirect_to <%= identity.route_scope %>_verify_phone_path, alert: "Too many codes requested. Please try again later." })
7
+ before_action :set_pending_registration
8
+
9
+ def new
10
+ end
11
+
12
+ def create
13
+ <%= deferred_call("@registration", :request_phone_verification) %>
14
+ redirect_to <%= identity.route_scope %>_verify_phone_path, notice: "We sent you another code."
15
+ end
16
+
17
+ def update
18
+ <%- if principals.username -%>
19
+ @registration.username = params[:username] if params[:username]
20
+ return render :new, status: :unprocessable_entity unless @registration.valid?
21
+ <%- end -%>
22
+ unless @registration.verify_sms_challenge(params[:code])
23
+ flash.now[:alert] = "That code is incorrect or has expired."
24
+ return render :new, status: :unprocessable_entity
25
+ end
26
+
27
+ @registration.update!(phone_verified_at: Time.current)
28
+ advance(@registration)
29
+ end
30
+
31
+ private
32
+
33
+ def set_pending_registration
34
+ @registration = <%= identity.pending_registration_class %>.unexpired.find_by(id: session[:<%= pending_registration_session_key %>])
35
+ redirect_to <%= registration_entry_path %> unless @registration
36
+ end
37
+ end
@@ -0,0 +1,112 @@
1
+ class <%= identity.module_name %>::RegistrationsController < <%= identity.controller_superclass %>
2
+ <%- if typed_staged_registration? -%>
3
+ include <%= identity.authentication_concern %>::Registering
4
+
5
+ <%- end -%>
6
+ require_unauthenticated_access
7
+ rate_limit(**<%= identity.config_reference %>.rate_limits.sign_up, only: :create,
8
+ with: -> { redirect_to <%= identity.route_scope %>_sign_up_path, alert: "Too many sign-up attempts. Please try again later." })
9
+ <%- if invite_only? -%>
10
+ before_action :require_invitation_to_register
11
+ <%- end -%>
12
+
13
+ def new
14
+ <%- if invitable? -%>
15
+ @pending_invitation = pending_invitation
16
+ <%- end -%>
17
+ <%- if typed_staged_registration? -%>
18
+ @registration = <%= identity.pending_registration_class %>.new
19
+ <%- else -%>
20
+ @user = <%= identity.class_name %>.new
21
+ <%- end -%>
22
+ end
23
+
24
+ <%- if typed_staged_registration? -%>
25
+ def create
26
+ @registration = <%= identity.pending_registration_class %>.new(registration_params)
27
+ <%- if captchable? -%>
28
+ @registration.validate
29
+ @registration.errors.add(:base, "Captcha verification failed. Please try again.") unless captcha_verified?
30
+ return render :new, status: :unprocessable_entity if @registration.errors.any?
31
+ <%- else -%>
32
+ return render :new, status: :unprocessable_entity unless @registration.valid?
33
+ <%- end -%>
34
+ <%- if !coy? -%>
35
+
36
+ if (taken = taken_identifier)
37
+ @registration.errors.add(taken, "has already been taken")
38
+ return render :new, status: :unprocessable_entity
39
+ end
40
+ <%- end -%>
41
+
42
+ @registration.save!
43
+ session[:<%= pending_registration_session_key %>] = @registration.id
44
+
45
+ advance(@registration)
46
+ end
47
+
48
+ private
49
+ <%- if !coy? -%>
50
+
51
+ def taken_identifier
52
+ <%- if principals.email -%>
53
+ return :email if @registration.email.present? && <%= identity.class_name %>.exists?(email: @registration.email)
54
+ <%- end -%>
55
+ <%- if principals.phone -%>
56
+ return :phone if @registration.phone.present? && <%= identity.class_name %>.exists?(phone: @registration.phone)
57
+ <%- end -%>
58
+
59
+ nil
60
+ end
61
+ <%- end -%>
62
+
63
+ def registration_params
64
+ <%= sign_up_params_expression %>
65
+ end
66
+ <%- else -%>
67
+ def create
68
+ @user = <%= identity.class_name %>.new(user_params)
69
+ <%- if captchable? -%>
70
+ @user.validate
71
+ @user.errors.add(:base, "Captcha verification failed. Please try again.") unless captcha_verified?
72
+ return render :new, status: :unprocessable_entity if @user.errors.any?
73
+ <%- else -%>
74
+ return render :new, status: :unprocessable_entity unless @user.valid?
75
+ <%- end -%>
76
+
77
+ if @user.save
78
+ <%- if invitable? -%>
79
+ pending_invitation&.accept(@user)
80
+ <%- end -%>
81
+ <%= sign_in_call("@user", via: :registration, may_challenge: false) %>
82
+ redirect_to after_authentication_url, status: :see_other
83
+ else
84
+ render :new, status: :unprocessable_entity
85
+ end
86
+ rescue ActiveRecord::RecordNotUnique
87
+ <%- if principals.email -%>
88
+ @user.errors.add(:email, "has already been taken")
89
+ <%- elsif principals.phone -%>
90
+ @user.errors.add(:phone, "has already been taken")
91
+ <%- else -%>
92
+ @user.errors.add(:base, "That account already exists")
93
+ <%- end -%>
94
+ render :new, status: :unprocessable_entity
95
+ end
96
+
97
+ private
98
+
99
+ def user_params
100
+ <%= sign_up_params_expression %>
101
+ end
102
+ <%- end -%>
103
+ <%- if invitable? -%>
104
+
105
+ def with_invitation(permitted)
106
+ invitation = pending_invitation
107
+ return permitted unless invitation
108
+
109
+ permitted.merge(<%= invitation_channel_pair %><%= ", invitation:" if typed_staged_registration? %>)
110
+ end
111
+ <%- end -%>
112
+ end
@@ -0,0 +1,52 @@
1
+ class <%= identity.module_name %>::SessionsController < <%= identity.controller_superclass %>
2
+ require_unauthenticated_access except: [ :destroy ]
3
+ <%- if impeded? -%>
4
+ skip_impediments only: :destroy
5
+ <%- end -%>
6
+ <%- if password? -%>
7
+ rate_limit(**<%= identity.config_reference %>.rate_limits.sign_in, only: :create,
8
+ with: -> { redirect_to <%= identity.route_scope %>_sign_in_path, alert: "Too many sign-in attempts. Please try again later." })
9
+ <%- end -%>
10
+
11
+ def new
12
+ end
13
+
14
+ <%- if password? -%>
15
+ def create
16
+ <%- if multi_login? -%>
17
+ identifier = params[:login].to_s.strip
18
+
19
+ <%- end -%>
20
+ <%- if captchable? -%>
21
+ unless captcha_verified?
22
+ return redirect_to <%= identity.route_scope %>_sign_in_path(<%= login_hint_param %>: <%= login_identifier_expr %>),
23
+ alert: "Captcha verification failed. Please try again."
24
+ end
25
+
26
+ <%- end -%>
27
+ user = <%= identity.class_name %>.authenticate_with_password(<%= login_identifier_expr %>, params[:password])
28
+
29
+ unless user
30
+ <%- if deadboltable? -%>
31
+ <%= failed_sign_in_lookup %>
32
+ return redirect_to <%= identity.route_scope %>_sign_in_path(<%= login_hint_param %>: <%= login_identifier_expr %>),
33
+ alert: <%= failed_sign_in_alert %>
34
+ <%- else -%>
35
+ return redirect_to <%= identity.route_scope %>_sign_in_path(<%= login_hint_param %>: <%= login_identifier_expr %>), alert: "<%= incorrect_login_message %>."
36
+ <%- end -%>
37
+ end
38
+ <%- if deadboltable? -%>
39
+
40
+ user.reset_failed_attempts!
41
+ <%- end -%>
42
+
43
+ <%= sign_in_call("user", via: :password, remember_expr: 'params[:remember_me] == "1"') %>
44
+ redirect_to after_authentication_url, status: :see_other
45
+ end
46
+
47
+ <%- end -%>
48
+ def destroy
49
+ terminate_current_session
50
+ redirect_to root_url, notice: "Signed out.", status: :see_other
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ class <%= identity.module_name %>::SmsSessionsController < <%= identity.controller_superclass %>
2
+ allow_unauthenticated_access
3
+ rate_limit(**<%= identity.config_reference %>.rate_limits.sms_sign_in, only: :create,
4
+ with: -> { redirect_to <%= identity.route_scope %>_sms_sign_in_path, alert: "Too many codes requested. Please try again later." })
5
+ before_action :set_pending_user, only: [ :edit, :update ]
6
+
7
+ SESSION_KEY = :pending_sms_sign_in
8
+
9
+ def new
10
+ end
11
+
12
+ def create
13
+ <%- if captchable? -%>
14
+ unless captcha_verified?
15
+ flash.now[:alert] = "Captcha verification failed. Please try again."
16
+ return render :new, status: :unprocessable_entity
17
+ end
18
+
19
+ <%- end -%>
20
+ <%- if coy? -%>
21
+ <%= deferred_call(identity.class_name, :issue_sms_sign_in_code, "params[:phone].to_s") %>
22
+ session[SESSION_KEY] = params[:phone].to_s
23
+ redirect_to <%= identity.route_scope %>_sms_sign_in_code_path, notice: "We sent a code if that number has an account."
24
+ <%- else -%>
25
+ unless <%= identity.class_name %>.issue_sms_sign_in_code(params[:phone])
26
+ return redirect_to <%= identity.route_scope %>_sms_sign_in_path, alert: "No account was found for that number."
27
+ end
28
+
29
+ session[SESSION_KEY] = params[:phone].to_s
30
+ redirect_to <%= identity.route_scope %>_sms_sign_in_code_path, notice: "We sent a code to that number."
31
+ <%- end -%>
32
+ end
33
+
34
+ def edit
35
+ end
36
+
37
+ def update
38
+ unless (@user || <%= identity.class_name %>.new).verify_sms_challenge(params[:code])
39
+ flash.now[:alert] = "That code is incorrect or has expired."
40
+ return render :edit, status: :unprocessable_entity
41
+ end
42
+
43
+ session.delete(SESSION_KEY)
44
+ <%= sign_in_call("@user", via: :sms_code) %>
45
+ redirect_to after_authentication_url, status: :see_other
46
+ end
47
+
48
+ private
49
+
50
+ def set_pending_user
51
+ @user = <%= identity.class_name %>.find_by(phone: session[SESSION_KEY])
52
+ end
53
+ end