passwordless 1.5.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|