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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +384 -0
- data/lib/authnz_eleven/identity.rb +210 -0
- data/lib/authnz_eleven/principal.rb +201 -0
- data/lib/authnz_eleven/principals.rb +111 -0
- data/lib/authnz_eleven/version.rb +5 -0
- data/lib/authnz_eleven/wizard.rb +570 -0
- data/lib/authnz_eleven.rb +15 -0
- data/lib/generators/authnz_eleven/USAGE +26 -0
- data/lib/generators/authnz_eleven/authnz_eleven_generator.rb +3040 -0
- data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.LICENSE +22 -0
- data/lib/generators/authnz_eleven/templates/config/forbidden_usernames.txt +558 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/authentication.rb.tt +275 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/omniauth.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/team_slug.rb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/config/initializers/webauthn.rb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/config/locales/authentication.en.yml.tt +11 -0
- data/lib/generators/authnz_eleven/templates/controllers/active_teams_controller.rb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin/dashboard_controller.rb.tt +4 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin/sessions_controller.rb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin/users_controller.rb.tt +91 -0
- data/lib/generators/authnz_eleven/templates/controllers/admin_controller.rb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/controllers/base_controller.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/api_token_authentication.rb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/easy_dev_login.rb.tt +47 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/registering.rb.tt +25 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/sudoable.rb.tt +117 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication/webauthn_ceremony.rb.tt +51 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authentication.rb.tt +421 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/authorization.rb.tt +48 -0
- data/lib/generators/authnz_eleven/templates/controllers/concerns/captchable.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/controllers/impersonations_controller.rb.tt +8 -0
- data/lib/generators/authnz_eleven/templates/controllers/invitations_controller.rb.tt +65 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/api_tokens_controller.rb.tt +54 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/authentications/events_controller.rb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/dashboard_controller.rb.tt +4 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/email_verifications_controller.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/emails_controller.rb.tt +93 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/invitations_controller.rb.tt +61 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/authenticators_controller.rb.tt +87 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/recovery_codes_controller.rb.tt +27 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/security_keys_controller.rb.tt +86 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/multi_factor_authentication/sms_controller.rb.tt +84 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/omniauth_identities_controller.rb.tt +27 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/passkeys_controller.rb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/passwords_controller.rb.tt +74 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/phone_verifications_controller.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/phones_controller.rb.tt +88 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/sessions_controller.rb.tt +29 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings/users_controller.rb.tt +42 -0
- data/lib/generators/authnz_eleven/templates/controllers/settings_controller.rb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/controllers/teams_controller.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passkeys_controller.rb.tt +36 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments/passwords_controller.rb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/credential_enrollments_controller.rb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/easy_dev_logins_controller.rb.tt +19 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/email_verifications_controller.rb.tt +36 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/magic_link_controller.rb.tt +53 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/recovery_codes_controller.rb.tt +30 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/security_keys_controller.rb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/sms_controller.rb.tt +45 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/multi_factor_authentication/challenge/totps_controller.rb.tt +42 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/omniauth/registrations_controller.rb.tt +26 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/omniauth_controller.rb.tt +139 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/passkeys/sessions_controller.rb.tt +18 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/password_resets_controller.rb.tt +64 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/registrations/email_verifications_controller.rb.tt +53 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/registrations/phone_verifications_controller.rb.tt +37 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/registrations_controller.rb.tt +112 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/sessions_controller.rb.tt +52 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/sms_sessions_controller.rb.tt +53 -0
- data/lib/generators/authnz_eleven/templates/controllers/users/sudos_controller.rb.tt +42 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/dashboard/index.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/sessions/index.html.erb.tt +27 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/users/index.html.erb.tt +32 -0
- data/lib/generators/authnz_eleven/templates/erb/admin/users/show.html.erb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/erb/invitations/show.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/create.html.erb.tt +7 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/api_tokens/index.html.erb.tt +29 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/authentications/events/index.html.erb.tt +18 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/dashboard/index.html.erb.tt +75 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/emails/edit.html.erb.tt +68 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/invitations/new.html.erb.tt +31 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/authenticators/new.html.erb.tt +57 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/create.html.erb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/recovery_codes/index.html.erb.tt +13 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/edit.html.erb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/index.html.erb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/security_keys/new.html.erb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/multi_factor_authentication/sms/new.html.erb.tt +35 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/omniauth_identities/index.html.erb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/edit.html.erb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/index.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passkeys/new.html.erb.tt +13 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/passwords/edit.html.erb.tt +68 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/phone_verifications/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/phones/edit.html.erb.tt +58 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/sessions/index.html.erb.tt +29 -0
- data/lib/generators/authnz_eleven/templates/erb/settings/users/show.html.erb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_nav.html.erb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/erb/shared/_authnz_eleven_sudo_challenge.html.erb.tt +43 -0
- data/lib/generators/authnz_eleven/templates/erb/shared/_captcha.html.erb.tt +8 -0
- data/lib/generators/authnz_eleven/templates/erb/teams/index.html.erb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/erb/teams/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/teams/show.html.erb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/email_change_verification.html.erb.tt +7 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/invitation.html.erb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/magic_link.html.erb.tt +5 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/password_reset.html.erb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/registration_email_verification.html.erb.tt +7 -0
- data/lib/generators/authnz_eleven/templates/erb/user_mailer/security_notification.html.erb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passkeys/new.html.erb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/passwords/new.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/users/credential_enrollments/show.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/users/email_verifications/show.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/users/magic_link/edit.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/erb/users/magic_link/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/recovery_codes/new.html.erb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/security_keys/new.html.erb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/sms/new.html.erb.tt +25 -0
- data/lib/generators/authnz_eleven/templates/erb/users/multi_factor_authentication/challenge/totps/new.html.erb.tt +21 -0
- data/lib/generators/authnz_eleven/templates/erb/users/omniauth/registrations/new.html.erb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/erb/users/password_resets/edit.html.erb.tt +26 -0
- data/lib/generators/authnz_eleven/templates/erb/users/password_resets/new.html.erb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/new.html.erb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/email_verifications/show.html.erb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/new.html.erb.tt +87 -0
- data/lib/generators/authnz_eleven/templates/erb/users/registrations/phone_verifications/new.html.erb.tt +46 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sessions/new.html.erb.tt +88 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/edit.html.erb.tt +30 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sms_sessions/new.html.erb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/erb/users/sudos/new.html.erb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/javascript/controllers/webauthn_controller.js.tt +142 -0
- data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_deferred_job.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/jobs/authnz_eleven_mail_delivery_job.rb.tt +3 -0
- data/lib/generators/authnz_eleven/templates/jobs/sms_delivery_job.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/lib/captcha/null.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/lib/captcha/turnstile.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/lib/captcha.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/lib/sms/log.rb.tt +8 -0
- data/lib/generators/authnz_eleven/templates/lib/sms/twilio.rb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/lib/sms.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/mailers/user_mailer.rb.tt +98 -0
- data/lib/generators/authnz_eleven/templates/migrations/add_guest_to_users_migration.rb.tt +6 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_api_tokens_migration.rb.tt +14 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_events_migration.rb.tt +26 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_invitations_migration.rb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_memberships_migration.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_omniauth_identities_migration.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_password_histories_migration.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_pending_registrations_migration.rb.tt +31 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_recovery_codes_migration.rb.tt +11 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_sessions_migration.rb.tt +19 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_teams_migration.rb.tt +9 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_users_migration.rb.tt +70 -0
- data/lib/generators/authnz_eleven/templates/migrations/create_webauthn_credentials_migration.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/models/api_token.rb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/adminable.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/authenticatable.rb.tt +151 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/bannable.rb.tt +24 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/deadboltable.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/email_verifiable.rb.tt +52 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/eventable.rb.tt +22 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/guestable.rb.tt +30 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/invitable.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/magic_linkable.rb.tt +34 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/password_historical.rb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/password_policy.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/password_rotatable.rb.tt +15 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/phone_verifiable.rb.tt +50 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/principals.rb.tt +67 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/recoverable.rb.tt +19 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/second_factor.rb.tt +52 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/sms_challengeable.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/models/concerns/team_member.rb.tt +20 -0
- data/lib/generators/authnz_eleven/templates/models/current.rb.tt +28 -0
- data/lib/generators/authnz_eleven/templates/models/event.rb.tt +32 -0
- data/lib/generators/authnz_eleven/templates/models/invitation.rb.tt +122 -0
- data/lib/generators/authnz_eleven/templates/models/membership.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/models/omniauth_identity.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/models/password_history.rb.tt +3 -0
- data/lib/generators/authnz_eleven/templates/models/pending_registration.rb.tt +216 -0
- data/lib/generators/authnz_eleven/templates/models/recovery_code.rb.tt +41 -0
- data/lib/generators/authnz_eleven/templates/models/session.rb.tt +46 -0
- data/lib/generators/authnz_eleven/templates/models/team.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/models/user.rb.tt +59 -0
- data/lib/generators/authnz_eleven/templates/models/webauthn_credential.rb.tt +12 -0
- data/lib/generators/authnz_eleven/templates/tasks/authnz_eleven.rake.tt +41 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/account_existence_parity_test.rb.tt +158 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/access_test.rb.tt +139 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/admin/multi_factor_authentication_test.rb.tt +65 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/api_token_authentication_test.rb.tt +45 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/credential_enrollment_test.rb.tt +84 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/guest_access_test.rb.tt +113 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/invitations_controller_test.rb.tt +230 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/passkey_ceremony_test.rb.tt +126 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/password_rotation_test.rb.tt +67 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_key_ceremony_test.rb.tt +218 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/security_notifications_test.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/session_lifecycle_test.rb.tt +102 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/api_tokens_controller_test.rb.tt +61 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/authentications/events_controller_test.rb.tt +17 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/email_verifications_controller_test.rb.tt +39 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/invitations_controller_test.rb.tt +157 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/management_test.rb.tt +256 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/multi_factor_authentication/management_test.rb.tt +222 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/omniauth_identities_controller_test.rb.tt +57 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/settings/phone_verifications_controller_test.rb.tt +146 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/teams_controller_test.rb.tt +83 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/email_verifications_controller_test.rb.tt +74 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/magic_link_controller_test.rb.tt +90 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/multi_factor_authentication/challenge_test.rb.tt +143 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth/registrations_controller_test.rb.tt +138 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/omniauth_controller_test.rb.tt +97 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/password_resets_controller_test.rb.tt +58 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/email_verifications_controller_test.rb.tt +102 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations/phone_verifications_controller_test.rb.tt +69 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/registrations_controller_test.rb.tt +180 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sessions_controller_test.rb.tt +124 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sms_sessions_controller_test.rb.tt +111 -0
- data/lib/generators/authnz_eleven/templates/test_unit/controllers/users/sudos_controller_test.rb.tt +110 -0
- data/lib/generators/authnz_eleven/templates/test_unit/fixtures/memberships.yml.tt +13 -0
- data/lib/generators/authnz_eleven/templates/test_unit/fixtures/teams.yml.tt +2 -0
- data/lib/generators/authnz_eleven/templates/test_unit/fixtures/users.yml.tt +68 -0
- data/lib/generators/authnz_eleven/templates/test_unit/mailers/user_mailer_test.rb.tt +88 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/api_token_test.rb.tt +38 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/password_history_test.rb.tt +51 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/session_test.rb.tt +16 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/sms_challengeable_test.rb.tt +108 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user/guestable_test.rb.tt +78 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user/recovery_codes_test.rb.tt +32 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user/sign_in_methods_test.rb.tt +56 -0
- data/lib/generators/authnz_eleven/templates/test_unit/models/user_test.rb.tt +145 -0
- data/lib/generators/authnz_eleven/wizard_generator.rb +52 -0
- metadata +309 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Two-factor authentication</h1>
|
|
5
|
+
|
|
6
|
+
<p>Type the six-digit code we texted you.</p>
|
|
7
|
+
|
|
8
|
+
<%%= form_with url: <%= identity.route_scope %>_mfa_challenge_sms_path, method: :patch do |form| %>
|
|
9
|
+
<div>
|
|
10
|
+
<%%= form.label :code, "Security code" %><br>
|
|
11
|
+
<%%= form.text_field :code, required: true, autocomplete: "one-time-code",
|
|
12
|
+
inputmode: "numeric", pattern: "[0-9]*", autofocus: true %>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div><%%= form.submit "Verify" %></div>
|
|
16
|
+
<%% end %>
|
|
17
|
+
|
|
18
|
+
<%%= button_to "Didn't get it? Send another code", <%= identity.route_scope %>_mfa_challenge_sms_path %>
|
|
19
|
+
|
|
20
|
+
<p><%%= link_to "Use a recovery code instead", new_<%= identity.route_scope %>_mfa_challenge_recovery_codes_path %></p>
|
|
21
|
+
<%- second_factor_alternatives(:sms).each do |clause, label, path| -%>
|
|
22
|
+
<%% if <%= clause %> %>
|
|
23
|
+
<p><%%= link_to "<%= label %>", <%= path %> %></p>
|
|
24
|
+
<%% end %>
|
|
25
|
+
<%- end -%>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<p style="color: red"><%%= alert %></p>
|
|
2
|
+
|
|
3
|
+
<h1>Two-factor authentication</h1>
|
|
4
|
+
|
|
5
|
+
<p>Enter the 6-digit code from your authenticator app.</p>
|
|
6
|
+
|
|
7
|
+
<%%= form_with url: <%= identity.route_scope %>_mfa_challenge_totp_path, method: :post do |form| %>
|
|
8
|
+
<div>
|
|
9
|
+
<%%= form.label :code %><br>
|
|
10
|
+
<%%= form.text_field :code, required: true, autofocus: true, autocomplete: "one-time-code", inputmode: "numeric" %>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div><%%= form.submit "Verify" %></div>
|
|
14
|
+
<%% end %>
|
|
15
|
+
|
|
16
|
+
<p><%%= link_to "Use a recovery code instead", new_<%= identity.route_scope %>_mfa_challenge_recovery_codes_path %></p>
|
|
17
|
+
<%- second_factor_alternatives(:totp).each do |clause, label, path| -%>
|
|
18
|
+
<%% if <%= clause %> %>
|
|
19
|
+
<p><%%= link_to "<%= label %>", <%= path %> %></p>
|
|
20
|
+
<%% end %>
|
|
21
|
+
<%- end -%>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Finish signing up</h1>
|
|
5
|
+
|
|
6
|
+
<%- if principals.email -%>
|
|
7
|
+
<%% if @registration.email.present? %>
|
|
8
|
+
<p>Signing in as <%%= @registration.email %>. Choose the rest of your details to finish.</p>
|
|
9
|
+
<%% end %>
|
|
10
|
+
<%- end -%>
|
|
11
|
+
|
|
12
|
+
<%% if @registration.errors.any? %>
|
|
13
|
+
<ul style="color: red">
|
|
14
|
+
<%% @registration.errors.full_messages.each do |message| %>
|
|
15
|
+
<li><%%= message %></li>
|
|
16
|
+
<%% end %>
|
|
17
|
+
</ul>
|
|
18
|
+
<%% end %>
|
|
19
|
+
|
|
20
|
+
<%%= form_with url: <%= identity.route_scope %>_omniauth_registration_path do |form| %>
|
|
21
|
+
<%- omniauth_registration_principals.each_with_index do |principal, i| -%>
|
|
22
|
+
<div>
|
|
23
|
+
<%%= form.label :<%= principal.column %> %><br>
|
|
24
|
+
<%%= form.<%= principal.field_helper %> :<%= principal.column %>, value: @registration.<%= principal.column %>, required: true<%= i.zero? ? ", autofocus: true" : "" %>, autocomplete: "<%= principal.autocomplete %>" %>
|
|
25
|
+
</div>
|
|
26
|
+
<%- end -%>
|
|
27
|
+
<div><%%= form.submit "Complete sign up" %></div>
|
|
28
|
+
<%% end %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Reset your password</h1>
|
|
5
|
+
|
|
6
|
+
<%% if @user.errors.any? %>
|
|
7
|
+
<ul style="color: red">
|
|
8
|
+
<%% @user.errors.full_messages.each do |message| %>
|
|
9
|
+
<li><%%= message %></li>
|
|
10
|
+
<%% end %>
|
|
11
|
+
</ul>
|
|
12
|
+
<%% end %>
|
|
13
|
+
|
|
14
|
+
<%%= form_with url: <%= identity.route_scope %>_reset_password_path(sid: params[:sid]), method: :patch do |form| %>
|
|
15
|
+
<div>
|
|
16
|
+
<%%= form.label :password, "New password" %><br>
|
|
17
|
+
<%%= form.password_field :password, required: true, autofocus: true, autocomplete: "new-password" %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div>
|
|
21
|
+
<%%= form.label :password_confirmation %><br>
|
|
22
|
+
<%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div><%%= form.submit "Reset password" %></div>
|
|
26
|
+
<%% end %>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Forgot your password?</h1>
|
|
5
|
+
|
|
6
|
+
<%%= form_with url: <%= identity.route_scope %>_reset_password_path, method: :post<%= captcha_form_options %> do |form| %>
|
|
7
|
+
<div>
|
|
8
|
+
<%%= form.label :email %><br>
|
|
9
|
+
<%%= form.email_field :email, required: true, autofocus: true, autocomplete: "email" %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<%- if captchable? -%>
|
|
13
|
+
|
|
14
|
+
<%%= render "shared/captcha" %>
|
|
15
|
+
<%- end -%>
|
|
16
|
+
|
|
17
|
+
<div><%%= form.submit "Send reset instructions" %></div>
|
|
18
|
+
<%% end %>
|
|
19
|
+
|
|
20
|
+
<p><%%= link_to "Back to sign in", <%= identity.route_scope %>_sign_in_path %></p>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Confirm your email</h1>
|
|
5
|
+
|
|
6
|
+
<p>Click the link we sent to <%%= @registration.email %> to finish creating your account.</p>
|
|
7
|
+
|
|
8
|
+
<%%# Always offer resend. If we only showed it when a link had actually been sent it would reveal whether the address already has an account. %>
|
|
9
|
+
<%%= button_to "Send me another link", <%= identity.route_scope %>_verify_email_path, method: :post %>
|
|
10
|
+
|
|
11
|
+
<p><%%= link_to "<%= typed_registration? ? "Back to sign up" : "Back to sign in" %>", <%= registration_entry_path %> %></p>
|
|
@@ -0,0 +1,34 @@
|
|
|
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 <%%= @registration.email %> to finish creating your account.</p>
|
|
7
|
+
<%- if principals.username -%>
|
|
8
|
+
|
|
9
|
+
<%% if @registration.errors.any? %>
|
|
10
|
+
<ul style="color: red">
|
|
11
|
+
<%% @registration.errors.full_messages.each do |message| %>
|
|
12
|
+
<li><%%= message %></li>
|
|
13
|
+
<%% end %>
|
|
14
|
+
</ul>
|
|
15
|
+
<%% end %>
|
|
16
|
+
|
|
17
|
+
<%%= form_with url: <%= identity.route_scope %>_verify_email_path(sid: params[:sid]), method: :patch do |form| %>
|
|
18
|
+
<%%# Username may have been taken in between time the registration was made and the email was verified. %>
|
|
19
|
+
<%% if @registration.errors.any? %>
|
|
20
|
+
<div>
|
|
21
|
+
<%%= form.label :username %><br>
|
|
22
|
+
<%%= form.text_field :username, value: @registration.username, required: true, autofocus: true, autocomplete: "username" %>
|
|
23
|
+
</div>
|
|
24
|
+
<%% else %>
|
|
25
|
+
<%%= form.hidden_field :username, value: @registration.username %>
|
|
26
|
+
<%% end %>
|
|
27
|
+
|
|
28
|
+
<div><%%= form.submit "Confirm my email" %></div>
|
|
29
|
+
<%% end %>
|
|
30
|
+
<%- else -%>
|
|
31
|
+
|
|
32
|
+
<%%# Confirm with a non-GET request so mail scanners cannot create and sign in an account. %>
|
|
33
|
+
<%%= button_to "Confirm my email", <%= identity.route_scope %>_verify_email_path(sid: params[:sid]), method: :patch %>
|
|
34
|
+
<%- end -%>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Sign up</h1>
|
|
5
|
+
|
|
6
|
+
<%% if <%= registration_form_object %>.errors.any? %>
|
|
7
|
+
<ul style="color: red">
|
|
8
|
+
<%% <%= registration_form_object %>.errors.full_messages.each do |message| %>
|
|
9
|
+
<li><%%= message %></li>
|
|
10
|
+
<%% end %>
|
|
11
|
+
</ul>
|
|
12
|
+
<%% end %>
|
|
13
|
+
|
|
14
|
+
<%%= form_with url: <%= identity.route_scope %>_sign_up_path<%= captcha_form_options %> do |form| %>
|
|
15
|
+
<%- if principals.username -%>
|
|
16
|
+
<div>
|
|
17
|
+
<%%= form.label :username %><br>
|
|
18
|
+
<%%= form.text_field :username, value: <%= registration_form_object %>.username, required: true, autofocus: true, autocomplete: "username" %>
|
|
19
|
+
</div>
|
|
20
|
+
<%- end -%>
|
|
21
|
+
<%- if principals.email -%>
|
|
22
|
+
<div>
|
|
23
|
+
<%%= form.label :email<%= principals.email.optional? ? %q(, "Email (optional)") : "" %> %><br>
|
|
24
|
+
<%- if invitable? -%>
|
|
25
|
+
<%% if @pending_invitation&.email_addressed? %>
|
|
26
|
+
<%%= form.email_field :email, value: @pending_invitation.sent_to, readonly: true %>
|
|
27
|
+
<%% else %>
|
|
28
|
+
<%%= form.email_field :email, value: <%= registration_form_object %>.email<%= principals.email.required? ? ", required: true" : "" %><%= principals.username ? "" : ", autofocus: true" %>, autocomplete: "email" %>
|
|
29
|
+
<%% end %>
|
|
30
|
+
<%- else -%>
|
|
31
|
+
<%%= form.email_field :email, value: <%= registration_form_object %>.email<%= principals.email.required? ? ", required: true" : "" %><%= principals.username ? "" : ", autofocus: true" %>, autocomplete: "email" %>
|
|
32
|
+
<%- end -%>
|
|
33
|
+
</div>
|
|
34
|
+
<%- end -%>
|
|
35
|
+
<%- if principals.phone -%>
|
|
36
|
+
<div>
|
|
37
|
+
<%%= form.label :phone<%= principals.phone.optional? ? %q(, "Phone (optional)") : "" %> %><br>
|
|
38
|
+
<%- if invitable? -%>
|
|
39
|
+
<%% if @pending_invitation&.phone_addressed? %>
|
|
40
|
+
<%%= form.telephone_field :phone, value: @pending_invitation.sent_to, readonly: true %>
|
|
41
|
+
<%% else %>
|
|
42
|
+
<%%= form.telephone_field :phone, value: <%= registration_form_object %>.phone<%= principals.phone.required? ? ", required: true" : "" %>, autocomplete: "tel", placeholder: "(415) 826-4001" %>
|
|
43
|
+
<%% end %>
|
|
44
|
+
<%- else -%>
|
|
45
|
+
<%%= form.telephone_field :phone, value: <%= registration_form_object %>.phone<%= principals.phone.required? ? ", required: true" : "" %><%= (!principals.username && !principals.email) ? ", autofocus: true" : "" %>, autocomplete: "tel", placeholder: "(415) 826-4001" %>
|
|
46
|
+
<%- end -%>
|
|
47
|
+
</div>
|
|
48
|
+
<%- end -%>
|
|
49
|
+
<%- if password_on_sign_up_form? -%>
|
|
50
|
+
|
|
51
|
+
<div>
|
|
52
|
+
<%%= form.label :password<%= password_form_optional? ? %(, "Password (optional)") : "" %> %><br>
|
|
53
|
+
<%- if password_form_optional? -%>
|
|
54
|
+
<%%# There is more than one way to sign in, so this field is not required. %>
|
|
55
|
+
<%- end -%>
|
|
56
|
+
<%%= form.password_field :password<%= password_form_optional? ? "" : ", required: true" %>, autocomplete: "new-password" %>
|
|
57
|
+
<%- if password_form_optional? -%>
|
|
58
|
+
<%- end -%>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div>
|
|
62
|
+
<%%= form.label :password_confirmation %><br>
|
|
63
|
+
<%%= form.password_field :password_confirmation<%= password_form_optional? ? "" : ", required: true" %>, autocomplete: "new-password" %>
|
|
64
|
+
</div>
|
|
65
|
+
<%- end -%>
|
|
66
|
+
<%- if captchable? -%>
|
|
67
|
+
|
|
68
|
+
<%%= render "shared/captcha" %>
|
|
69
|
+
<%- end -%>
|
|
70
|
+
|
|
71
|
+
<div><%%= form.submit "Sign up" %></div>
|
|
72
|
+
<%% end %>
|
|
73
|
+
<%- if omniauth? -%>
|
|
74
|
+
|
|
75
|
+
<%- end -%>
|
|
76
|
+
<%- if omniauth? -%>
|
|
77
|
+
|
|
78
|
+
<%%# Add buttons for configured OmniAuth providers above this development-only example. %>
|
|
79
|
+
<%% if Rails.env.development? %>
|
|
80
|
+
<fieldset style="color: gray; margin-top: 1em;">
|
|
81
|
+
<legend><em>Development sign-up</em></legend>
|
|
82
|
+
<%%= button_to "Sign up with Developer", "<%= omniauth_path_prefix %>/developer", data: {turbo: false} %>
|
|
83
|
+
</fieldset>
|
|
84
|
+
<%% end %>
|
|
85
|
+
<%- end -%>
|
|
86
|
+
|
|
87
|
+
<p><%%= link_to "Sign in", <%= identity.route_scope %>_sign_in_path %></p>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Confirm your phone</h1>
|
|
5
|
+
|
|
6
|
+
<p>
|
|
7
|
+
Enter the six-digit code we texted you to finish creating your account.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<%#- The number is deliberately NOT echoed here. Sign-up answers identically
|
|
11
|
+
whether or not the submitted number was already registered, and printing
|
|
12
|
+
it back would undo that. -%>
|
|
13
|
+
|
|
14
|
+
<%- if principals.username -%>
|
|
15
|
+
<%% if @registration.errors.any? %>
|
|
16
|
+
<ul style="color: red">
|
|
17
|
+
<%% @registration.errors.full_messages.each do |message| %>
|
|
18
|
+
<li><%%= message %></li>
|
|
19
|
+
<%% end %>
|
|
20
|
+
</ul>
|
|
21
|
+
<%% end %>
|
|
22
|
+
|
|
23
|
+
<%- end -%>
|
|
24
|
+
<%%= form_with url: <%= identity.route_scope %>_verify_phone_path, method: :patch do |form| %>
|
|
25
|
+
<%- if principals.username -%>
|
|
26
|
+
<%%# Username may have been taken in between time the registration was made and the phone was verified. %>
|
|
27
|
+
<%% if @registration.errors.any? %>
|
|
28
|
+
<div>
|
|
29
|
+
<%%= form.label :username %><br>
|
|
30
|
+
<%%= form.text_field :username, value: @registration.username, required: true, autocomplete: "username" %>
|
|
31
|
+
</div>
|
|
32
|
+
<%% else %>
|
|
33
|
+
<%%= form.hidden_field :username, value: @registration.username %>
|
|
34
|
+
<%% end %>
|
|
35
|
+
|
|
36
|
+
<%- end -%>
|
|
37
|
+
<div>
|
|
38
|
+
<%%= form.label :code, "Verification code" %><br>
|
|
39
|
+
<%%= form.text_field :code, required: true, autocomplete: "one-time-code",
|
|
40
|
+
inputmode: "numeric", pattern: "[0-9]*", autofocus: true %>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div><%%= form.submit "Confirm phone" %></div>
|
|
44
|
+
<%% end %>
|
|
45
|
+
|
|
46
|
+
<%%= button_to "Send me another code", <%= identity.route_scope %>_verify_phone_path, method: :post %>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Sign in</h1>
|
|
5
|
+
|
|
6
|
+
<%- if password? -%>
|
|
7
|
+
<%%= form_with url: <%= identity.route_scope %>_sign_in_path<%= captcha_form_options %> do |form| %>
|
|
8
|
+
<div>
|
|
9
|
+
<%- if multi_login? -%>
|
|
10
|
+
<%%= form.label :login, "<%= login_keys_phrase.capitalize %>" %><br>
|
|
11
|
+
<%%= form.text_field :login, value: params[:login_hint], required: true, autofocus: true, autocomplete: "username" %>
|
|
12
|
+
<%- else -%>
|
|
13
|
+
<%%= form.label :<%= login_field %> %><br>
|
|
14
|
+
<%%= form.<%= login_principal.field_helper %> :<%= login_field %>, value: params[:<%= login_hint_param %>], required: true, autofocus: true, autocomplete: "<%= login_principal.autocomplete %>" %>
|
|
15
|
+
<%- end -%>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div>
|
|
19
|
+
<%%= form.label :password %><br>
|
|
20
|
+
<%%= form.password_field :password, required: true, autocomplete: "current-password" %>
|
|
21
|
+
</div>
|
|
22
|
+
<%- if rememberable? -%>
|
|
23
|
+
|
|
24
|
+
<div>
|
|
25
|
+
<%%= form.check_box :remember_me %>
|
|
26
|
+
<%%= form.label :remember_me, "Remember me on this device" %>
|
|
27
|
+
</div>
|
|
28
|
+
<%- end -%>
|
|
29
|
+
<%- if captchable? -%>
|
|
30
|
+
|
|
31
|
+
<%%= render "shared/captcha" %>
|
|
32
|
+
<%- end -%>
|
|
33
|
+
|
|
34
|
+
<div><%%= form.submit "Sign in" %></div>
|
|
35
|
+
<%% end %>
|
|
36
|
+
<%- end -%>
|
|
37
|
+
<%- if passkey? -%>
|
|
38
|
+
|
|
39
|
+
<div data-controller="webauthn"
|
|
40
|
+
data-webauthn-challenge-url-value="<%%= new_<%= identity.route_scope %>_passkeys_session_path(format: :json) %>"
|
|
41
|
+
data-webauthn-verification-url-value="<%%= <%= identity.route_scope %>_passkeys_session_path %>">
|
|
42
|
+
<button data-webauthn-target="button" data-action="webauthn#getCredential">Sign in with a passkey</button>
|
|
43
|
+
<p data-webauthn-target="error" style="color: red" hidden></p>
|
|
44
|
+
</div>
|
|
45
|
+
<%- end -%>
|
|
46
|
+
|
|
47
|
+
<%- if public_registration? || recoverable? -%>
|
|
48
|
+
<p>
|
|
49
|
+
<%- if public_registration? -%>
|
|
50
|
+
<%%= link_to "Sign up", <%= identity.route_scope %>_sign_up_path %><%= recoverable? ? " |" : "" %>
|
|
51
|
+
<%- end -%>
|
|
52
|
+
<%- if recoverable? -%>
|
|
53
|
+
<%%= link_to "Forgot your password?", <%= identity.route_scope %>_forgot_password_path %>
|
|
54
|
+
<%- end -%>
|
|
55
|
+
</p>
|
|
56
|
+
<%- end -%>
|
|
57
|
+
<%- if magic_link? -%>
|
|
58
|
+
<p><%%= link_to "Sign in with a magic link", <%= identity.route_scope %>_new_magic_link_path %></p>
|
|
59
|
+
<%- end -%>
|
|
60
|
+
<%- if sms_code? -%>
|
|
61
|
+
<p><%%= link_to "Sign in with a texted code", <%= identity.route_scope %>_sms_sign_in_path %></p>
|
|
62
|
+
<%- end -%>
|
|
63
|
+
<%- if omniauth? -%>
|
|
64
|
+
<%- end -%>
|
|
65
|
+
<%- if omniauth? || easy_dev_login? -%>
|
|
66
|
+
|
|
67
|
+
<%%# Add sign-in buttons for configured OmniAuth providers above the development-only example. %>
|
|
68
|
+
<%% if Rails.env.development? %>
|
|
69
|
+
<fieldset style="color: gray; margin-top: 1em;">
|
|
70
|
+
<legend><em>Development sign-in</em></legend>
|
|
71
|
+
<%- if omniauth? -%>
|
|
72
|
+
<%%= button_to "Sign in with Developer", "<%= omniauth_path_prefix %>/developer", data: {turbo: false} %>
|
|
73
|
+
<%- end -%>
|
|
74
|
+
<%- if easy_dev_login? && password? -%>
|
|
75
|
+
<p>Enter any user's <%= login_keys_phrase %> above. The password is ignored.</p>
|
|
76
|
+
<%- elsif easy_dev_login? -%>
|
|
77
|
+
<%%= form_with url: <%= identity.route_scope %>_easy_dev_login_path do |form| %>
|
|
78
|
+
<%- if multi_login? -%>
|
|
79
|
+
<%%= form.text_field :login, required: true, autocomplete: "off", placeholder: "<%= login_keys_phrase %>" %>
|
|
80
|
+
<%- else -%>
|
|
81
|
+
<%%= form.<%= login_principal.field_helper %> :<%= login_field %>, required: true, autocomplete: "off", placeholder: "<%= login_field %>" %>
|
|
82
|
+
<%- end -%>
|
|
83
|
+
<%%= form.submit "Sign in as" %>
|
|
84
|
+
<%% end %>
|
|
85
|
+
<%- end -%>
|
|
86
|
+
</fieldset>
|
|
87
|
+
<%% end %>
|
|
88
|
+
<%- end -%>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<p style="color: green"><%%= notice %></p>
|
|
2
|
+
<p style="color: red"><%%= alert %></p>
|
|
3
|
+
|
|
4
|
+
<h1>Enter your code</h1>
|
|
5
|
+
|
|
6
|
+
<p>Type the six-digit code we texted you.</p>
|
|
7
|
+
|
|
8
|
+
<%#- The number is deliberately NOT echoed here. This page answers identically
|
|
9
|
+
whether or not the number that was submitted has an account, and printing it
|
|
10
|
+
back (even the one that was typed) invites the habit of trusting whatever
|
|
11
|
+
the page claims to know about you. -%>
|
|
12
|
+
|
|
13
|
+
<%%= form_with url: <%= identity.route_scope %>_sms_sign_in_code_path, method: :patch do |form| %>
|
|
14
|
+
<div>
|
|
15
|
+
<%%= form.label :code, "Sign-in code" %><br>
|
|
16
|
+
<%%= form.text_field :code, required: true, autocomplete: "one-time-code",
|
|
17
|
+
inputmode: "numeric", pattern: "[0-9]*", autofocus: true %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div><%%= form.submit "Sign in" %></div>
|
|
21
|
+
<%% end %>
|
|
22
|
+
|
|
23
|
+
<%#- Resending means going back and retyping the number, deliberately. A resend
|
|
24
|
+
button here would need a second endpoint that texts whatever number the
|
|
25
|
+
pending row names, reachable by anyone who reached this page, and it would
|
|
26
|
+
buy very little, since the request form is one click away. -%>
|
|
27
|
+
<p>
|
|
28
|
+
<%%= link_to "Didn't get it? Request another code", <%= identity.route_scope %>_sms_sign_in_path %> |
|
|
29
|
+
<%%= link_to "Back to sign in", <%= identity.route_scope %>_sign_in_path %>
|
|
30
|
+
</p>
|
|
@@ -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 code</h1>
|
|
5
|
+
|
|
6
|
+
<p>Enter your phone number and we'll text you a six-digit code to sign in with.</p>
|
|
7
|
+
|
|
8
|
+
<%%= form_with url: <%= identity.route_scope %>_sms_sign_in_path, method: :post<%= captcha_form_options %> do |form| %>
|
|
9
|
+
<div>
|
|
10
|
+
<%%= form.label :phone, "Phone number" %><br>
|
|
11
|
+
<%%= form.telephone_field :phone, required: true, autofocus: true, autocomplete: "tel" %>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<%- if captchable? -%>
|
|
15
|
+
|
|
16
|
+
<%%= render "shared/captcha" %>
|
|
17
|
+
<%- end -%>
|
|
18
|
+
|
|
19
|
+
<div><%%= form.submit "Text me a code" %></div>
|
|
20
|
+
<%% end %>
|
|
21
|
+
|
|
22
|
+
<p><%%= link_to "Back to sign in", <%= identity.route_scope %>_sign_in_path %></p>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<p style="color: red"><%%= alert %></p>
|
|
2
|
+
|
|
3
|
+
<h1>Confirm it's you</h1>
|
|
4
|
+
|
|
5
|
+
<p><%%= session[:<%= session_key(:sudo_message) %>].presence || "Please confirm it's you before continuing." %></p>
|
|
6
|
+
|
|
7
|
+
<%%= form_with url: <%= identity.route_scope %>_sudo_path, method: :post do |form| %>
|
|
8
|
+
<%%= render "<%= identity.sudo_partial %>", form:, submit: "Confirm" %>
|
|
9
|
+
<%% end %>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { FetchRequest } from "@rails/request.js"
|
|
3
|
+
|
|
4
|
+
export default class WebauthnController extends Controller {
|
|
5
|
+
static targets = [ "error", "button", "supportText", "field" ]
|
|
6
|
+
static classes = [ "loading" ]
|
|
7
|
+
static values = {
|
|
8
|
+
challengeUrl: String,
|
|
9
|
+
verificationUrl: String,
|
|
10
|
+
fallbackUrl: String,
|
|
11
|
+
retryText: { type: String, default: "Retry" },
|
|
12
|
+
notAllowedText: { type: String, default: "That didn't work. Either it was cancelled or took too long. Please try again." },
|
|
13
|
+
invalidStateText: { type: String, default: "We couldn't add that security key. Please confirm you haven't already registered it, then try again." },
|
|
14
|
+
unsupportedText: { type: String, default: "This browser can't use passkeys or security keys. Please try again in an up-to-date browser." }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
connect() {
|
|
18
|
+
if (!this.supported()) {
|
|
19
|
+
this.handleUnsupportedBrowser()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getCredential() {
|
|
24
|
+
this.hideError()
|
|
25
|
+
this.disableForm()
|
|
26
|
+
this.requestChallengeAndVerify("get")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
createCredential() {
|
|
30
|
+
this.hideError()
|
|
31
|
+
this.disableForm()
|
|
32
|
+
this.requestChallengeAndVerify("create")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
supported() {
|
|
37
|
+
return typeof PublicKeyCredential !== "undefined" &&
|
|
38
|
+
typeof navigator.credentials !== "undefined" &&
|
|
39
|
+
typeof PublicKeyCredential.parseCreationOptionsFromJSON === "function" &&
|
|
40
|
+
typeof PublicKeyCredential.parseRequestOptionsFromJSON === "function" &&
|
|
41
|
+
typeof PublicKeyCredential.prototype.toJSON === "function"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
handleUnsupportedBrowser() {
|
|
45
|
+
this.buttonTarget.disabled = true
|
|
46
|
+
|
|
47
|
+
if (this.fallbackUrlValue) return window.location.replace(this.fallbackUrlValue)
|
|
48
|
+
|
|
49
|
+
if (this.hasErrorTarget) {
|
|
50
|
+
this.errorTarget.textContent = this.unsupportedTextValue
|
|
51
|
+
this.errorTarget.hidden = false
|
|
52
|
+
}
|
|
53
|
+
this.supportTextTargets.forEach(target => target.hidden = !target.hidden)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async requestChallengeAndVerify(mode) {
|
|
57
|
+
try {
|
|
58
|
+
const challengeResponse = await this.requestPublicKeyChallenge()
|
|
59
|
+
const credentialResponse = await this.requestCredential(mode, challengeResponse)
|
|
60
|
+
|
|
61
|
+
if (this.hasFieldTarget) {
|
|
62
|
+
this.fieldTarget.value = JSON.stringify(credentialResponse.toJSON())
|
|
63
|
+
this.element.closest("form").requestSubmit()
|
|
64
|
+
} else {
|
|
65
|
+
this.onCompletion(await this.verify(credentialResponse.toJSON()))
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.onError(error)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async requestCredential(mode, challengeResponse) {
|
|
73
|
+
if (mode === "create") {
|
|
74
|
+
return await navigator.credentials.create({
|
|
75
|
+
publicKey: PublicKeyCredential.parseCreationOptionsFromJSON(challengeResponse)
|
|
76
|
+
})
|
|
77
|
+
} else {
|
|
78
|
+
return await navigator.credentials.get({
|
|
79
|
+
publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(challengeResponse)
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async requestPublicKeyChallenge() {
|
|
85
|
+
return await this.request("get", this.challengeUrlValue)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async verify(credentialResponse) {
|
|
89
|
+
return await this.request("post", this.verificationUrlValue, {
|
|
90
|
+
body: JSON.stringify({ credential: credentialResponse }),
|
|
91
|
+
contentType: "application/json",
|
|
92
|
+
responseKind: "json"
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
onCompletion(response) {
|
|
97
|
+
window.location.replace(response.location)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onError(error) {
|
|
101
|
+
if (error.code === 0 && error.name === "NotAllowedError") {
|
|
102
|
+
this.errorTarget.textContent = this.notAllowedTextValue
|
|
103
|
+
} else if (error.code === 11 && error.name === "InvalidStateError") {
|
|
104
|
+
this.errorTarget.textContent = this.invalidStateTextValue
|
|
105
|
+
} else {
|
|
106
|
+
this.errorTarget.textContent = error.message
|
|
107
|
+
}
|
|
108
|
+
this.showError()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
hideError() {
|
|
112
|
+
if (this.hasErrorTarget) this.errorTarget.hidden = true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
showError() {
|
|
116
|
+
if (this.hasErrorTarget) {
|
|
117
|
+
this.errorTarget.hidden = false
|
|
118
|
+
this.buttonTarget.textContent = this.retryTextValue
|
|
119
|
+
this.enableForm()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
enableForm() {
|
|
124
|
+
if (this.hasLoadingClass) this.element.classList.remove(this.loadingClass)
|
|
125
|
+
this.buttonTarget.disabled = false
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
disableForm() {
|
|
129
|
+
if (this.hasLoadingClass) this.element.classList.add(this.loadingClass)
|
|
130
|
+
this.buttonTarget.disabled = true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async request(method, url, options) {
|
|
134
|
+
const request = new FetchRequest(method, url, { responseKind: "json", ...options })
|
|
135
|
+
const response = await request.perform()
|
|
136
|
+
const body = await response.json
|
|
137
|
+
|
|
138
|
+
if (!response.ok) throw new Error(body.error ?? `Something went wrong (${response.statusCode}).`)
|
|
139
|
+
|
|
140
|
+
return body
|
|
141
|
+
}
|
|
142
|
+
}
|