passwordless 1.6.0 → 1.8.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 +88 -2
- data/app/controllers/passwordless/sessions_controller.rb +7 -0
- data/app/mailers/passwordless/mailer.rb +1 -1
- data/db/migrate/20171104221735_create_passwordless_sessions.rb +1 -0
- data/lib/passwordless/config.rb +6 -0
- data/lib/passwordless/constraint.rb +40 -0
- data/lib/passwordless/controller_helpers.rb +1 -1
- data/lib/passwordless/engine.rb +0 -4
- data/lib/passwordless/version.rb +1 -1
- data/lib/passwordless.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0e00cb666dc0018cb5789bf9f3b9186e7d107631365847374aa718d547f872e
|
4
|
+
data.tar.gz: dd7cf0cd6c95151f3987e827693f3bb78811b6eba50661a6c822a54e0788b908
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 664906f92b865e89863f5a886bacc119a1ccc1970eead36d7496a12545a8ce022553f30982bc4b8b852fee9b6cdb4580ae01e4c37d56e59ae52b026c836bbebc
|
7
|
+
data.tar.gz: b02a2dd05dae19dd283a455da70690c8086bfd6958f78986b7b8ac6442fa31bdc627362e12a59f19c9e51f1fa15a7e47fced64c09529ed3c2aa0454cd33bb84a
|
data/README.md
CHANGED
@@ -8,7 +8,24 @@
|
|
8
8
|
|
9
9
|
Add authentication to your Rails app without all the icky-ness of passwords. _Magic link_ authentication, if you will. We call it _passwordless_.
|
10
10
|
|
11
|
-
|
11
|
+
- [Installation](#installation)
|
12
|
+
- [Upgrading](#upgrading)
|
13
|
+
- [Usage](#usage)
|
14
|
+
- [Getting the current user, restricting access, the usual](#getting-the-current-user-restricting-access-the-usual)
|
15
|
+
- [Providing your own templates](#providing-your-own-templates)
|
16
|
+
- [Registering new users](#registering-new-users)
|
17
|
+
- [URLs and links](#urls-and-links)
|
18
|
+
- [Route constraints](#route-constraints)
|
19
|
+
- [Configuration](#configuration)
|
20
|
+
- [Delivery method](#delivery-method)
|
21
|
+
- [Token generation](#token-generation)
|
22
|
+
- [Timeout and Expiry](#timeout-and-expiry)
|
23
|
+
- [Redirection after sign-in](#redirection-after-sign-in)
|
24
|
+
- [Looking up the user](#looking-up-the-user)
|
25
|
+
- [Test helpers](#test-helpers)
|
26
|
+
- [Security considerations](#security-considerations)
|
27
|
+
- [Alternatives](#alternatives)
|
28
|
+
- [License](#license)
|
12
29
|
|
13
30
|
## Installation
|
14
31
|
|
@@ -149,6 +166,59 @@ config.action_mailer.default_url_options = {host: "www.example.com"}
|
|
149
166
|
routes.default_url_options[:host] ||= "www.example.com"
|
150
167
|
```
|
151
168
|
|
169
|
+
Note as well that `passwordless_for` accepts a custom controller. One possible application of this
|
170
|
+
is to add a `before_action` that redirects authenticated users from the sign-in routes, as in this example:
|
171
|
+
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# config/routes.rb
|
175
|
+
passwordless_for :users, controller: "sessions"
|
176
|
+
```
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
# app/controllers/sessions_controller.rb
|
180
|
+
|
181
|
+
class SessionsController < Passwordless::SessionsController
|
182
|
+
before_action :require_unauth!, only: %i[new show]
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def require_unauth!
|
187
|
+
return unless current_user
|
188
|
+
redirect_to("/", notice: "You are already signed in.")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
### Route constraints
|
194
|
+
|
195
|
+
With [constraints](https://guides.rubyonrails.org/routing.html#request-based-constraints) you can restrict access to certain routes.
|
196
|
+
Passwordless provides `Passwordless::Constraint` and it's negative counterpart `Passwordless::ConstraintNot` for this purpose.
|
197
|
+
|
198
|
+
To limit a route to only authenticated `User`s:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
constraints Passwordless::Constraint.new(User) do
|
202
|
+
# ...
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
The constraint takes a second `if:` argument, that expects a block and is passed the `authenticatable` record, (ie. `User`):
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
constraints Passwordless::Constraint.new(User, if: -> (user) { user.email.include?("john") }) do
|
210
|
+
# ...
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
214
|
+
The negated version has the same API but with the opposite result, ie. ensuring authenticated user **don't** have access:
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
constraints Passwordless::ConstraintNot.new(User) do
|
218
|
+
get("/no-users-allowed", to: "secrets#index")
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
152
222
|
## Configuration
|
153
223
|
|
154
224
|
To customize Passwordless, create a file `config/initializers/passwordless.rb`.
|
@@ -160,7 +230,7 @@ Passwordless.configure do |config|
|
|
160
230
|
config.default_from_address = "CHANGE_ME@example.com"
|
161
231
|
config.parent_controller = "ApplicationController"
|
162
232
|
config.parent_mailer = "ActionMailer::Base"
|
163
|
-
config.restrict_token_reuse =
|
233
|
+
config.restrict_token_reuse = true # Can a token/link be used multiple times?
|
164
234
|
config.token_generator = Passwordless::ShortTokenGenerator.new # Used to generate magic link tokens.
|
165
235
|
|
166
236
|
config.expires_at = lambda { 1.year.from_now } # How long until a signed in session expires.
|
@@ -173,6 +243,8 @@ Passwordless.configure do |config|
|
|
173
243
|
config.sign_out_redirect_path = '/' # After a user signs out
|
174
244
|
|
175
245
|
config.paranoid = false # Display email sent notice even when the resource is not found.
|
246
|
+
|
247
|
+
config.after_session_confirm = ->(request, session) {} # Called after a session is confirmed.
|
176
248
|
end
|
177
249
|
```
|
178
250
|
|
@@ -194,6 +266,20 @@ Passwordless.configure do |config|
|
|
194
266
|
end
|
195
267
|
```
|
196
268
|
|
269
|
+
## After Session Confirm Hook
|
270
|
+
|
271
|
+
An `after_session_confirm` hook is called after a successful session confirmation – in other words: after a user signs in successfully.
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
Passwordless.configure do |config|
|
275
|
+
config.after_session_confirm = ->(session, request) {
|
276
|
+
user = session.authenticatable
|
277
|
+
user.update!(
|
278
|
+
email_verified: true.
|
279
|
+
last_login_ip: request.remote_ip
|
280
|
+
)
|
281
|
+
}
|
282
|
+
end
|
197
283
|
### Token generation
|
198
284
|
|
199
285
|
By default Passwordless generates short, 6-digit, alpha numeric tokens. You can change the generator using `Passwordless.config.token_generator` to something else that responds to `call(session)` eg.:
|
@@ -150,6 +150,7 @@ module Passwordless
|
|
150
150
|
def authenticate_and_sign_in(session, token)
|
151
151
|
if session.authenticate(token)
|
152
152
|
sign_in(session)
|
153
|
+
call_after_session_confirm(session, request)
|
153
154
|
redirect_to(
|
154
155
|
passwordless_success_redirect_path(session.authenticatable),
|
155
156
|
status: :see_other,
|
@@ -188,6 +189,12 @@ module Passwordless
|
|
188
189
|
end
|
189
190
|
end
|
190
191
|
|
192
|
+
def call_after_session_confirm(session, request)
|
193
|
+
return unless Passwordless.config.after_session_confirm.respond_to?(:call)
|
194
|
+
|
195
|
+
Passwordless.config.after_session_confirm.call(session, request)
|
196
|
+
end
|
197
|
+
|
191
198
|
def find_authenticatable
|
192
199
|
if authenticatable_class.respond_to?(:fetch_resource_for_passwordless)
|
193
200
|
authenticatable_class.fetch_resource_for_passwordless(normalized_email_param)
|
data/lib/passwordless/config.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "passwordless/controller_helpers"
|
4
|
+
|
5
|
+
module Passwordless
|
6
|
+
# A class the constraint routes to authenticated records
|
7
|
+
class Constraint
|
8
|
+
include ControllerHelpers
|
9
|
+
|
10
|
+
attr_reader :authenticatable_type, :predicate, :session
|
11
|
+
|
12
|
+
# @param [Class] authenticatable_type Authenticatable class
|
13
|
+
# @option options [Proc] :if A lambda that takes an authenticatable and returns a boolean
|
14
|
+
def initialize(authenticatable_type, **options)
|
15
|
+
@authenticatable_type = authenticatable_type
|
16
|
+
# `if' is a keyword but so we do this instead of keyword arguments
|
17
|
+
@predicate = options.fetch(:if) { -> (_) { true } }
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(request)
|
21
|
+
# used in authenticate_by_session
|
22
|
+
@session = request.session
|
23
|
+
authenticatable = authenticate_by_session(authenticatable_type)
|
24
|
+
!!(authenticatable && predicate.call(authenticatable))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# A class the constraint routes to NOT authenticated records
|
29
|
+
class ConstraintNot < Constraint
|
30
|
+
# @param [Class] authenticatable_type Authenticatable class
|
31
|
+
# @option options [Proc] :if A lambda that takes an authenticatable and returns a boolean
|
32
|
+
def initialize(authenticatable_type, **options)
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def matches?(request)
|
37
|
+
!super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -54,7 +54,7 @@ module Passwordless
|
|
54
54
|
end
|
55
55
|
|
56
56
|
# Signs in session
|
57
|
-
# @param
|
57
|
+
# @param passwordless_session [Passwordless::Session] Instance of {Passwordless::Session}
|
58
58
|
# to sign in
|
59
59
|
# @return [ActiveRecord::Base] the record that is passed in.
|
60
60
|
def sign_in(passwordless_session)
|
data/lib/passwordless/engine.rb
CHANGED
@@ -11,9 +11,5 @@ module Passwordless
|
|
11
11
|
ActionDispatch::Routing::Mapper.include RouterHelpers
|
12
12
|
ActiveRecord::Base.extend ModelHelpers
|
13
13
|
end
|
14
|
-
|
15
|
-
config.before_initialize do |app|
|
16
|
-
app.config.i18n.load_path += Dir[Engine.root.join("config", "locales", "*.yml")]
|
17
|
-
end
|
18
14
|
end
|
19
15
|
end
|
data/lib/passwordless/version.rb
CHANGED
data/lib/passwordless.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: 1.
|
4
|
+
version: 1.8.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: 2024-
|
11
|
+
date: 2024-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- lib/generators/passwordless/views_generator.rb
|
63
63
|
- lib/passwordless.rb
|
64
64
|
- lib/passwordless/config.rb
|
65
|
+
- lib/passwordless/constraint.rb
|
65
66
|
- lib/passwordless/context.rb
|
66
67
|
- lib/passwordless/controller_helpers.rb
|
67
68
|
- lib/passwordless/engine.rb
|
@@ -91,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
|
-
rubygems_version: 3.5.
|
95
|
+
rubygems_version: 3.5.22
|
95
96
|
signing_key:
|
96
97
|
specification_version: 4
|
97
98
|
summary: Add authentication to your app without all the ickyness of passwords.
|