no_password_auth 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README-ES.md +230 -0
- data/README.md +230 -0
- data/Rakefile +18 -0
- data/app/assets/config/no_password/manifest.js +4 -0
- data/app/assets/config/no_password/tailwind.config.js +61 -0
- data/app/assets/images/no_password/aoo.svg +1 -0
- data/app/assets/javascripts/no_password/application.js +4 -0
- data/app/assets/javascripts/no_password/controllers/alert_controller.js +31 -0
- data/app/assets/javascripts/no_password/controllers/application.js +10 -0
- data/app/assets/javascripts/no_password/controllers/index.js +11 -0
- data/app/assets/stylesheets/no_password/application.css +15 -0
- data/app/assets/stylesheets/no_password/application.tailwind.css +34 -0
- data/app/controllers/concerns/no_password/controller_helpers.rb +45 -0
- data/app/controllers/concerns/no_password/web_tokens.rb +33 -0
- data/app/controllers/no_password/application_controller.rb +4 -0
- data/app/controllers/no_password/session_confirmations_controller.rb +46 -0
- data/app/controllers/no_password/sessions_controller.rb +49 -0
- data/app/helpers/no_password/application_helper.rb +4 -0
- data/app/helpers/no_password/no_password_helper.rb +13 -0
- data/app/jobs/no_password/application_job.rb +4 -0
- data/app/mailers/no_password/application_mailer.rb +6 -0
- data/app/mailers/no_password/sessions_mailer.rb +16 -0
- data/app/models/no_password/application_record.rb +5 -0
- data/app/models/no_password/session.rb +20 -0
- data/app/use_cases/no_password/session_manager.rb +51 -0
- data/app/views/layouts/no_password/application.html.erb +26 -0
- data/app/views/layouts/no_password/mailer.html.erb +106 -0
- data/app/views/layouts/no_password/mailer.text.erb +6 -0
- data/app/views/no_password/application/_notification.html.erb +55 -0
- data/app/views/no_password/session_confirmations/_form.html.erb +19 -0
- data/app/views/no_password/session_confirmations/edit.html.erb +15 -0
- data/app/views/no_password/session_confirmations/update.turbo_stream.erb +3 -0
- data/app/views/no_password/sessions/_form.html.erb +16 -0
- data/app/views/no_password/sessions/create.turbo_stream.erb +3 -0
- data/app/views/no_password/sessions/new.html.erb +15 -0
- data/app/views/no_password/sessions_mailer/send_token.html.erb +14 -0
- data/app/views/no_password/sessions_mailer/send_token.text.erb +12 -0
- data/config/initializers/importmap.rb +17 -0
- data/config/locales/en/flash.en.yml +11 -0
- data/config/locales/en/forms.en.yml +9 -0
- data/config/locales/en/mailers.en.yml +21 -0
- data/config/locales/en/views.en.yml +12 -0
- data/config/locales/es/flash.es.yml +11 -0
- data/config/locales/es/forms.es.yml +9 -0
- data/config/locales/es/mailers.es.yml +18 -0
- data/config/locales/es/views.es.yml +13 -0
- data/config/locales/models.en.yml +8 -0
- data/config/locales/models.es.yml +8 -0
- data/config/locales/models.yml +8 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20211202211706_create_no_password_sessions.rb +16 -0
- data/docs/aoorora-demo.gif +0 -0
- data/docs/dummy-app.png +0 -0
- data/lib/generators/no_password/install_generator.rb +30 -0
- data/lib/generators/no_password/install_templates_generator.rb +27 -0
- data/lib/generators/no_password/tailwind_config_generator.rb +9 -0
- data/lib/generators/no_password/templates/app/assets/config/no_password/tailwind.config.js.tt +57 -0
- data/lib/generators/no_password/templates/config/initializers/no_password.rb +10 -0
- data/lib/no_password/engine.rb +11 -0
- data/lib/no_password/railtie.rb +7 -0
- data/lib/no_password/version.rb +3 -0
- data/lib/no_password.rb +42 -0
- data/lib/tasks/install.rake +17 -0
- data/lib/tasks/no_password_tasks.rake +4 -0
- data/lib/tasks/tailwind.rake +23 -0
- metadata +227 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
@tailwind base;
|
2
|
+
@tailwind components;
|
3
|
+
@tailwind utilities;
|
4
|
+
|
5
|
+
@layer base {
|
6
|
+
:root {
|
7
|
+
--color-base: 31, 27, 54;
|
8
|
+
--color-accented: 80, 70, 137;
|
9
|
+
--color-inverted: 255, 255, 255;
|
10
|
+
--color-accented-hover: 60, 53, 103;
|
11
|
+
--color-muted: 55, 65, 81;
|
12
|
+
--color-dimmed: 75, 85, 99;
|
13
|
+
--color-error: 220, 38, 38;
|
14
|
+
|
15
|
+
--color-border-base: 209, 213, 219;
|
16
|
+
--color-border-accented: 80, 70, 137;
|
17
|
+
}
|
18
|
+
|
19
|
+
.input {
|
20
|
+
@apply border-skin-base rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-skin-accented focus:border-skin-accented sm:text-sm;
|
21
|
+
}
|
22
|
+
|
23
|
+
.check {
|
24
|
+
@apply border-skin-base h-4 w-4 text-skin-accented focus:ring-skin-accented rounded;
|
25
|
+
}
|
26
|
+
|
27
|
+
.button {
|
28
|
+
@apply inline-flex justify-center py-2 px-4 border border-skin-base rounded-md shadow-sm text-sm font-medium text-skin-muted bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-skin-accented;
|
29
|
+
}
|
30
|
+
|
31
|
+
.button-accented {
|
32
|
+
@apply border-transparent bg-skin-button-accented hover:bg-skin-button-accented-hover text-skin-inverted;
|
33
|
+
}
|
34
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoPassword
|
4
|
+
module ControllerHelpers
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
def current_session
|
9
|
+
@session ||= begin
|
10
|
+
session_id = session[session_key]
|
11
|
+
return nil if session_id.blank?
|
12
|
+
|
13
|
+
current_session = find_session(session_id)
|
14
|
+
session[session_key] = nil if current_session.blank?
|
15
|
+
|
16
|
+
current_session
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def signed_in_session?
|
21
|
+
current_session.present?
|
22
|
+
end
|
23
|
+
|
24
|
+
def authenticate_session!
|
25
|
+
redirect_to no_password.new_session_path(return_to: CGI.escape(request.fullpath)), alert: t("flash.update.session.alert") unless signed_in_session?
|
26
|
+
end
|
27
|
+
|
28
|
+
helper_method :current_session, :signed_in_session?
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def session_key(value = "id")
|
33
|
+
"—-no_password_session_#{value}"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def find_session(session_id)
|
39
|
+
NoPassword::Session.where.not(claimed_at: nil)
|
40
|
+
.where("expires_at > ?", Time.zone.now)
|
41
|
+
.find_by_id(session_id)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module NoPassword
|
6
|
+
module WebTokens
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
def sign_token(data)
|
11
|
+
secret_key = NoPassword.configuration.secret_key || Rails.application.secret_key_base
|
12
|
+
verifier = ActiveSupport::MessageVerifier.new(secret_key)
|
13
|
+
verifier.generate(data, expires_in: NoPassword.configuration.session_expiration, purpose: :no_password_login)
|
14
|
+
end
|
15
|
+
|
16
|
+
def verify_token(data)
|
17
|
+
secret_key = NoPassword.configuration.secret_key || Rails.application.secret_key_base
|
18
|
+
verifier = ActiveSupport::MessageVerifier.new(secret_key)
|
19
|
+
|
20
|
+
token = token_from_url(data)
|
21
|
+
verifier.verified(token, purpose: :no_password_login)
|
22
|
+
end
|
23
|
+
|
24
|
+
def token_to_url(token)
|
25
|
+
CGI.escape(token)
|
26
|
+
end
|
27
|
+
|
28
|
+
def token_from_url(token)
|
29
|
+
CGI.unescape(token)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoPassword
|
4
|
+
class SessionConfirmationsController < ApplicationController
|
5
|
+
include NoPassword::ControllerHelpers
|
6
|
+
include NoPassword::WebTokens
|
7
|
+
|
8
|
+
def edit
|
9
|
+
if params[:token].present?
|
10
|
+
token = verify_token(params[:token])
|
11
|
+
|
12
|
+
sign_in_session(token, true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def update
|
17
|
+
result = sign_in_session(params[:token])
|
18
|
+
return result if result.present?
|
19
|
+
|
20
|
+
response.status = :unprocessable_entity
|
21
|
+
render turbo_stream: turbo_stream.update("notifications", partial: "notification")
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def sign_in_session(token, by_url = false)
|
27
|
+
current_session = SessionManager.new.claim(token)
|
28
|
+
|
29
|
+
flash.now.alert = t("flash.update.invalid_code.alert") if current_session.blank?
|
30
|
+
|
31
|
+
result = if respond_to?(:after_sign_in!)
|
32
|
+
after_sign_in!(current_session.present?, by_url, current_session&.return_url)
|
33
|
+
elsif current_session.present?
|
34
|
+
save_session_to_cookie(current_session)
|
35
|
+
redirect_to(current_session.return_url || main_app.root_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
result if result.present?
|
39
|
+
end
|
40
|
+
|
41
|
+
def save_session_to_cookie(current_session, key = nil, data = nil)
|
42
|
+
session[session_key] = current_session.id
|
43
|
+
session[session_key(key)] = data if key.present? && data.present?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoPassword
|
4
|
+
class SessionsController < ApplicationController
|
5
|
+
include NoPassword::ControllerHelpers
|
6
|
+
|
7
|
+
def new
|
8
|
+
@return_to = params[:return_to].to_s
|
9
|
+
@resource = Session.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
return_to = params[:return_to].to_s
|
14
|
+
current_session = SessionManager.new.create(request.user_agent, params.dig(:session, :email), request.remote_ip, referrer_path(return_to))
|
15
|
+
|
16
|
+
if current_session.present?
|
17
|
+
SessionsMailer.with(session: current_session).send_token.deliver_later
|
18
|
+
|
19
|
+
after_session_request if respond_to?(:after_session_request)
|
20
|
+
|
21
|
+
respond_to do |format|
|
22
|
+
format.html { redirect_to no_password.edit_session_confirmations_path }
|
23
|
+
format.turbo_stream
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy
|
29
|
+
sign_out
|
30
|
+
redirect_to main_app.root_path
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def referrer_path(return_to)
|
36
|
+
referrer = CGI.unescape(return_to)
|
37
|
+
return nil if referrer.blank?
|
38
|
+
|
39
|
+
referrer.include?(no_password.new_session_path) || referrer.include?(no_password.edit_session_confirmations_path) ? nil : referrer
|
40
|
+
end
|
41
|
+
|
42
|
+
def sign_out(key = nil)
|
43
|
+
session.delete(session_key)
|
44
|
+
session.delete(session_key(key)) if key.present?
|
45
|
+
@session = nil
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module NoPassword
|
2
|
+
module NoPasswordHelper
|
3
|
+
def no_password_importmap_tags(entry_point = "application", shim: true)
|
4
|
+
safe_join [
|
5
|
+
javascript_inline_importmap_tag(NoPassword.configuration.importmap.to_json(resolver: self)),
|
6
|
+
javascript_importmap_module_preload_tags(NoPassword.configuration.importmap),
|
7
|
+
(javascript_importmap_shim_nonce_configuration_tag if shim),
|
8
|
+
(javascript_importmap_shim_tag if shim),
|
9
|
+
javascript_import_module_tag(entry_point)
|
10
|
+
].compact, "\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoPassword
|
4
|
+
class SessionsMailer < ApplicationMailer
|
5
|
+
include NoPassword::WebTokens
|
6
|
+
|
7
|
+
def send_token
|
8
|
+
@session = params[:session]
|
9
|
+
@title = t("mailers.send_token.subject")
|
10
|
+
@friendly_token = @session.token
|
11
|
+
@signed_token = token_to_url(sign_token(@friendly_token))
|
12
|
+
|
13
|
+
mail(to: @session.email, subject: t("mailers.send_token.subject"))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoPassword
|
4
|
+
class Session < ApplicationRecord
|
5
|
+
validates :token, :user_agent, :remote_addr, :email, presence: true
|
6
|
+
|
7
|
+
def claimed?
|
8
|
+
claimed_at.present?
|
9
|
+
end
|
10
|
+
|
11
|
+
def expired?
|
12
|
+
current_time = Time.zone.now
|
13
|
+
expires_at <= current_time
|
14
|
+
end
|
15
|
+
|
16
|
+
def invalid?
|
17
|
+
claimed? || expired?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoPassword
|
4
|
+
class SessionManager
|
5
|
+
def create(user_agent, email, remote_addr, return_url = nil)
|
6
|
+
expire_unclaimed_session(email)
|
7
|
+
|
8
|
+
Session.create(
|
9
|
+
user_agent: user_agent,
|
10
|
+
email: email,
|
11
|
+
expires_at: NoPassword.configuration.session_expiration.from_now,
|
12
|
+
token: generate_friendly_token,
|
13
|
+
remote_addr: remote_addr,
|
14
|
+
return_url: return_url
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def claim(token)
|
19
|
+
current_time = Time.zone.now
|
20
|
+
|
21
|
+
session = Session
|
22
|
+
.where(token: token, claimed_at: nil)
|
23
|
+
.where("expires_at > ?", current_time)
|
24
|
+
.first
|
25
|
+
|
26
|
+
if session.present?
|
27
|
+
session.claimed_at = current_time
|
28
|
+
session.save
|
29
|
+
|
30
|
+
return session
|
31
|
+
end
|
32
|
+
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def expire_unclaimed_session(email)
|
39
|
+
current_time = Time.zone.now
|
40
|
+
|
41
|
+
Session
|
42
|
+
.where(email: email, claimed_at: nil)
|
43
|
+
.where("expires_at > ?", current_time)
|
44
|
+
.update_all(expires_at: current_time)
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_friendly_token
|
48
|
+
"#{SecureRandom.alphanumeric(4)}-#{SecureRandom.random_number(10_000)}-#{SecureRandom.alphanumeric(4)}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>No Password</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<%= csp_meta_tag %>
|
8
|
+
|
9
|
+
<%= stylesheet_link_tag "no_password/tailwind", "inter-font", "data-turbo-track": "reload" %>
|
10
|
+
<%= stylesheet_link_tag "no_password/application", media: "all", "data-turbo-track": "reload" %>
|
11
|
+
|
12
|
+
<%= no_password_importmap_tags %>
|
13
|
+
</head>
|
14
|
+
|
15
|
+
<body>
|
16
|
+
<div aria-live="assertive" class="fixed inset-0 z-50 flex items-end px-4 py-6 pointer-events-none sm:p-6 sm:items-start">
|
17
|
+
<%= turbo_frame_tag "notifications", class: "w-full flex flex-col items-center space-y-4 sm:items-end" do %>
|
18
|
+
<%= render partial: "notification" %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<section>
|
23
|
+
<%= yield %>
|
24
|
+
</section>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,106 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="es" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<meta name="x-apple-disable-message-reformatting">
|
7
|
+
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
9
|
+
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
10
|
+
<!--[if mso]>
|
11
|
+
<xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml>
|
12
|
+
<style>
|
13
|
+
td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
|
14
|
+
</style>
|
15
|
+
<![endif]-->
|
16
|
+
<title>
|
17
|
+
<%= @title %>
|
18
|
+
</title>
|
19
|
+
<style>
|
20
|
+
.hover-underline:hover {
|
21
|
+
text-decoration: underline !important;
|
22
|
+
}
|
23
|
+
|
24
|
+
@media (max-width: 600px) {
|
25
|
+
.sm-h-32 {
|
26
|
+
height: 32px !important;
|
27
|
+
}
|
28
|
+
|
29
|
+
.sm-w-full {
|
30
|
+
width: 100% !important;
|
31
|
+
}
|
32
|
+
|
33
|
+
.sm-px-24 {
|
34
|
+
padding-left: 24px !important;
|
35
|
+
padding-right: 24px !important;
|
36
|
+
}
|
37
|
+
|
38
|
+
.sm-pb-12 {
|
39
|
+
padding-bottom: 12px !important;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
</style>
|
43
|
+
</head>
|
44
|
+
|
45
|
+
<body
|
46
|
+
style="margin: 0; width: 100%; padding: 0; word-break: break-word; -webkit-font-smoothing: antialiased; height: 100%; background-color: #f3f4f6;">
|
47
|
+
<div role="article" aria-roledescription="email" aria-label="<%= @title %>" lang="es">
|
48
|
+
<table
|
49
|
+
style="height: 100%; width: 100%; font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;"
|
50
|
+
cellpadding="0" cellspacing="0" role="presentation">
|
51
|
+
<tr>
|
52
|
+
<td align="center"
|
53
|
+
style="background-color: #f3f4f6; padding-top: 24px; padding-bottom: 24px; vertical-align: middle;"
|
54
|
+
valign="middle">
|
55
|
+
<table class="sm-w-full" style="width: 600px;" cellpadding="0" cellspacing="0" role="presentation">
|
56
|
+
<tr>
|
57
|
+
<td class="sm-pb-12" style="padding-bottom: 24px; text-align: center;">
|
58
|
+
<h1 style="font-size: 24px; font-weight: 600; color: #000000;">
|
59
|
+
<%= @title %>
|
60
|
+
</h1>
|
61
|
+
</td>
|
62
|
+
</tr>
|
63
|
+
<tr>
|
64
|
+
<td align="center" class="sm-px-24">
|
65
|
+
<table class="sm-w-full" style="width: 75%;" cellpadding="0" cellspacing="0" role="presentation">
|
66
|
+
<tr>
|
67
|
+
<td>
|
68
|
+
<div class="sm-px-24" style="overflow: hidden; border-radius: 6px; background-color: #ffffff; padding: 36px; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);">
|
69
|
+
<%= image_tag("no_password/aoo.svg", style: "height: 3rem; width: auto; margin-bottom: 2rem;")%>
|
70
|
+
<%= yield %>
|
71
|
+
</div>
|
72
|
+
</td>
|
73
|
+
</tr>
|
74
|
+
<tr>
|
75
|
+
<td style="padding: 32px; text-align: center; font-size: 12px; color: #4b5563;">
|
76
|
+
<%= link_to "https://aoorora.com" do %>
|
77
|
+
<%= image_tag("no_password/aoo.svg",
|
78
|
+
style: "width: 120px;margin-bottom: 24px;") %>
|
79
|
+
<% end %>
|
80
|
+
<p style="margin: 0 0 4px; text-transform: uppercase;">
|
81
|
+
<%= t(".service") %>
|
82
|
+
</p>
|
83
|
+
<p style="margin: 0; font-style: italic;">
|
84
|
+
<%= t(".platform") %>
|
85
|
+
</p>
|
86
|
+
<p style="cursor: default;">
|
87
|
+
<%= link_to "Aoorora" , "https://aoorora.com" , class: "hover-underline" ,
|
88
|
+
style: "color: #4b5563; text-decoration: none;" %> •
|
89
|
+
<%= link_to "OnBoarding" , "https://aoorora.com" , class: "hover-underline" ,
|
90
|
+
style: "color: #4b5563; text-decoration: none;" %> •
|
91
|
+
<%= link_to "Identidad" , "https://aoorora.com/identidad" , class: "hover-underline" ,
|
92
|
+
style: "color: #4b5563; text-decoration: none;" %>
|
93
|
+
</p>
|
94
|
+
</td>
|
95
|
+
</tr>
|
96
|
+
</table>
|
97
|
+
</td>
|
98
|
+
</tr>
|
99
|
+
</table>
|
100
|
+
</td>
|
101
|
+
</tr>
|
102
|
+
</table>
|
103
|
+
</div>
|
104
|
+
</body>
|
105
|
+
|
106
|
+
</html>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<% unless flash.empty? %>
|
2
|
+
<% if flash.alert.present? && flash.alert.is_a?(Hash) %>
|
3
|
+
<% title = flash.alert&.dig(:title) || flash.alert&.dig("title") %>
|
4
|
+
<% description = flash.alert&.dig(:description) || flash.alert&.dig("description") %>
|
5
|
+
<% elsif flash.notice.present? && flash.notice.is_a?(Hash) %>
|
6
|
+
<% title = flash.notice&.dig(:title) || flash.notice&.dig("title") %>
|
7
|
+
<% description = flash.notice&.dig(:description) || flash.notice&.dig("description") %>
|
8
|
+
<% else %>
|
9
|
+
<% title = "Aplicación" %>
|
10
|
+
<% description = flash.notice || flash.alert %>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<div class="flex flex-col items-center w-full space-y-4 sm:items-end" data-controller="alert">
|
14
|
+
<div class="w-full max-w-sm overflow-hidden bg-white rounded-lg shadow-lg pointer-events-auto ring-1 ring-black ring-opacity-5 hidden"
|
15
|
+
data-alert-target="notification"
|
16
|
+
data-transition-enter="transform ease-out duration-300 transition"
|
17
|
+
data-transition-enter-active="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
|
18
|
+
data-transition-enter-to="translate-y-0 opacity-100 sm:translate-x-0"
|
19
|
+
data-transition-leave="transition ease-in duration-100"
|
20
|
+
data-transition-leave-active="opacity-100"
|
21
|
+
data-transition-leave-to="opacity-0">
|
22
|
+
<div class="p-4">
|
23
|
+
<div class="flex items-start">
|
24
|
+
<div class="flex-shrink-0">
|
25
|
+
<% if alert %>
|
26
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
27
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
28
|
+
</svg>
|
29
|
+
<% else %>
|
30
|
+
<svg class="w-6 h-6 text-green-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
32
|
+
</svg>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
<div class="ml-3 w-0 flex-1 pt-0.5">
|
36
|
+
<p class="text-sm font-medium text-gray-900">
|
37
|
+
<%= title %>
|
38
|
+
</p>
|
39
|
+
<p class="mt-1 text-sm text-gray-500">
|
40
|
+
<%= description %>
|
41
|
+
</p>
|
42
|
+
</div>
|
43
|
+
<div class="flex flex-shrink-0 ml-4">
|
44
|
+
<button data-action="alert#close" class="inline-flex text-gray-400 bg-white rounded-md hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-creditario-500">
|
45
|
+
<span class="sr-only">Close</span>
|
46
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
47
|
+
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
48
|
+
</svg>
|
49
|
+
</button>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<%= turbo_frame_tag :form do %>
|
2
|
+
<%= form_with url: no_password.session_confirmations_path, method: "patch", class: "space-y-6", html: { "data-turbo-frame": "_top" } do |form| %>
|
3
|
+
<p class="text-skin-muted px-4 text-center">
|
4
|
+
<%= t("no_password.session_confirmations.edit.description") %>
|
5
|
+
</p>
|
6
|
+
<div>
|
7
|
+
<label class="block text-sm font-medium text-skin-muted"><%= form.label t("form.labels.token") %></label>
|
8
|
+
<div class="mt-1">
|
9
|
+
<%= form.text_field :token, maxlength: 60, required: true, class: "input w-full", type: "text", placeholder: t("form.placeholders.token") %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div>
|
13
|
+
<%= form.submit t("form.submit.login"), class: "w-full flex button button-accented" %>
|
14
|
+
</div>
|
15
|
+
<div>
|
16
|
+
<%= link_to t("no_password.session_confirmations.edit.request_token"), no_password.new_session_path, class: "w-full block text-center text-skin-accented hover:text-skin-accented-hover", "data-turbo": false %>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="h-screen bg-gray-50">
|
2
|
+
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
3
|
+
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
4
|
+
<%= image_tag("no_password/aoo.svg", class:"mx-auto h-12 w-auto", alt: "Workflow")%>
|
5
|
+
<h2 class="mt-6 text-center text-3xl font-bold text-skin-base leading-3">
|
6
|
+
<%= t("no_password.session_confirmations.edit.title") %>
|
7
|
+
</h2>
|
8
|
+
</div>
|
9
|
+
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
10
|
+
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
11
|
+
<%= render partial: "form" %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<%= turbo_frame_tag :form do %>
|
2
|
+
<%= form_with model: @resource, local: true, url: no_password.session_path(return_to: @return_to), class: "space-y-6", html: {"data-turbo": false} do |form| %>
|
3
|
+
<p class="text-skin-muted px-4 text-center">
|
4
|
+
<%= t("no_password.sessions.new.description") %>
|
5
|
+
</p>
|
6
|
+
<div>
|
7
|
+
<label class="block text-sm font-medium text-skin-muted"><%= form.label :email %></label>
|
8
|
+
<div class="mt-1">
|
9
|
+
<%= form.text_field :email, maxlength: 60, required: true, class: "input w-full", type: "email", placeholder: t("form.placeholders.email") %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div>
|
13
|
+
<%= form.submit t("form.submit.login"), class: "w-full flex button button-accented" %>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="h-screen bg-gray-50">
|
2
|
+
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
3
|
+
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
4
|
+
<%= image_tag("no_password/aoo.svg", class:"mx-auto h-12 w-auto", alt: "Workflow")%>
|
5
|
+
<h2 class="mt-6 text-center text-3xl font-bold text-skin-base leading-3">
|
6
|
+
<%= t("no_password.sessions.new.title") %>
|
7
|
+
</h2>
|
8
|
+
</div>
|
9
|
+
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
10
|
+
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
11
|
+
<%= render partial: "form" %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<p style="margin: 0; font-size: 18px;"><%= t("mailers.send_token.greetings") %></p>
|
2
|
+
<p style="font-size: 16px; color: #374151;"><%= t("mailers.send_token.instructions_1") %></p>
|
3
|
+
<p style="text-align:center; font-size: 20px; color: #374151; font-weight: 600;"><%= @friendly_token %></p>
|
4
|
+
<p style="font-size: 16px; color: #374151;"><%= t("mailers.send_token.instructions_2") %></p>
|
5
|
+
<div style="margin: auto 0; text-align: center;">
|
6
|
+
<%= link_to no_password.session_confirmation_url(token: @signed_token), class: "hover-bg-skin-button-accented-hover", style: "display: inline-block; margin: auto; border-radius: 6px; background-color: rgb(80, 70, 137); padding: 16px 24px; font-size: 16px; font-weight: 500; color: rgb(255, 255, 255); text-decoration: none; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);" do %>
|
7
|
+
<!--[if mso]><i style="letter-spacing: 24px; mso-font-width: -100%; mso-text-raise: 26pt;"> </i><![endif]-->
|
8
|
+
<span style="mso-text-raise: 13pt;"><%= t("mailers.send_token.start_session") %></span>
|
9
|
+
<!--[if mso]><i style="letter-spacing: 24px; mso-font-width: -100%;"> </i><![endif]-->
|
10
|
+
<% end %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<hr style="margin-top: 25px; margin-bottom: 25px; border: 1px solid #f3f4f6" />
|
14
|
+
<p style="font-size: 16px; color: #374151;"><%= t("mailers.send_token.instructions_3") %></p>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%= t("mailers.send_token.subject") %>
|
2
|
+
|
3
|
+
<%= t("mailers.send_token.greetings") %>
|
4
|
+
|
5
|
+
<%= t("mailers.send_token.instructions_1") %>
|
6
|
+
|
7
|
+
<%= @friendly_token %>
|
8
|
+
|
9
|
+
<%= t("mailers.send_token.instructions_2_text") %>
|
10
|
+
<%= no_password.session_confirmation_url(token: @signed_token)%>
|
11
|
+
|
12
|
+
<%= t("mailers.send_token.instructions_3") %>
|