passwordless 0.10.0 → 0.11.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/README.md +57 -7
- data/app/controllers/passwordless/sessions_controller.rb +10 -8
- data/app/mailers/passwordless/mailer.rb +1 -2
- data/app/models/passwordless/session.rb +12 -6
- data/config/routes.rb +4 -4
- data/db/migrate/20171104221735_create_passwordless_sessions.rb +8 -7
- data/lib/generators/passwordless/views_generator.rb +15 -0
- data/lib/passwordless/controller_helpers.rb +15 -9
- data/lib/passwordless/engine.rb +4 -2
- data/lib/passwordless/errors.rb +4 -2
- data/lib/passwordless/model_helpers.rb +3 -1
- data/lib/passwordless/router_helpers.rb +4 -2
- data/lib/passwordless/test_helpers.rb +43 -0
- data/lib/passwordless/version.rb +2 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfea8774f73a80e003f7257caa02afc7c1475a87077a4881b2c1e85442ebf8e9
|
4
|
+
data.tar.gz: 4882a066aa2ecc18a4a170e697014b5f09b9bde58c32e821eb60944c9fb90b13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f71185082eb25883c1a7778276c244cd8e4ada2ee1c76137b2838c08b40ed8687b1db3eae2ed869f376be762d03d0ad7978c481d73f42c2338be0b1d200cb07c
|
7
|
+
data.tar.gz: 4ad367839721156af66ee6a76786acae842636eda04841ce1a92012dcc245ddd20414d00108908cd27190fa01e4636c88c4668012b893700f6e67ce604ca979f
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
<br />
|
5
5
|
</p>
|
6
6
|
|
7
|
-
[](https://github.com/mikker/passwordless/actions/workflows/ci.yml) [](https://rubygems.org/gems/passwordless) [](https://codecov.io/gh/mikker/passwordless)
|
8
8
|
|
9
9
|
Add authentication to your Rails app without all the icky-ness of passwords.
|
10
10
|
|
@@ -26,6 +26,8 @@ Add authentication to your Rails app without all the icky-ness of passwords.
|
|
26
26
|
* [Token and Session Expiry](#token-and-session-expiry)
|
27
27
|
* [Redirecting back after sign-in](#redirecting-back-after-sign-in)
|
28
28
|
* [Claiming tokens](#claiming-tokens)
|
29
|
+
* [Supporting UUID primary keys](#supporting-uuid-primary-keys)
|
30
|
+
* [Testing helpers](#testing-helpers)
|
29
31
|
* [E-mail security](#e-mail-security)
|
30
32
|
* [License](#license)
|
31
33
|
|
@@ -48,7 +50,7 @@ $ bin/rails passwordless:install:migrations
|
|
48
50
|
|
49
51
|
Passwordless creates a single model called `Passwordless::Session`. It doesn't come with its own `User` model, it expects you to create one:
|
50
52
|
|
51
|
-
```
|
53
|
+
```sh
|
52
54
|
$ bin/rails generate model User email
|
53
55
|
```
|
54
56
|
|
@@ -111,7 +113,9 @@ end
|
|
111
113
|
|
112
114
|
### Providing your own templates
|
113
115
|
|
114
|
-
Override `passwordless`' bundled views by adding your own.
|
116
|
+
Override `passwordless`' bundled views by adding your own. You can manually copy the specific views that you need or copy them to your application with `rails generate passwordless:views`.
|
117
|
+
|
118
|
+
`passwordless` has 2 action views and 1 mailer view:
|
115
119
|
|
116
120
|
```sh
|
117
121
|
# the form where the user inputs their email address
|
@@ -131,6 +135,8 @@ If you'd like to let the user know whether or not a record was found, `@resource
|
|
131
135
|
<% end %>
|
132
136
|
```
|
133
137
|
|
138
|
+
Please note that, from a security standpoint, this is a **bad practice** because you'd be giving information about which users are registered on your system. It is recommended to use a single message similar to the default one: "If we found you in the system, we've sent you an email". The **best practice** is to never expose which emails are registered on your system.
|
139
|
+
|
134
140
|
See [the bundled views](https://github.com/mikker/passwordless/tree/master/app/views/passwordless).
|
135
141
|
|
136
142
|
### Registering new users
|
@@ -177,7 +183,7 @@ By default, magic link will send by email. You can customize this method. For ex
|
|
177
183
|
|
178
184
|
config/initializers/passwordless.rb
|
179
185
|
|
180
|
-
```
|
186
|
+
```ruby
|
181
187
|
Passwordless.after_session_save = lambda do |session, request|
|
182
188
|
# Default behavior is
|
183
189
|
# Passwordless::Mailer.magic_link(session).deliver_now
|
@@ -195,7 +201,7 @@ Currently there is not an officially supported way to generate your own magic li
|
|
195
201
|
|
196
202
|
However, you can accomplish this with the following snippet of code.
|
197
203
|
|
198
|
-
```
|
204
|
+
```ruby
|
199
205
|
session = Passwordless::Session.new({
|
200
206
|
authenticatable: @manager,
|
201
207
|
user_agent: 'Command Line',
|
@@ -319,7 +325,7 @@ config/initializers/passwordless.rb
|
|
319
325
|
Passwordless.restrict_token_reuse = true
|
320
326
|
```
|
321
327
|
|
322
|
-
#### Upgrading an existing Rails app
|
328
|
+
#### Upgrading an existing Rails app to use claim token
|
323
329
|
|
324
330
|
The simplest way to update your sessions table is with a single migration:
|
325
331
|
|
@@ -340,11 +346,55 @@ end
|
|
340
346
|
```
|
341
347
|
</details>
|
342
348
|
|
349
|
+
### Supporting UUID primary keys
|
350
|
+
|
351
|
+
If your `users` table uses UUIDs for its primary keys, you will need to add a migration
|
352
|
+
to change the type of `passwordless`' `authenticatable_id` field to match your primary key type (this will also involve dropping and recreating associated indices).
|
353
|
+
|
354
|
+
Here is an example migration you can use:
|
355
|
+
```ruby
|
356
|
+
class SupportUuidInPasswordlessSessions < ActiveRecord::Migration[6.0]
|
357
|
+
def change
|
358
|
+
remove_index :passwordless_sessions, column: [:authenticatable_type, :authenticatable_id] if index_exists? :authenticatable_type, :authenticatable_id
|
359
|
+
remove_column :passwordless_sessions, :authenticatable_id
|
360
|
+
add_column :passwordless_sessions, :authenticatable_id, :uuid
|
361
|
+
add_index :passwordless_sessions, [:authenticatable_type, :authenticatable_id], name: 'authenticatable'
|
362
|
+
end
|
363
|
+
end
|
364
|
+
```
|
365
|
+
|
366
|
+
Alternatively, you can use `add_reference` with `type: :uuid` in your migration (see docs [here](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference)).
|
367
|
+
|
368
|
+
## Testing helpers
|
369
|
+
|
370
|
+
To help with testing, a set of test helpers are provided.
|
371
|
+
|
372
|
+
If you are using RSpec, add the following line to your `spec/rails_helper.rb` or
|
373
|
+
`spec/spec_helper.rb` if `rails_helper.rb` does not exist:
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
require "passwordless/test_helpers"
|
377
|
+
```
|
378
|
+
|
379
|
+
If you are using TestUnit, add this line to your `test/test_helper.rb`:
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
require "passwordless/test_helpers"
|
383
|
+
```
|
384
|
+
|
385
|
+
|
386
|
+
Then in your controller, request, and system tests/specs, you can utilize the following methods:
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
passwordless_sign_in(user) # signs you in as a user
|
390
|
+
passwordless_sign_out # signs out user
|
391
|
+
```
|
392
|
+
|
343
393
|
## E-mail security
|
344
394
|
|
345
395
|
There's no reason that this approach should be less secure than the usual username/password combo. In fact this is most often a more secure option, as users don't get to choose the weak passwords they still use. In a way this is just the same as having each user go through "Forgot password" on every login.
|
346
396
|
|
347
|
-
But be aware that when everyone authenticates via emails you send, the way you send those mails becomes a weak spot. Email services usually provide a log of all the mails you send so if your app's account is compromised, every user in the system is as well. (This is the same for "Forgot password".) [Reddit was compromised](https://thenextweb.com/hardfork/2018/01/05/reddit-bitcoin-cash-hack/) using this method.
|
397
|
+
But be aware that when everyone authenticates via emails you send, the way you send those mails becomes a weak spot. Email services usually provide a log of all the mails you send so if your app's account is compromised, every user in the system is as well. (This is the same for "Forgot password".) [Reddit was compromised](https://thenextweb.com/hardfork/2018/01/05/reddit-bitcoin-cash-stolen-hack/) using this method.
|
348
398
|
|
349
399
|
Ideally you should set up your email provider to not log these mails. And be sure to turn on 2-factor auth if your provider supports it.
|
350
400
|
|
@@ -29,9 +29,11 @@ module Passwordless
|
|
29
29
|
else
|
30
30
|
Passwordless.after_session_save.call(session)
|
31
31
|
end
|
32
|
-
end
|
33
32
|
|
34
|
-
|
33
|
+
render :create, status: :ok
|
34
|
+
else
|
35
|
+
render :create, status: :unprocessable_entity
|
36
|
+
end
|
35
37
|
end
|
36
38
|
|
37
39
|
# get '/sign_in/:token'
|
@@ -43,23 +45,23 @@ module Passwordless
|
|
43
45
|
def show
|
44
46
|
# Make it "slow" on purpose to make brute-force attacks more of a hassle
|
45
47
|
BCrypt::Password.create(params[:token])
|
46
|
-
sign_in
|
48
|
+
sign_in(passwordless_session)
|
47
49
|
|
48
|
-
redirect_to
|
50
|
+
redirect_to(passwordless_success_redirect_path)
|
49
51
|
rescue Errors::TokenAlreadyClaimedError
|
50
52
|
flash[:error] = I18n.t(".passwordless.sessions.create.token_claimed")
|
51
|
-
redirect_to
|
53
|
+
redirect_to(passwordless_failure_redirect_path)
|
52
54
|
rescue Errors::SessionTimedOutError
|
53
55
|
flash[:error] = I18n.t(".passwordless.sessions.create.session_expired")
|
54
|
-
redirect_to
|
56
|
+
redirect_to(passwordless_failure_redirect_path)
|
55
57
|
end
|
56
58
|
|
57
59
|
# match '/sign_out', via: %i[get delete].
|
58
60
|
# Signs user out. Redirects to root_path
|
59
61
|
# @see ControllerHelpers#sign_out
|
60
62
|
def destroy
|
61
|
-
sign_out
|
62
|
-
redirect_to
|
63
|
+
sign_out(authenticatable_class)
|
64
|
+
redirect_to(passwordless_sign_out_redirect_path)
|
63
65
|
end
|
64
66
|
|
65
67
|
protected
|
@@ -10,8 +10,7 @@ module Passwordless
|
|
10
10
|
def magic_link(session)
|
11
11
|
@session = session
|
12
12
|
|
13
|
-
@magic_link = send(Passwordless.mounted_as)
|
14
|
-
.token_sign_in_url(session.token)
|
13
|
+
@magic_link = send(Passwordless.mounted_as).token_sign_in_url(session.token)
|
15
14
|
|
16
15
|
email_field = @session.authenticatable.class.passwordless_email_field
|
17
16
|
mail(
|
@@ -4,10 +4,13 @@ module Passwordless
|
|
4
4
|
# The session responsible for holding the connection between the record
|
5
5
|
# trying to log in and the unique tokens.
|
6
6
|
class Session < ApplicationRecord
|
7
|
-
belongs_to
|
8
|
-
|
7
|
+
belongs_to(
|
8
|
+
:authenticatable,
|
9
|
+
polymorphic: true,
|
10
|
+
inverse_of: :passwordless_sessions
|
11
|
+
)
|
9
12
|
|
10
|
-
validates
|
13
|
+
validates(
|
11
14
|
:authenticatable,
|
12
15
|
:timeout_at,
|
13
16
|
:expires_at,
|
@@ -15,16 +18,19 @@ module Passwordless
|
|
15
18
|
:remote_addr,
|
16
19
|
:token,
|
17
20
|
presence: true
|
21
|
+
)
|
18
22
|
|
19
23
|
before_validation :set_defaults
|
20
24
|
|
21
|
-
scope
|
22
|
-
|
23
|
-
|
25
|
+
scope(
|
26
|
+
:available,
|
27
|
+
lambda { where("expires_at > ?", Time.current) }
|
28
|
+
)
|
24
29
|
|
25
30
|
def self.valid
|
26
31
|
available
|
27
32
|
end
|
33
|
+
|
28
34
|
class << self
|
29
35
|
deprecate :valid, deprecator: SessionValidDeprecation
|
30
36
|
end
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Passwordless::Engine.routes.draw do
|
4
|
-
get
|
5
|
-
post
|
6
|
-
get
|
7
|
-
match
|
4
|
+
get("/sign_in", to: "sessions#new", as: :sign_in)
|
5
|
+
post("/sign_in", to: "sessions#create")
|
6
|
+
get("/sign_in/:token", to: "sessions#show", as: :token_sign_in)
|
7
|
+
match("/sign_out", to: "sessions#destroy", via: %i[get delete], as: :sign_out)
|
8
8
|
end
|
@@ -2,18 +2,19 @@
|
|
2
2
|
|
3
3
|
class CreatePasswordlessSessions < ActiveRecord::Migration[5.1]
|
4
4
|
def change
|
5
|
-
create_table
|
5
|
+
create_table(:passwordless_sessions) do |t|
|
6
6
|
t.belongs_to(
|
7
7
|
:authenticatable,
|
8
8
|
polymorphic: true,
|
9
9
|
index: {name: "authenticatable"}
|
10
10
|
)
|
11
|
-
|
12
|
-
t.datetime
|
13
|
-
t.datetime :
|
14
|
-
t.
|
15
|
-
t.
|
16
|
-
t.string
|
11
|
+
|
12
|
+
t.datetime(:timeout_at, null: false)
|
13
|
+
t.datetime(:expires_at, null: false)
|
14
|
+
t.datetime(:claimed_at)
|
15
|
+
t.text(:user_agent, null: false)
|
16
|
+
t.string(:remote_addr, null: false)
|
17
|
+
t.string(:token, null: false)
|
17
18
|
|
18
19
|
t.timestamps
|
19
20
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Passwordless
|
4
|
+
module Generators
|
5
|
+
class ViewsGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../../../app/views/passwordless', __dir__)
|
7
|
+
|
8
|
+
def install
|
9
|
+
copy_file 'mailer/magic_link.text.erb', 'app/views/passwordless/mailer/magic_link.text.erb'
|
10
|
+
copy_file 'sessions/new.html.erb', 'app/views/passwordless/sessions/new.html.erb'
|
11
|
+
copy_file 'sessions/create.html.erb', 'app/views/passwordless/sessions.create.html.erb'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -39,6 +39,7 @@ module Passwordless
|
|
39
39
|
|
40
40
|
authenticate_by_session(authenticatable_class)
|
41
41
|
end
|
42
|
+
|
42
43
|
deprecate :authenticate_by_cookie, deprecator: CookieDeprecation
|
43
44
|
|
44
45
|
def upgrade_passwordless_cookie(authenticatable_class)
|
@@ -51,7 +52,7 @@ module Passwordless
|
|
51
52
|
return unless (record = authenticatable_class.find_by(id: authenticatable_id))
|
52
53
|
new_session = build_passwordless_session(record).tap { |s| s.save! }
|
53
54
|
|
54
|
-
sign_in
|
55
|
+
sign_in(new_session)
|
55
56
|
|
56
57
|
new_session.authenticatable
|
57
58
|
end
|
@@ -73,20 +74,25 @@ module Passwordless
|
|
73
74
|
# to sign in
|
74
75
|
# @return [ActiveRecord::Base] the record that is passed in.
|
75
76
|
def sign_in(record)
|
76
|
-
passwordless_session =
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
passwordless_session = if record.is_a?(Passwordless::Session)
|
78
|
+
record
|
79
|
+
else
|
80
|
+
warn(
|
81
|
+
"Passwordless::ControllerHelpers#sign_in with authenticatable " \
|
81
82
|
"(`#{record.class}') is deprecated. Falling back to creating a " \
|
82
83
|
"new Passwordless::Session"
|
83
|
-
|
84
|
-
|
84
|
+
)
|
85
|
+
build_passwordless_session(record).tap { |s| s.save! }
|
86
|
+
end
|
85
87
|
|
86
88
|
passwordless_session.claim! if Passwordless.restrict_token_reuse
|
87
89
|
|
88
90
|
raise Passwordless::Errors::SessionTimedOutError if passwordless_session.timed_out?
|
89
91
|
|
92
|
+
old_session = session.dup.to_hash
|
93
|
+
reset_session
|
94
|
+
old_session.each_pair { |k, v| session[k.to_sym] = v }
|
95
|
+
|
90
96
|
key = session_key(passwordless_session.authenticatable_type)
|
91
97
|
session[key] = passwordless_session.id
|
92
98
|
|
@@ -105,8 +111,8 @@ module Passwordless
|
|
105
111
|
key = cookie_name(authenticatable_class)
|
106
112
|
cookies.encrypted.permanent[key] = {value: nil}
|
107
113
|
cookies.delete(key)
|
108
|
-
# /deprecated
|
109
114
|
|
115
|
+
# /deprecated
|
110
116
|
reset_session
|
111
117
|
true
|
112
118
|
end
|
data/lib/passwordless/engine.rb
CHANGED
@@ -7,15 +7,17 @@ module Passwordless
|
|
7
7
|
|
8
8
|
config.to_prepare do
|
9
9
|
require "passwordless/router_helpers"
|
10
|
+
|
10
11
|
ActionDispatch::Routing::Mapper.include RouterHelpers
|
11
12
|
require "passwordless/model_helpers"
|
13
|
+
|
12
14
|
ActiveRecord::Base.extend ModelHelpers
|
13
15
|
require "passwordless/controller_helpers"
|
16
|
+
|
14
17
|
end
|
15
18
|
|
16
19
|
config.before_initialize do |app|
|
17
|
-
app.config.i18n.load_path +=
|
18
|
-
Dir[Engine.root.join("config", "locales", "*.yml")]
|
20
|
+
app.config.i18n.load_path += Dir[Engine.root.join("config", "locales", "*.yml")]
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
data/lib/passwordless/errors.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
module Passwordless
|
4
4
|
module Errors
|
5
5
|
# Raise this exception when a session is expired.
|
6
|
-
class SessionTimedOutError < StandardError
|
6
|
+
class SessionTimedOutError < StandardError
|
7
|
+
end
|
7
8
|
|
8
9
|
# Raise this exception when the token has been previously claimed
|
9
|
-
class TokenAlreadyClaimedError < StandardError
|
10
|
+
class TokenAlreadyClaimedError < StandardError
|
11
|
+
end
|
10
12
|
end
|
11
13
|
end
|
@@ -8,9 +8,11 @@ module Passwordless
|
|
8
8
|
# field name (e.g. `:email`)
|
9
9
|
# @param field [string] email submitted by user.
|
10
10
|
def passwordless_with(field)
|
11
|
-
has_many
|
11
|
+
has_many(
|
12
|
+
:passwordless_sessions,
|
12
13
|
class_name: "Passwordless::Session",
|
13
14
|
as: :authenticatable
|
15
|
+
)
|
14
16
|
|
15
17
|
define_singleton_method(:passwordless_email_field) { field }
|
16
18
|
end
|
@@ -20,8 +20,10 @@ module Passwordless
|
|
20
20
|
mount_at = at || resource.to_s
|
21
21
|
mount_as = as || resource.to_s
|
22
22
|
mount(
|
23
|
-
Passwordless::Engine,
|
24
|
-
|
23
|
+
Passwordless::Engine,
|
24
|
+
at: mount_at,
|
25
|
+
as: mount_as,
|
26
|
+
defaults: {authenticatable: resource.to_s.singularize}
|
25
27
|
)
|
26
28
|
|
27
29
|
Passwordless.mounted_as = mount_as
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Passwordless
|
2
|
+
module TestHelpers
|
3
|
+
module TestCase
|
4
|
+
def passwordless_sign_out
|
5
|
+
delete Passwordless::Engine.routes.url_helpers.sign_out_path
|
6
|
+
follow_redirect!
|
7
|
+
end
|
8
|
+
|
9
|
+
def passwordless_sign_in(resource)
|
10
|
+
session = Passwordless::Session.create!(authenticatable: resource, user_agent: "TestAgent", remote_addr: "unknown")
|
11
|
+
get Passwordless::Engine.routes.url_helpers.token_sign_in_path(session.token)
|
12
|
+
follow_redirect!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module SystemTestCase
|
17
|
+
def passwordless_sign_out
|
18
|
+
visit Passwordless::Engine.routes.url_helpers.sign_out_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def passwordless_sign_in(resource)
|
22
|
+
session = Passwordless::Session.create!(authenticatable: resource, user_agent: "TestAgent", remote_addr: "unknown")
|
23
|
+
visit Passwordless::Engine.routes.url_helpers.token_sign_in_path(session.token)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if defined?(ActiveSupport::TestCase)
|
30
|
+
ActiveSupport::TestCase.send(:include, ::Passwordless::TestHelpers::TestCase)
|
31
|
+
end
|
32
|
+
|
33
|
+
if defined?(ActionDispatch::SystemTestCase)
|
34
|
+
ActionDispatch::SystemTestCase.send(:include, ::Passwordless::TestHelpers::SystemTestCase)
|
35
|
+
end
|
36
|
+
|
37
|
+
if defined?(RSpec)
|
38
|
+
RSpec.configure do |config|
|
39
|
+
config.include ::Passwordless::TestHelpers::TestCase, type: :request
|
40
|
+
config.include ::Passwordless::TestHelpers::TestCase, type: :controller
|
41
|
+
config.include ::Passwordless::TestHelpers::SystemTestCase, type: :system
|
42
|
+
end
|
43
|
+
end
|
data/lib/passwordless/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passwordless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikkel Malmberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -101,12 +101,14 @@ files:
|
|
101
101
|
- config/locales/en.yml
|
102
102
|
- config/routes.rb
|
103
103
|
- db/migrate/20171104221735_create_passwordless_sessions.rb
|
104
|
+
- lib/generators/passwordless/views_generator.rb
|
104
105
|
- lib/passwordless.rb
|
105
106
|
- lib/passwordless/controller_helpers.rb
|
106
107
|
- lib/passwordless/engine.rb
|
107
108
|
- lib/passwordless/errors.rb
|
108
109
|
- lib/passwordless/model_helpers.rb
|
109
110
|
- lib/passwordless/router_helpers.rb
|
111
|
+
- lib/passwordless/test_helpers.rb
|
110
112
|
- lib/passwordless/url_safe_base_64_generator.rb
|
111
113
|
- lib/passwordless/version.rb
|
112
114
|
homepage: https://github.com/mikker/passwordless
|
@@ -128,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
130
|
- !ruby/object:Gem::Version
|
129
131
|
version: '0'
|
130
132
|
requirements: []
|
131
|
-
rubygems_version: 3.
|
133
|
+
rubygems_version: 3.3.7
|
132
134
|
signing_key:
|
133
135
|
specification_version: 4
|
134
136
|
summary: Add authentication to your app without all the ickyness of passwords.
|