searls-auth 0.1.1 → 0.2.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/LICENSE.txt +1 -1
- data/README.md +23 -1
- data/app/controllers/searls/auth/logins_controller.rb +6 -1
- data/app/controllers/searls/auth/verifications_controller.rb +18 -4
- data/app/mailers/searls/auth/login_link_mailer.rb +12 -1
- data/app/views/searls/auth/login_link_mailer/login_link.html.erb +28 -24
- data/app/views/searls/auth/login_link_mailer/login_link.text.erb +7 -2
- data/app/views/searls/auth/verifications/show.html.erb +20 -20
- data/lib/searls/auth/config.rb +5 -0
- data/lib/searls/auth/emails_link.rb +2 -2
- data/lib/searls/auth/version.rb +1 -1
- data/lib/searls/auth.rb +1 -0
- data/script/setup +1 -6
- data/script/test +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 608a7738fece38d0fce18f906f68c5d91122aaf2d274cdcf48dba6b770c04069
|
4
|
+
data.tar.gz: 76d7b5e89479236705a1cb866d7358016510fa9899dffd2474fe9139cfc688bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c23b8352aaeec522b1878bf3b32c721cd14d06ef31b306a23fcb7e8b1ca1b3659a39904409e30c4492724289057b1a4594486e7878d01304b2830a7ed39e3ecf
|
7
|
+
data.tar.gz: 387b365eb74882a67258552f0da17d58913d001ea574fcf4d4c2b0c1487aeac939e6431e210c0b8bbb1471c00bfd01591e3f6aa6969520cee7727850a4d8552f
|
data/CHANGELOG.md
CHANGED
data/LICENSE.txt
CHANGED
@@ -653,7 +653,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|
653
653
|
If the program does terminal interaction, make it output a short
|
654
654
|
notice like this when it starts in an interactive mode:
|
655
655
|
|
656
|
-
|
656
|
+
searls-auth (C) 2025 Searls LLC
|
657
657
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
658
658
|
This is free software, and you are welcome to redistribute it
|
659
659
|
under certain conditions; type `show c' for details.
|
data/README.md
CHANGED
@@ -82,8 +82,30 @@ end
|
|
82
82
|
```
|
83
83
|
As stated in the comment above, you can find each configuration and its default value in the code.
|
84
84
|
|
85
|
+
### Choose your login methods
|
86
|
+
|
87
|
+
By default, users can log in either by clicking a magic link or by entering a 6‑digit code they receive via email. This is controlled by the `auth_methods` configuration:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
# config/initializers/searls_auth.rb
|
91
|
+
Rails.application.config.after_initialize do
|
92
|
+
Searls::Auth.configure do |config|
|
93
|
+
# Defaults:
|
94
|
+
config.auth_methods = [:email_link, :email_otp]
|
95
|
+
|
96
|
+
# Link-only (no code in emails, no OTP input shown):
|
97
|
+
# config.auth_methods = :email_link
|
98
|
+
|
99
|
+
# Code-only (no link in emails; OTP input shown):
|
100
|
+
# config.auth_methods = :email_otp
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
One reason you might want to disable e-mail OTP is that it exposes your users to [a pretty easy-to-implement man-in-the-middle attack](https://blog.danielh.cc/blog/passwords).
|
106
|
+
|
85
107
|
## Use it
|
86
108
|
|
87
109
|
Of course, having a user be "logged in" or not doesn't mean anything if your application doesn't do anything with the knowledge. Users that are logged in will have `session[:user_id]` set to the value of the logged-in user's ID. Logged out users won't have anything set to `session[:user_id]`. What you do with that is your job, not this gem. (Wait, after 20 years does this mean I finally understand the difference between authentication and authorization? Better late than never.)
|
88
110
|
|
89
|
-
If this is your first rodeo and you just read the previous paragraph and thought, _yeah, but now what?_, check out the tail end of the [example app README](/example/simple_app/README.md), which shows an approach that a lot of apps use.
|
111
|
+
If this is your first rodeo and you just read the previous paragraph and thought, _yeah, but now what?_, check out the tail end of the [example app README](/example/simple_app/README.md#5-require-authentication-for-appropriate-actions), which shows an approach that a lot of apps use.
|
@@ -11,7 +11,12 @@ module Searls
|
|
11
11
|
user = searls_auth_config.user_finder_by_email.call(params[:email])
|
12
12
|
|
13
13
|
if user.present?
|
14
|
-
|
14
|
+
if searls_auth_config.auth_methods.include?(:email_otp)
|
15
|
+
attach_short_code_to_session!(user)
|
16
|
+
else
|
17
|
+
clear_short_code_from_session!
|
18
|
+
end
|
19
|
+
|
15
20
|
EmailsLink.new.email(
|
16
21
|
user:,
|
17
22
|
redirect_path: params[:redirect_path],
|
@@ -8,13 +8,27 @@ module Searls
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def create
|
11
|
-
auth_method = params[:short_code].present? ? :
|
11
|
+
auth_method = params[:short_code].present? ? :email_otp : :email_link
|
12
|
+
if auth_method == :email_otp && !searls_auth_config.auth_methods.include?(:email_otp)
|
13
|
+
flash[:error] = searls_auth_config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
|
14
|
+
return redirect_to searls_auth.login_path(
|
15
|
+
redirect_path: params[:redirect_path],
|
16
|
+
redirect_subdomain: params[:redirect_subdomain]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
if auth_method == :email_link && !searls_auth_config.auth_methods.include?(:email_link)
|
20
|
+
flash[:error] = searls_auth_config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
|
21
|
+
return redirect_to searls_auth.login_path(
|
22
|
+
redirect_path: params[:redirect_path],
|
23
|
+
redirect_subdomain: params[:redirect_subdomain]
|
24
|
+
)
|
25
|
+
end
|
12
26
|
authenticator = AuthenticatesUser.new
|
13
27
|
result = case auth_method
|
14
|
-
when :
|
28
|
+
when :email_otp
|
15
29
|
log_short_code_verification_attempt!
|
16
30
|
authenticator.authenticate_by_short_code(params[:short_code], session)
|
17
|
-
when :
|
31
|
+
when :email_link
|
18
32
|
authenticator.authenticate_by_token(params[:token])
|
19
33
|
end
|
20
34
|
|
@@ -36,7 +50,7 @@ module Searls
|
|
36
50
|
redirect_to searls_auth_config.resolve(:default_redirect_path_after_login,
|
37
51
|
result.user, params, request, main_app)
|
38
52
|
end
|
39
|
-
elsif auth_method == :
|
53
|
+
elsif auth_method == :email_otp
|
40
54
|
if result.exceeded_short_code_attempt_limit?
|
41
55
|
clear_short_code_from_session!
|
42
56
|
flash[:error] = searls_auth_config.resolve(
|
@@ -11,7 +11,7 @@ module Searls
|
|
11
11
|
|
12
12
|
mail(
|
13
13
|
to: format_to(@user),
|
14
|
-
subject:
|
14
|
+
subject: mail_subject,
|
15
15
|
template_path: @config.mail_login_template_path,
|
16
16
|
template_name: @config.mail_login_template_name
|
17
17
|
) do |format|
|
@@ -19,6 +19,17 @@ module Searls
|
|
19
19
|
format.text
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def mail_subject
|
26
|
+
methods = Searls::Auth.config.auth_methods
|
27
|
+
if methods.include?(:email_otp) && @short_code.present?
|
28
|
+
"Your #{searls_auth_helper.rpad(@config.app_name)}login code is #{@short_code}"
|
29
|
+
else
|
30
|
+
"Your #{searls_auth_helper.rpad(@config.app_name)}login link"
|
31
|
+
end
|
32
|
+
end
|
22
33
|
end
|
23
34
|
end
|
24
35
|
end
|
@@ -6,32 +6,36 @@
|
|
6
6
|
Hello!
|
7
7
|
<% end %>
|
8
8
|
</p>
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
<% if Searls::Auth.config.auth_methods.include?(:email_link) %>
|
10
|
+
<p style="margin-top: 1rem;">
|
11
|
+
You can log in by clicking this button:
|
12
|
+
<div style="margin-top: 2rem; text-align: center;">
|
13
|
+
<%= link_to verify_token_url({
|
14
|
+
token: @token,
|
15
|
+
redirect_path: @redirect_path,
|
16
|
+
redirect_subdomain: @redirect_subdomain
|
17
|
+
}.compact_blank) do %>
|
18
|
+
<span style="width: 70%; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); display: inline-block; padding-left: 2rem; padding-right: 2rem; padding-top: 1rem; padding-bottom: 1rem; font-size: 1.5rem; font-weight: 700; border-radius: 0.75rem; background-color: <%= @config.email_button_color %>; color: white;">Log in</span>
|
19
|
+
<% end %>
|
20
|
+
<div style="margin-top: 0.5rem; font-size: 0.875rem; color: #6b7280;">
|
21
|
+
(This link will expire in <%= @config.token_expiry_minutes %> minutes.)
|
22
|
+
</div>
|
21
23
|
</div>
|
22
|
-
</
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
<
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
</p>
|
25
|
+
<% end %>
|
26
|
+
<% if Searls::Auth.config.auth_methods.include?(:email_otp) && @short_code.present? %>
|
27
|
+
<p style="margin-top: 2rem;">
|
28
|
+
You can log in by entering this code:
|
29
|
+
<div style="margin-top: 2rem; text-align: center;">
|
30
|
+
<span id="one-time-code" style="width: 70%; display: inline-block; padding-left: 2rem; padding-right: 2rem; padding-top: 1rem; padding-bottom: 1rem; font-size: 1.5rem; font-weight: 700; border: 1px solid; border-radius: 0.75rem; background-color: #f3f4f6; border-color: #e5e7eb;">
|
31
|
+
<%= @short_code %>
|
32
|
+
</span>
|
33
|
+
<div style="margin-top: 0.5rem; font-size: 0.875rem; color: #6b7280;">
|
34
|
+
(So will this code.)
|
35
|
+
</div>
|
32
36
|
</div>
|
33
|
-
</
|
34
|
-
|
37
|
+
</p>
|
38
|
+
<% end %>
|
35
39
|
<p style="margin-top: 2rem; font-size: 0.875rem; color: #6b7280;">
|
36
40
|
p.s. Didn't try to log in? You can safely ignore this email. If this keeps happening, please <%= link_to "let us know", "mailto:#{@config.support_email_address}" %>.
|
37
41
|
</p>
|
@@ -4,17 +4,22 @@ Hi, <%= user_name %>!
|
|
4
4
|
Hello!
|
5
5
|
<% end %>
|
6
6
|
|
7
|
-
|
7
|
+
<% if Searls::Auth.config.auth_methods.include?(:email_link) %>
|
8
|
+
You can log in by visiting this URL for your <%= searls_auth_helper.rpad(@config.app_name) %>account:
|
8
9
|
|
9
10
|
<%= searls_auth.verify_token_url({
|
10
11
|
token: @token,
|
11
12
|
redirect_path: @redirect_path,
|
12
13
|
redirect_subdomain: @redirect_subdomain
|
13
14
|
}.compact_blank) %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% if Searls::Auth.config.auth_methods.include?(:email_otp) && @short_code.present? %>
|
14
18
|
|
15
|
-
|
19
|
+
You can log in by entering this code:
|
16
20
|
|
17
21
|
<%= @short_code %>
|
22
|
+
<% end %>
|
18
23
|
|
19
24
|
Didn't try to log in? You can safely ignore this email.<%= " If this keeps
|
20
25
|
happening, please let us know at #{@config.support_email_address}" if @config.support_email_address.present? %>
|
@@ -1,24 +1,24 @@
|
|
1
1
|
<h1>Check your email!</h1>
|
2
|
+
<% parts = { email_link: "a link", email_otp: "a six-digit code" }.slice(*Searls::Auth.config.auth_methods).values %>
|
2
3
|
<p>
|
3
|
-
In the next few moments, you should receive an email
|
4
|
-
two ways to log in: a link and a six-digit code that you can enter below.
|
4
|
+
In the next few moments, you should receive an email with <%= parts.to_sentence %> to log in.
|
5
5
|
</p>
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
6
|
+
|
7
|
+
<% if Searls::Auth.config.auth_methods.include?(:email_otp) %>
|
8
|
+
<%= form_with(url: searls_auth.verify_path, method: :post, data: { turbo: searls_auth_helper.enable_turbo? }) do |f| %>
|
9
|
+
<%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
|
10
|
+
<%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
|
11
|
+
<div data-controller="<%= searls_auth_helper.otp_stimulus_controller %>">
|
12
|
+
<%= f.label :short_code, "Code" %>
|
13
|
+
<%= f.text_field :short_code,
|
14
|
+
maxlength: 6,
|
15
|
+
inputmode: "numeric",
|
16
|
+
pattern: "\\d{6}",
|
17
|
+
autocomplete: "one-time-code",
|
18
|
+
title: "six-digit code that was emailed to you",
|
19
|
+
data: searls_auth_helper.otp_field_stimulus_data
|
20
|
+
%>
|
21
|
+
</div>
|
22
|
+
<%= f.submit "Log in" %>
|
23
|
+
<% end %>
|
24
24
|
<% end %>
|
data/lib/searls/auth/config.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Searls
|
2
2
|
module Auth
|
3
3
|
Config = Struct.new(
|
4
|
+
:auth_methods, # array of symbols, e.g., [:email_link, :email_otp]
|
4
5
|
# Data setup
|
5
6
|
:user_finder_by_email, # proc(email)
|
6
7
|
:user_finder_by_id, # proc(id)
|
@@ -53,6 +54,10 @@ module Searls
|
|
53
54
|
self[option]
|
54
55
|
end
|
55
56
|
end
|
57
|
+
|
58
|
+
def auth_methods
|
59
|
+
Array(self[:auth_methods]).map(&:to_sym)
|
60
|
+
end
|
56
61
|
end
|
57
62
|
end
|
58
63
|
end
|
@@ -4,8 +4,8 @@ module Searls
|
|
4
4
|
def email(user:, short_code:, redirect_path: nil, redirect_subdomain: nil)
|
5
5
|
LoginLinkMailer.with(
|
6
6
|
user:,
|
7
|
-
token: generate_token!(user),
|
8
|
-
short_code
|
7
|
+
token: (Searls::Auth.config.auth_methods.include?(:email_link) ? generate_token!(user) : nil),
|
8
|
+
short_code: (Searls::Auth.config.auth_methods.include?(:email_otp) ? short_code : nil),
|
9
9
|
redirect_path:,
|
10
10
|
redirect_subdomain:
|
11
11
|
).login_link.deliver_later
|
data/lib/searls/auth/version.rb
CHANGED
data/lib/searls/auth.rb
CHANGED
data/script/setup
CHANGED
@@ -5,10 +5,5 @@ set -e
|
|
5
5
|
bundle
|
6
6
|
|
7
7
|
cd example/simple_app
|
8
|
-
|
9
|
-
bin/rake db:setup
|
10
|
-
export PLAYWRIGHT_CLI_VERSION=$(bundle exec ruby -e 'require "playwright"; puts Playwright::COMPATIBLE_PLAYWRIGHT_VERSION.strip')
|
11
|
-
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn add -D "playwright@$PLAYWRIGHT_CLI_VERSION"
|
12
|
-
yarn run playwright install chromium
|
13
|
-
|
8
|
+
./script/setup
|
14
9
|
cd ../..
|
data/script/test
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searls-auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '0'
|
86
86
|
requirements: []
|
87
|
-
rubygems_version: 3.6.
|
87
|
+
rubygems_version: 3.6.9
|
88
88
|
specification_version: 4
|
89
89
|
summary: Searls-flavored login for Rails apps
|
90
90
|
test_files: []
|