passwordless 1.5.0 → 1.7.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 +48 -2
- data/app/controllers/passwordless/sessions_controller.rb +7 -6
- data/app/models/passwordless/session.rb +2 -1
- data/app/views/passwordless/sessions/show.html.erb +1 -1
- data/config/locales/en.yml +1 -0
- data/db/migrate/20171104221735_create_passwordless_sessions.rb +1 -1
- 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: 7e765c341b12f05bb0156802dae574f7a0db31288181d4f71779a6ca49e48cd6
|
4
|
+
data.tar.gz: 746f09e1aefaea54d0f926c9bff27548ba1f8a4fdeeea0cfbd87ddc4b22ad65f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8792ad81a4efbd5a071bb8a119d3e1ad057f2112e2bebbb888bbfdc9a84b95a77e4e2cb68ffb957fecb98f75cf05efb6c17cbd5c97b67a41f1372257eeb1cae9
|
7
|
+
data.tar.gz: 763dbbba33a8568976b5a5df2c28f0d157dcf9fc92504bf9ca016e85f8239c59b1e6ae48f4fa4e2e5ab2391aba9239ce956f7706c5437e168c27756a80fc9f33
|
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,35 @@ 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
|
+
### Route constraints
|
170
|
+
|
171
|
+
With [constraints](https://guides.rubyonrails.org/routing.html#request-based-constraints) you can restrict access to certain routes.
|
172
|
+
Passwordless provides `Passwordless::Constraint` and it's negative counterpart `Passwordless::NotConstraint` for this purpose.
|
173
|
+
|
174
|
+
To limit a route to only authenticated `User`s:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
constraints Passwordless::Constraint.new(User) do
|
178
|
+
# ...
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
The constraint takes a second `if:` argument, that expects a block and is passed the `authenticatable` record, (ie. `User`):
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
constraints Passwordless::Constraint.new(User, if: -> (user) { user.email.include?("john") }) do
|
186
|
+
# ...
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
The negated version has the same API but with the opposite result, ie. ensuring authenticated user **don't** have access:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
constraints Passwordless::NotConstraint.new(User) do
|
194
|
+
get("/no-users-allowed", to: "secrets#index")
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
152
198
|
## Configuration
|
153
199
|
|
154
200
|
To customize Passwordless, create a file `config/initializers/passwordless.rb`.
|
@@ -160,7 +206,7 @@ Passwordless.configure do |config|
|
|
160
206
|
config.default_from_address = "CHANGE_ME@example.com"
|
161
207
|
config.parent_controller = "ApplicationController"
|
162
208
|
config.parent_mailer = "ActionMailer::Base"
|
163
|
-
config.restrict_token_reuse =
|
209
|
+
config.restrict_token_reuse = true # Can a token/link be used multiple times?
|
164
210
|
config.token_generator = Passwordless::ShortTokenGenerator.new # Used to generate magic link tokens.
|
165
211
|
|
166
212
|
config.expires_at = lambda { 1.year.from_now } # How long until a signed in session expires.
|
@@ -30,19 +30,20 @@ module Passwordless
|
|
30
30
|
Passwordless.context.path_for(
|
31
31
|
@session,
|
32
32
|
id: @session.to_param,
|
33
|
-
action: "show"
|
33
|
+
action: "show",
|
34
|
+
**default_url_options
|
34
35
|
),
|
35
36
|
flash: {notice: I18n.t("passwordless.sessions.create.email_sent")}
|
36
37
|
)
|
37
38
|
else
|
38
|
-
flash
|
39
|
+
flash.alert = I18n.t("passwordless.sessions.create.error")
|
39
40
|
render(:new, status: :unprocessable_entity)
|
40
41
|
end
|
41
42
|
|
42
43
|
rescue ActiveRecord::RecordNotFound
|
43
44
|
@session = Session.new
|
44
45
|
|
45
|
-
flash
|
46
|
+
flash.alert = I18n.t("passwordless.sessions.create.not_found")
|
46
47
|
render(:new, status: :not_found)
|
47
48
|
end
|
48
49
|
|
@@ -155,15 +156,15 @@ module Passwordless
|
|
155
156
|
**redirect_to_options
|
156
157
|
)
|
157
158
|
else
|
158
|
-
flash
|
159
|
+
flash.alert = I18n.t("passwordless.sessions.errors.invalid_token")
|
159
160
|
render(status: :forbidden, action: "show")
|
160
161
|
end
|
161
162
|
|
162
163
|
rescue Errors::TokenAlreadyClaimedError
|
163
|
-
flash
|
164
|
+
flash.alert = I18n.t("passwordless.sessions.errors.token_claimed")
|
164
165
|
redirect_to(passwordless_failure_redirect_path, status: :see_other, **redirect_to_options)
|
165
166
|
rescue Errors::SessionTimedOutError
|
166
|
-
flash
|
167
|
+
flash.alert = I18n.t("passwordless.sessions.errors.session_expired")
|
167
168
|
redirect_to(passwordless_failure_redirect_path, status: :see_other, **redirect_to_options)
|
168
169
|
end
|
169
170
|
|
data/config/locales/en.yml
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.7.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-05-28 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.10
|
95
96
|
signing_key:
|
96
97
|
specification_version: 4
|
97
98
|
summary: Add authentication to your app without all the ickyness of passwords.
|