searls-auth 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e2b6983e76f48e3663946462d67f0dfdf62914aa8187c29a332111f430335f2
4
- data.tar.gz: 0cfbab5b379a053ff0f23406e25c045caa6d7ca2c249330d0ac24b459e6f1ecf
3
+ metadata.gz: 608a7738fece38d0fce18f906f68c5d91122aaf2d274cdcf48dba6b770c04069
4
+ data.tar.gz: 76d7b5e89479236705a1cb866d7358016510fa9899dffd2474fe9139cfc688bf
5
5
  SHA512:
6
- metadata.gz: 820ae8d2258753dd6e7f2f9bea7054537f0242aacc5218f220cc3a83d639a11a5204bcca728a95ec06e4ae8094994609abf0859f5df8162684a56c42bd27757b
7
- data.tar.gz: 9d6f9469c3c4d99cd42f0b410f89934951924c605cff3fe9bf7490064d790d1a814b20e154574de45d525b325acaac6ccd793496f2ffbc83645042e4f2a0268f
6
+ metadata.gz: c23b8352aaeec522b1878bf3b32c721cd14d06ef31b306a23fcb7e8b1ca1b3659a39904409e30c4492724289057b1a4594486e7878d01304b2830a7ed39e3ecf
7
+ data.tar.gz: 387b365eb74882a67258552f0da17d58913d001ea574fcf4d4c2b0c1487aeac939e6431e210c0b8bbb1471c00bfd01591e3f6aa6969520cee7727850a4d8552f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2025-09-11
4
+
5
+ * Add `auth_methods` configuration with default `[:email_link, :email_otp]`
6
+
7
+ ## [0.1.1] - 2025-04-27
8
+
9
+ * Improve error message when token generation fails due to a token not being configured on the user model
10
+
3
11
  ## [0.1.0] - 2025-04-26
4
12
 
5
13
  * Add `max_allowed_short_code_attempts` configuration, beyond which the code is erased from the session and the user needs to login again (default: 10)
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
- Fine Ants Copyright (C) 2016 Justin Searls
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
@@ -1 +1,111 @@
1
1
  # searls-auth
2
+
3
+ This gem provides a Ruby on Rails engine that implements a minimal, opinionated, and pleasant email-based authentication system. It has zero other dependencies, which is the correct number of dependencies.
4
+
5
+ For a detailed walk-through with pictures and whatnot, check out this [example app README](/example/simple_app/README.md). Below you'll find the basic steps for getting started.
6
+
7
+ ## Install it
8
+
9
+ Add it to your Gemfile and `bundle` it:
10
+
11
+ ```ruby
12
+ gem "searls-auth"
13
+ ```
14
+
15
+ ## Mount it
16
+
17
+ Next, you need to mount the gem's Engine to host any of the authentication controllers and mailers.
18
+
19
+ You can mount the engine at whatever you path you like (mounting it to "/" can result in some goofy behavior, so maybe not that one). I just do "/auth" because I'm boring:
20
+
21
+ ```ruby
22
+ # config/routes.rb
23
+ Rails.application.routes.draw do
24
+ # …
25
+ mount Searls::Auth::Engine => "/auth"
26
+ # …
27
+ end
28
+ ```
29
+
30
+ If you run your development server and visit [http://localhost:3000/auth/login](http://localhost:3000/auth/login), you should see an ugly login page. (If things look really goofy, it's because the gem defaults to your app's `"application"` layout).
31
+
32
+ ## Secure it
33
+
34
+ If you've got a `User` model with an `email` attribute, you're two-thirds of the way to this thing working. All you need now is to associate [a secure token](https://api.rubyonrails.org/classes/ActiveRecord/TokenFor/ClassMethods.html#method-i-generates_token_for) with the model named `:email_auth`.
35
+
36
+ ```ruby
37
+ # app/models/user.rb
38
+ class User < ApplicationRecord
39
+ # …
40
+ generates_token_for :email_auth, expires_in: 30.minutes
41
+ # …
42
+ end
43
+ ```
44
+
45
+ (You can [name all these things whatever you want](#configure-it), but this is what searls-auth will assume by default.)
46
+
47
+ I'm writing this README as I add searls-auth to my new [POSSE Party](https://posseparty.com) app. As soon as I added the above line I visited [http://localhost:3000/auth/login](http://localhost:3000/auth/login), typed in my email, hit "Log in", and saw this email get sent (thanks to [letter_opener](https://github.com/ryanb/letter_opener)):
48
+
49
+ ![A default searls-auth login email](https://github.com/user-attachments/assets/07114dae-a95b-49bd-ba57-92042c62c1b7)
50
+
51
+ When I pasted in the six-digit code into the (also ugly) default verification page, it auto-submitted the form. That's because my has a vanilla [import maps](https://guides.rubyonrails.org/working_with_javascript_in_rails.html#import-maps) configuration, the least-bad of the various JavaScript ordeals Rails has on offer. (Don't use import maps? Then I leave figuring out how to load the gem's [Stimulus controllers](app/javascript/controllers/searls_auth_login_controller.js) as an exercise to the reader.)
52
+
53
+ I repeated the process to ensure the "magic link" also would have worked by visiting [http://localhost:3000/auth/logout](http://localhost:3000/auth/logout) and then clicking the link.
54
+
55
+ ## Configure it
56
+
57
+ Almost every user-facing thing searls-auth does is configurable, because authentication is an _intimate and precious_ thing that every application must find a way to tweak, brand, and confuse.
58
+
59
+ To configure things, create an initializer:
60
+
61
+ ```
62
+ touch config/initializers/searls_auth.rb
63
+ ```
64
+
65
+ And paste this into it as a starting point:
66
+
67
+ ```ruby
68
+ Rails.application.config.after_initialize do
69
+ Searls::Auth.configure do |config|
70
+ # You can find the defaults here-ish:
71
+ # https://github.com/searlsco/searls-auth/blob/main/lib/searls/auth.rb#L14
72
+ #
73
+ # The expected type of each option is documented inline here-ish:
74
+ # https://github.com/searlsco/searls-auth/blob/main/lib/searls/auth/config.rb#L3
75
+ #
76
+ # (Note that many options can take a proc or a value, which you may want)
77
+ #
78
+ # Override any option like this:
79
+ # config.app_name = "POSSE Party"
80
+ end
81
+ end
82
+ ```
83
+ As stated in the comment above, you can find each configuration and its default value in the code.
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
+
107
+ ## Use it
108
+
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.)
110
+
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
- attach_short_code_to_session!(user)
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? ? :short_code : :token
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 :short_code
28
+ when :email_otp
15
29
  log_short_code_verification_attempt!
16
30
  authenticator.authenticate_by_short_code(params[:short_code], session)
17
- when :token
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 == :short_code
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: "Your #{searls_auth_helper.rpad(@config.app_name)}login code is #{@short_code}",
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
- <p style="margin-top: 1rem;">
10
- You can click the button below to log in to your account:
11
- <div style="margin-top: 2rem; text-align: center;">
12
- <%= link_to verify_token_url({
13
- token: @token,
14
- redirect_path: @redirect_path,
15
- redirect_subdomain: @redirect_subdomain
16
- }.compact_blank) do %>
17
- <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>
18
- <% end %>
19
- <div style="margin-top: 0.5rem; font-size: 0.875rem; color: #6b7280;">
20
- (This link will expire in <%= @config.token_expiry_minutes %> minutes.)
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
- </div>
23
- </p>
24
- <p style="margin-top: 2rem;">
25
- Alternatively, you can type or paste the following code into your browser:
26
- <div style="margin-top: 2rem; text-align: center;">
27
- <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;">
28
- <%= @short_code %>
29
- </span>
30
- <div style="margin-top: 0.5rem; font-size: 0.875rem; color: #6b7280;">
31
- (So will this code.)
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
- </div>
34
- </p>
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
- You can log in to your <%= searls_auth_helper.rpad(@config.app_name) %>account at this URL:
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
- Alternatively, you can type or paste the following code into your browser:
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 that will provide you
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
- <%= form_with(url: searls_auth.verify_path, method: :post, data: {
7
- # Don't use turbo on cross-domain redirects
8
- turbo: searls_auth_helper.enable_turbo?
9
- }) do |f| %>
10
- <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
11
- <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
12
- <div data-controller="<%= searls_auth_helper.otp_stimulus_controller %>">
13
- <%= f.label :short_code, "Code" %>
14
- <%= f.text_field :short_code,
15
- maxlength: 6,
16
- inputmode: "numeric",
17
- pattern: "\\d{6}",
18
- autocomplete: "one-time-code",
19
- title: "six-digit code that was emailed to you",
20
- data: searls_auth_helper.otp_field_stimulus_data
21
- %>
22
- </div>
23
- <%= f.submit "Log in" %>
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 %>
@@ -1,13 +1,14 @@
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
- :user_finder_by_email, # proc (email)
6
- :user_finder_by_id, # proc (id)
7
- :user_finder_by_token, # proc (token)
8
- :user_initializer, # proc (params)
6
+ :user_finder_by_email, # proc(email)
7
+ :user_finder_by_id, # proc(id)
8
+ :user_finder_by_token, # proc(token)
9
+ :user_initializer, # proc(params)
9
10
  :user_name_method, # string
10
- :token_generator, # proc ()
11
+ :token_generator, # proc()
11
12
  :token_expiry_minutes, # integer
12
13
  # Controller setup
13
14
  :preserve_session_keys_after_logout, # array of symbols
@@ -21,11 +22,11 @@ module Searls
21
22
  :mail_login_template_path, # string
22
23
  :mail_login_template_name, # string
23
24
  # Routing setup
24
- :redirect_path_after_register, # string or proc, all new registrations redirect here
25
- :default_redirect_path_after_login, # string or proc, only redirected here if redirect_path param not set
25
+ :redirect_path_after_register, # string or proc(user, params, request, routes), all new registrations redirect here
26
+ :default_redirect_path_after_login, # string or proc(user, params, request, routes), only redirected here if redirect_path param not set
26
27
  # Hook setup
27
- :validate_registration, # proc (user, params, errors = []), must return an array of error messages where empty means valid
28
- :after_login_success, # proc (user)
28
+ :validate_registration, # proc(user, params, errors = []), must return an array of error messages where empty means valid
29
+ :after_login_success, # proc(user)
29
30
  # Branding setup
30
31
  :app_name, # string
31
32
  :app_url, # string
@@ -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,12 +4,31 @@ module Searls
4
4
  def email(user:, short_code:, redirect_path: nil, redirect_subdomain: nil)
5
5
  LoginLinkMailer.with(
6
6
  user:,
7
- token: Searls::Auth.config.token_generator.call(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
12
12
  end
13
+
14
+ private
15
+
16
+ def generate_token!(user)
17
+ Searls::Auth.config.token_generator.call(user)
18
+ rescue KeyError => e
19
+ raise Error, <<~MSG
20
+ Secure token generation for user failed!
21
+
22
+ Message: #{e.message}
23
+ User: #{user.inspect}
24
+
25
+ This can probably be fixed by adding a line like this to your #{user.class.name} class:
26
+
27
+ generates_token_for :email_auth, expires_in: 30.minutes
28
+
29
+ Otherwise, you may want to override searls-auth's "token_generator" setting with a proc of your own.
30
+ MSG
31
+ end
13
32
  end
14
33
  end
15
34
  end
@@ -1,5 +1,5 @@
1
1
  module Searls
2
2
  module Auth
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
data/lib/searls/auth.rb CHANGED
@@ -12,6 +12,7 @@ module Searls
12
12
  class Error < StandardError; end
13
13
 
14
14
  DEFAULT_CONFIG = {
15
+ auth_methods: [:email_link, :email_otp],
15
16
  # Data setup
16
17
  user_finder_by_email: ->(email) { User.find_by(email:) },
17
18
  user_finder_by_id: ->(id) { User.find_by(id:) },
data/script/setup CHANGED
@@ -5,10 +5,5 @@ set -e
5
5
  bundle
6
6
 
7
7
  cd example/simple_app
8
- bundle
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
@@ -7,7 +7,7 @@ bundle exec rake
7
7
 
8
8
  echo "-----> Testing example/simple_app"
9
9
  cd example/simple_app
10
- bin/rake
10
+ ./script/test
11
11
  cd ../..
12
12
 
13
13
  echo "-----> Looks good! 🪩"
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.1.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.7
87
+ rubygems_version: 3.6.9
88
88
  specification_version: 4
89
89
  summary: Searls-flavored login for Rails apps
90
90
  test_files: []