better_authy 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/MIT-LICENSE +20 -0
- data/README.md +123 -0
- data/Rakefile +3 -0
- data/app/controllers/better_authy/base_controller.rb +39 -0
- data/app/controllers/better_authy/passwords_controller.rb +92 -0
- data/app/controllers/better_authy/registrations_controller.rb +27 -0
- data/app/controllers/better_authy/sessions_controller.rb +48 -0
- data/app/helpers/better_authy/application_helper.rb +7 -0
- data/app/mailers/better_authy/application_mailer.rb +13 -0
- data/app/mailers/better_authy/password_reset_mailer.rb +20 -0
- data/app/models/better_authy/forgot_password_form.rb +12 -0
- data/app/models/better_authy/reset_password_form.rb +15 -0
- data/app/models/better_authy/session_form.rb +15 -0
- data/app/views/better_authy/password_reset_mailer/reset_password_instructions.html.erb +13 -0
- data/app/views/better_authy/password_reset_mailer/reset_password_instructions.text.erb +9 -0
- data/app/views/better_authy/passwords/edit.html.erb +55 -0
- data/app/views/better_authy/passwords/new.html.erb +61 -0
- data/app/views/better_authy/registrations/new.html.erb +59 -0
- data/app/views/better_authy/sessions/new.html.erb +80 -0
- data/app/views/layouts/better_authy/application.html.erb +20 -0
- data/config/locales/en.yml +48 -0
- data/config/locales/it.yml +48 -0
- data/config/routes.rb +23 -0
- data/lib/better_authy/configuration.rb +43 -0
- data/lib/better_authy/controller_helpers.rb +97 -0
- data/lib/better_authy/engine.rb +11 -0
- data/lib/better_authy/errors.rb +6 -0
- data/lib/better_authy/model_extensions.rb +23 -0
- data/lib/better_authy/models/authenticable.rb +118 -0
- data/lib/better_authy/scope_configuration.rb +30 -0
- data/lib/better_authy/version.rb +5 -0
- data/lib/better_authy.rb +37 -0
- metadata +137 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2cd343402586a23dd2212844efaa3a7007be3a0777b5012264d9380852b060c8
|
|
4
|
+
data.tar.gz: b0bde974d6e6c64172d8453193fe4c0727cd3d101b9548093928ab7a33259a0d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7f58ca868ea2d945e6f2ec0e22b4c255295efa4354a277648c32d9547d3525e8515f705d595a0c4671225cdf77e4ae070891832dfb96fb1d95fefce52a267b94
|
|
7
|
+
data.tar.gz: df65aa3eb2b1afb41e9c386d92e045e8f9deb3763c512d5d381d9e7256becefc1dcfe8a08fb16a8671fbfa12bf995948096e04b0c6816eb9ce30abee277ea50a
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Umberto Peserico
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# BetterAuthy
|
|
2
|
+
|
|
3
|
+
A flexible authentication engine for Rails 8.0+ with multi-scope support. Enables multiple authenticatable models (users, accounts, admins) through a scope-based configuration system.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Multi-scope authentication (multiple user types)
|
|
8
|
+
- Session-based authentication with encrypted cookies
|
|
9
|
+
- Remember me functionality
|
|
10
|
+
- Password reset via email
|
|
11
|
+
- Sign-in tracking (IP, timestamp, count)
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### 1. Add to Gemfile
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "better_authy", "~> 0.1.0"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Configure scope
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# config/initializers/better_authy.rb
|
|
25
|
+
BetterAuthy.configure do |config|
|
|
26
|
+
config.scope :user do |scope|
|
|
27
|
+
scope.model_name = "User"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 3. Add to model
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
class User < ApplicationRecord
|
|
36
|
+
better_authy_authenticable :user
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 4. Include controller helpers
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
class ApplicationController < ActionController::Base
|
|
44
|
+
include BetterAuthy::ControllerHelpers
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 5. Mount engine
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
Rails.application.routes.draw do
|
|
52
|
+
mount BetterAuthy::Engine => "/auth"
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 6. Create migration
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
class CreateUsers < ActiveRecord::Migration[8.0]
|
|
60
|
+
def change
|
|
61
|
+
create_table :users, id: :uuid do |t|
|
|
62
|
+
t.timestamps null: false
|
|
63
|
+
t.string :email, null: false
|
|
64
|
+
t.string :password_digest, null: false
|
|
65
|
+
t.string :remember_token_digest
|
|
66
|
+
t.datetime :remember_created_at
|
|
67
|
+
t.string :password_reset_token_digest
|
|
68
|
+
t.datetime :password_reset_sent_at
|
|
69
|
+
t.integer :sign_in_count, default: 0
|
|
70
|
+
t.datetime :current_sign_in_at, :last_sign_in_at
|
|
71
|
+
t.string :current_sign_in_ip, :last_sign_in_ip
|
|
72
|
+
t.index :email, unique: true
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
# In controllers
|
|
82
|
+
before_action :authenticate_user!
|
|
83
|
+
current_user
|
|
84
|
+
user_signed_in?
|
|
85
|
+
|
|
86
|
+
# Manual sign in/out
|
|
87
|
+
sign_in_user(user, remember: true)
|
|
88
|
+
sign_out_user
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Routes
|
|
92
|
+
|
|
93
|
+
| Path | Description |
|
|
94
|
+
|------|-------------|
|
|
95
|
+
| `/auth/user/login` | Login |
|
|
96
|
+
| `/auth/user/logout` | Logout |
|
|
97
|
+
| `/auth/user/signup` | Registration |
|
|
98
|
+
| `/auth/user/password/new` | Forgot password |
|
|
99
|
+
| `/auth/user/password/edit` | Reset password |
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
See the [docs/](docs/) folder for detailed guides:
|
|
104
|
+
|
|
105
|
+
- [Installation](docs/installation.md) - Complete setup instructions
|
|
106
|
+
- [Configuration](docs/configuration.md) - All configuration options
|
|
107
|
+
- [Models](docs/models.md) - Authenticable model setup
|
|
108
|
+
- [Controller Helpers](docs/controller-helpers.md) - Authentication methods
|
|
109
|
+
- [Routes](docs/routes.md) - Route helpers and customization
|
|
110
|
+
- [Password Reset](docs/password-reset.md) - Password reset flow
|
|
111
|
+
- [Multi-Scope](docs/multi-scope.md) - Multiple user types
|
|
112
|
+
- [Testing](docs/testing.md) - Test setup and patterns
|
|
113
|
+
|
|
114
|
+
## Dependencies
|
|
115
|
+
|
|
116
|
+
- Rails >= 8.0
|
|
117
|
+
- bcrypt ~> 3.1
|
|
118
|
+
- view_component ~> 4.0
|
|
119
|
+
- better_ui ~> 0.7
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT License
|
data/Rakefile
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class BaseController < ::ApplicationController
|
|
5
|
+
helper BetterAuthy::ApplicationHelper
|
|
6
|
+
|
|
7
|
+
layout :resolve_layout
|
|
8
|
+
protect_from_forgery with: :exception
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def scope_name
|
|
13
|
+
# Scope is injected into params via routes defaults: { scope: scope_name }
|
|
14
|
+
params[:scope]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def scope_config
|
|
18
|
+
@scope_config ||= BetterAuthy.scope_for!(scope_name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def redirect_if_signed_in
|
|
22
|
+
return unless send(:"#{scope_name}_signed_in?")
|
|
23
|
+
|
|
24
|
+
redirect_to scope_config.after_sign_in_path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def resolve_layout
|
|
28
|
+
return default_layout if scope_name.blank?
|
|
29
|
+
|
|
30
|
+
scope_config.layout
|
|
31
|
+
rescue BetterAuthy::ConfigurationError
|
|
32
|
+
default_layout
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def default_layout
|
|
36
|
+
"better_authy/application"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class PasswordsController < BaseController
|
|
5
|
+
before_action :redirect_if_signed_in, only: %i[new create edit update]
|
|
6
|
+
before_action :find_resource_by_token, only: %i[edit update]
|
|
7
|
+
|
|
8
|
+
def new
|
|
9
|
+
@forgot_password_form = BetterAuthy::ForgotPasswordForm.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create
|
|
13
|
+
@forgot_password_form = BetterAuthy::ForgotPasswordForm.new(forgot_password_params)
|
|
14
|
+
|
|
15
|
+
if @forgot_password_form.valid?
|
|
16
|
+
resource = find_resource_by_email
|
|
17
|
+
if resource
|
|
18
|
+
token = resource.generate_password_reset_token!
|
|
19
|
+
BetterAuthy::PasswordResetMailer.reset_password_instructions(
|
|
20
|
+
resource, token, scope_name
|
|
21
|
+
).deliver_later
|
|
22
|
+
end
|
|
23
|
+
# Always show generic message to prevent email enumeration
|
|
24
|
+
redirect_to send(:"#{scope_name}_login_path"),
|
|
25
|
+
notice: I18n.t("better_authy.passwords.send_instructions")
|
|
26
|
+
else
|
|
27
|
+
render :new, status: :unprocessable_content
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def edit
|
|
32
|
+
@reset_password_form = BetterAuthy::ResetPasswordForm.new(token: params[:token])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update
|
|
36
|
+
@reset_password_form = BetterAuthy::ResetPasswordForm.new(reset_password_params)
|
|
37
|
+
|
|
38
|
+
if @reset_password_form.valid? && @resource.reset_password!(
|
|
39
|
+
@reset_password_form.password,
|
|
40
|
+
@reset_password_form.password_confirmation
|
|
41
|
+
)
|
|
42
|
+
redirect_to send(:"#{scope_name}_login_path"),
|
|
43
|
+
notice: I18n.t("better_authy.passwords.updated")
|
|
44
|
+
else
|
|
45
|
+
@resource.errors.each do |error|
|
|
46
|
+
@reset_password_form.errors.add(error.attribute, error.message)
|
|
47
|
+
end
|
|
48
|
+
render :edit, status: :unprocessable_content
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def forgot_password_params
|
|
55
|
+
params.require(:forgot_password).permit(:email)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def reset_password_params
|
|
59
|
+
params.require(:reset_password).permit(:token, :password, :password_confirmation)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def find_resource_by_email
|
|
63
|
+
email = forgot_password_params[:email].to_s.strip.downcase
|
|
64
|
+
scope_config.model_class.find_by(email: email)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def find_resource_by_token
|
|
68
|
+
token = params[:token]
|
|
69
|
+
token ||= params.dig(:reset_password, :token)
|
|
70
|
+
|
|
71
|
+
if token.blank?
|
|
72
|
+
redirect_to send(:"new_#{scope_name}_password_path"),
|
|
73
|
+
alert: I18n.t("better_authy.passwords.no_token")
|
|
74
|
+
return
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
@resource = find_resource_with_valid_token(token)
|
|
78
|
+
|
|
79
|
+
unless @resource
|
|
80
|
+
redirect_to send(:"new_#{scope_name}_password_path"),
|
|
81
|
+
alert: I18n.t("better_authy.passwords.invalid_token")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def find_resource_with_valid_token(token)
|
|
86
|
+
scope_config.model_class.find_each do |resource|
|
|
87
|
+
return resource if resource.password_reset_token_valid?(token)
|
|
88
|
+
end
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class RegistrationsController < BaseController
|
|
5
|
+
before_action :redirect_if_signed_in, only: %i[new create]
|
|
6
|
+
|
|
7
|
+
def new
|
|
8
|
+
@resource = scope_config.model_class.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create
|
|
12
|
+
@resource = scope_config.model_class.new(resource_params)
|
|
13
|
+
if @resource.save
|
|
14
|
+
send(:"sign_in_#{scope_name}", @resource)
|
|
15
|
+
redirect_to scope_config.after_sign_in_path
|
|
16
|
+
else
|
|
17
|
+
render :new, status: :unprocessable_content
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def resource_params
|
|
24
|
+
params.require(scope_name).permit(:email, :password, :password_confirmation)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class SessionsController < BaseController
|
|
5
|
+
before_action :redirect_if_signed_in, only: %i[new create]
|
|
6
|
+
|
|
7
|
+
def new
|
|
8
|
+
@session_form = BetterAuthy::SessionForm.new(email: params.dig(:session, :email))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create
|
|
12
|
+
@session_form = BetterAuthy::SessionForm.new(session_params)
|
|
13
|
+
|
|
14
|
+
# debugger
|
|
15
|
+
|
|
16
|
+
resource = find_resource_by_email
|
|
17
|
+
if resource&.authenticate(session_params[:password])
|
|
18
|
+
remember = @session_form.remember_me
|
|
19
|
+
send(:"sign_in_#{scope_name}", resource, remember: remember)
|
|
20
|
+
redirect_to scope_config.after_sign_in_path
|
|
21
|
+
else
|
|
22
|
+
render_invalid_credentials
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def destroy
|
|
27
|
+
send(:"sign_out_#{scope_name}")
|
|
28
|
+
redirect_to scope_config.after_sign_in_path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def session_params
|
|
34
|
+
params.require(:session).permit(:email, :password, :remember_me)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def find_resource_by_email
|
|
38
|
+
email = session_params[:email].to_s.strip.downcase
|
|
39
|
+
scope_config.model_class.find_by(email: email)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def render_invalid_credentials
|
|
43
|
+
flash.now[:alert] = I18n.t("better_authy.sessions.invalid_credentials",
|
|
44
|
+
default: "Invalid email or password")
|
|
45
|
+
render :new, status: :unprocessable_content
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class ApplicationMailer < ::ActionMailer::Base
|
|
5
|
+
default from: -> { default_from_address }
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def default_from_address
|
|
10
|
+
ENV.fetch("BETTER_AUTHY_MAILER_FROM", "noreply@example.com")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class PasswordResetMailer < ApplicationMailer
|
|
5
|
+
include BetterAuthy::Engine.routes.url_helpers
|
|
6
|
+
|
|
7
|
+
def reset_password_instructions(resource, token, scope_name)
|
|
8
|
+
@resource = resource
|
|
9
|
+
@token = token
|
|
10
|
+
@scope_name = scope_name
|
|
11
|
+
@scope_config = BetterAuthy.scope_for!(scope_name)
|
|
12
|
+
@reset_url = send(:"edit_#{scope_name}_password_url", token: token)
|
|
13
|
+
|
|
14
|
+
mail(
|
|
15
|
+
to: resource.email,
|
|
16
|
+
subject: I18n.t("better_authy.passwords.mailer.subject")
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class ResetPasswordForm
|
|
5
|
+
include ActiveModel::Model
|
|
6
|
+
include ActiveModel::Attributes
|
|
7
|
+
|
|
8
|
+
attribute :token, :string
|
|
9
|
+
attribute :password, :string
|
|
10
|
+
attribute :password_confirmation, :string
|
|
11
|
+
|
|
12
|
+
validates :password, presence: true, length: { minimum: 8 }
|
|
13
|
+
validates :password_confirmation, presence: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuthy
|
|
4
|
+
class SessionForm
|
|
5
|
+
include ActiveModel::Model
|
|
6
|
+
include ActiveModel::Attributes
|
|
7
|
+
|
|
8
|
+
attribute :email, :string
|
|
9
|
+
attribute :password, :string
|
|
10
|
+
attribute :remember_me, :boolean, default: false
|
|
11
|
+
|
|
12
|
+
validates :email, presence: true
|
|
13
|
+
validates :password, presence: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<h1><%= t("better_authy.passwords.mailer.greeting", email: @resource.email) %></h1>
|
|
2
|
+
|
|
3
|
+
<p><%= t("better_authy.passwords.mailer.instruction") %></p>
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<a href="<%= @reset_url %>">
|
|
7
|
+
<%= t("better_authy.passwords.mailer.action") %>
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p><%= t("better_authy.passwords.mailer.ignore") %></p>
|
|
12
|
+
|
|
13
|
+
<p><%= t("better_authy.passwords.mailer.expiration", hours: (@scope_config.password_reset_within / 1.hour).to_i) %></p>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%= t("better_authy.passwords.mailer.greeting", email: @resource.email) %>
|
|
2
|
+
|
|
3
|
+
<%= t("better_authy.passwords.mailer.instruction") %>
|
|
4
|
+
|
|
5
|
+
<%= @reset_url %>
|
|
6
|
+
|
|
7
|
+
<%= t("better_authy.passwords.mailer.ignore") %>
|
|
8
|
+
|
|
9
|
+
<%= t("better_authy.passwords.mailer.expiration", hours: (@scope_config.password_reset_within / 1.hour).to_i) %>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<% content_for :title, t("better_authy.passwords.reset_title") %>
|
|
2
|
+
|
|
3
|
+
<div class="w-full max-w-md">
|
|
4
|
+
<% if flash[:alert].present? %>
|
|
5
|
+
<%= bui_action_messages([flash[:alert]],
|
|
6
|
+
variant: :danger,
|
|
7
|
+
style: :soft,
|
|
8
|
+
dismissible: true,
|
|
9
|
+
container_classes: "mb-6"
|
|
10
|
+
) %>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<%= bui_card(
|
|
14
|
+
variant: :light,
|
|
15
|
+
style: :bordered,
|
|
16
|
+
size: :lg,
|
|
17
|
+
shadow: true
|
|
18
|
+
) do |card| %>
|
|
19
|
+
<% card.with_header do %>
|
|
20
|
+
<h1 class="text-2xl font-bold text-center text-grayscale-900">
|
|
21
|
+
<%= t("better_authy.passwords.reset_title") %>
|
|
22
|
+
</h1>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<% card.with_body do %>
|
|
26
|
+
<%= form_with model: @reset_password_form, scope: :reset_password, url: send(:"#{params[:scope]}_password_path"), method: :patch, builder: BetterUi::UiFormBuilder, class: "space-y-6" do |f| %>
|
|
27
|
+
<%= f.hidden_field :token %>
|
|
28
|
+
|
|
29
|
+
<%= f.bui_password_input :password,
|
|
30
|
+
label: t("better_authy.attributes.password"),
|
|
31
|
+
placeholder: t("better_authy.placeholders.password"),
|
|
32
|
+
hint: t("better_authy.hints.password"),
|
|
33
|
+
autocomplete: "new-password" %>
|
|
34
|
+
|
|
35
|
+
<%= f.bui_password_input :password_confirmation,
|
|
36
|
+
label: t("better_authy.attributes.password_confirmation"),
|
|
37
|
+
placeholder: t("better_authy.placeholders.password_confirmation"),
|
|
38
|
+
autocomplete: "new-password" %>
|
|
39
|
+
|
|
40
|
+
<div>
|
|
41
|
+
<%= bui_button(
|
|
42
|
+
variant: :primary,
|
|
43
|
+
style: :solid,
|
|
44
|
+
size: :lg,
|
|
45
|
+
type: :submit,
|
|
46
|
+
show_loader_on_click: true,
|
|
47
|
+
container_classes: "w-full"
|
|
48
|
+
) do %>
|
|
49
|
+
<%= t("better_authy.passwords.reset_submit") %>
|
|
50
|
+
<% end %>
|
|
51
|
+
</div>
|
|
52
|
+
<% end %>
|
|
53
|
+
<% end %>
|
|
54
|
+
<% end %>
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<% content_for :title, t("better_authy.passwords.forgot_title") %>
|
|
2
|
+
|
|
3
|
+
<div class="w-full max-w-md">
|
|
4
|
+
<% if flash[:alert].present? %>
|
|
5
|
+
<%= bui_action_messages([flash[:alert]],
|
|
6
|
+
variant: :danger,
|
|
7
|
+
style: :soft,
|
|
8
|
+
dismissible: true,
|
|
9
|
+
container_classes: "mb-6"
|
|
10
|
+
) %>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<%= bui_card(
|
|
14
|
+
variant: :light,
|
|
15
|
+
style: :bordered,
|
|
16
|
+
size: :lg,
|
|
17
|
+
shadow: true
|
|
18
|
+
) do |card| %>
|
|
19
|
+
<% card.with_header do %>
|
|
20
|
+
<h1 class="text-2xl font-bold text-center text-grayscale-900">
|
|
21
|
+
<%= t("better_authy.passwords.forgot_title") %>
|
|
22
|
+
</h1>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<% card.with_body do %>
|
|
26
|
+
<p class="text-sm text-grayscale-600 mb-6">
|
|
27
|
+
<%= t("better_authy.passwords.forgot_instruction") %>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<%= form_with model: @forgot_password_form, scope: :forgot_password, url: send(:"#{params[:scope]}_password_path"), builder: BetterUi::UiFormBuilder, class: "space-y-6" do |f| %>
|
|
31
|
+
<%= f.bui_text_input :email,
|
|
32
|
+
label: t("better_authy.attributes.email"),
|
|
33
|
+
placeholder: t("better_authy.placeholders.email"),
|
|
34
|
+
type: "email",
|
|
35
|
+
autocomplete: "email" %>
|
|
36
|
+
|
|
37
|
+
<div>
|
|
38
|
+
<%= bui_button(
|
|
39
|
+
variant: :primary,
|
|
40
|
+
style: :solid,
|
|
41
|
+
size: :lg,
|
|
42
|
+
type: :submit,
|
|
43
|
+
show_loader_on_click: true,
|
|
44
|
+
container_classes: "w-full"
|
|
45
|
+
) do %>
|
|
46
|
+
<%= t("better_authy.passwords.forgot_submit") %>
|
|
47
|
+
<% end %>
|
|
48
|
+
</div>
|
|
49
|
+
<% end %>
|
|
50
|
+
<% end %>
|
|
51
|
+
|
|
52
|
+
<% card.with_footer do %>
|
|
53
|
+
<p class="text-center text-sm text-grayscale-600">
|
|
54
|
+
<%= t("better_authy.passwords.remembered") %>
|
|
55
|
+
<%= link_to t("better_authy.registrations.login_link"),
|
|
56
|
+
send(:"#{params[:scope]}_login_path"),
|
|
57
|
+
class: "font-medium text-primary-600 hover:text-primary-500" %>
|
|
58
|
+
</p>
|
|
59
|
+
<% end %>
|
|
60
|
+
<% end %>
|
|
61
|
+
</div>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<% content_for :title, t("better_authy.registrations.title") %>
|
|
2
|
+
|
|
3
|
+
<div class="w-full max-w-md">
|
|
4
|
+
<%= bui_card(
|
|
5
|
+
variant: :light,
|
|
6
|
+
style: :bordered,
|
|
7
|
+
size: :lg,
|
|
8
|
+
shadow: true
|
|
9
|
+
) do |card| %>
|
|
10
|
+
<% card.with_header do %>
|
|
11
|
+
<h1 class="text-2xl font-bold text-center text-grayscale-900">
|
|
12
|
+
<%= t("better_authy.registrations.title") %>
|
|
13
|
+
</h1>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
<% card.with_body do %>
|
|
17
|
+
<%= form_with model: @resource, scope: params[:scope], url: request.path, builder: BetterUi::UiFormBuilder, class: "space-y-6" do |f| %>
|
|
18
|
+
<%= f.bui_text_input :email,
|
|
19
|
+
label: t("better_authy.attributes.email"),
|
|
20
|
+
placeholder: t("better_authy.placeholders.email"),
|
|
21
|
+
type: "email",
|
|
22
|
+
autocomplete: "email" %>
|
|
23
|
+
|
|
24
|
+
<%= f.bui_password_input :password,
|
|
25
|
+
label: t("better_authy.attributes.password"),
|
|
26
|
+
placeholder: t("better_authy.placeholders.password"),
|
|
27
|
+
hint: t("better_authy.hints.password"),
|
|
28
|
+
autocomplete: "new-password" %>
|
|
29
|
+
|
|
30
|
+
<%= f.bui_password_input :password_confirmation,
|
|
31
|
+
label: t("better_authy.attributes.password_confirmation"),
|
|
32
|
+
placeholder: t("better_authy.placeholders.password_confirmation"),
|
|
33
|
+
autocomplete: "new-password" %>
|
|
34
|
+
|
|
35
|
+
<div>
|
|
36
|
+
<%= bui_button(
|
|
37
|
+
variant: :primary,
|
|
38
|
+
style: :solid,
|
|
39
|
+
size: :lg,
|
|
40
|
+
type: :submit,
|
|
41
|
+
show_loader_on_click: true,
|
|
42
|
+
container_classes: "w-full"
|
|
43
|
+
) do %>
|
|
44
|
+
<%= t("better_authy.registrations.submit") %>
|
|
45
|
+
<% end %>
|
|
46
|
+
</div>
|
|
47
|
+
<% end %>
|
|
48
|
+
<% end %>
|
|
49
|
+
|
|
50
|
+
<% card.with_footer do %>
|
|
51
|
+
<p class="text-center text-sm text-grayscale-600">
|
|
52
|
+
<%= t("better_authy.registrations.has_account") %>
|
|
53
|
+
<%= link_to t("better_authy.registrations.login_link"),
|
|
54
|
+
send(:"#{params[:scope]}_login_path"),
|
|
55
|
+
class: "font-medium text-primary-600 hover:text-primary-500" %>
|
|
56
|
+
</p>
|
|
57
|
+
<% end %>
|
|
58
|
+
<% end %>
|
|
59
|
+
</div>
|