passwordless 0.12.0 → 1.0.0.beta1

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.
@@ -3,17 +3,13 @@
3
3
  module Passwordless
4
4
  # Engine that runs the passwordless gem.
5
5
  class Engine < ::Rails::Engine
6
- isolate_namespace Passwordless
7
-
8
6
  config.to_prepare do
9
7
  require "passwordless/router_helpers"
10
-
11
- ActionDispatch::Routing::Mapper.include RouterHelpers
12
8
  require "passwordless/model_helpers"
13
-
14
- ActiveRecord::Base.extend ModelHelpers
15
9
  require "passwordless/controller_helpers"
16
10
 
11
+ ActionDispatch::Routing::Mapper.include RouterHelpers
12
+ ActiveRecord::Base.extend ModelHelpers
17
13
  end
18
14
 
19
15
  config.before_initialize do |app|
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Passwordless
4
4
  module Errors
5
+ # Raised when the authenticatable class is not found.
6
+ class MissingEmailFieldError < StandardError
7
+ end
8
+
5
9
  # Raise this exception when a session is expired.
6
10
  class SessionTimedOutError < StandardError
7
11
  end
@@ -8,6 +8,8 @@ module Passwordless
8
8
  # passwordless_for :users
9
9
  # # or with options ...
10
10
  # passwordless_for :users, at: 'session_stuff', as: :user_session_things
11
+ # # or with a custom controller ...
12
+ # passwordless_for :users, controller: 'my_custom_controller'
11
13
  # @param resource [Symbol] the pluralized symbol of a Model (e.g - :users).
12
14
  # @param at [String] Optional - provide custom path for the passwordless
13
15
  # engine to get mounted at (using the above example your URLs end
@@ -16,17 +18,29 @@ module Passwordless
16
18
  # helpers (using the above example in a view:
17
19
  # <%= link_to 'Sign in', user_session_things.sign_in_path %>).
18
20
  # (Default: resource.to_s)
19
- def passwordless_for(resource, at: nil, as: nil)
20
- mount_at = at || resource.to_s
21
- mount_as = as || resource.to_s
22
- mount(
23
- Passwordless::Engine,
24
- at: mount_at,
25
- as: mount_as,
26
- defaults: {authenticatable: resource.to_s.singularize}
27
- )
21
+ # @param controller [String] Optional - provide a custom controller for
22
+ # sessions to use (using the above example the controller called would be MyCustomController
23
+ # (Default: 'passwordless/sessions')
24
+ def passwordless_for(resource, at: :na, as: :na, controller: "passwordless/sessions")
25
+ at == :na && at = "/#{resource.to_s}"
26
+ as == :na && as = "#{resource.to_s}_"
28
27
 
29
- Passwordless.mounted_as = mount_as
28
+ plural = resource.to_s
29
+ singular = plural.singularize
30
+
31
+ defaults = {
32
+ authenticatable: singular,
33
+ resource: resource,
34
+ }
35
+
36
+ scope(defaults: defaults) do
37
+ get("#{at}/sign_in", to: "#{controller}#new", as: :"#{as}sign_in")
38
+ post("#{at}/sign_in", to: "#{controller}#create")
39
+ get("#{at}/sign_in/:id", to: "#{controller}#show", as: :"verify_#{as}sign_in")
40
+ get("#{at}/sign_in/:id/:token", to: "#{controller}#confirm", as: :"confirm_#{as}sign_in")
41
+ patch("#{at}/sign_in/:id", to: "#{controller}#update")
42
+ match("#{at}/sign_out", to: "#{controller}#destroy", via: %i[get delete], as: :"#{as}sign_out")
43
+ end
30
44
  end
31
45
  end
32
46
  end
@@ -0,0 +1,9 @@
1
+ module Passwordless
2
+ class ShortTokenGenerator
3
+ CHARS = [*"A".."Z", *"0".."9"].freeze
4
+
5
+ def call(_session)
6
+ CHARS.sample(6).join
7
+ end
8
+ end
9
+ end
@@ -2,25 +2,35 @@ module Passwordless
2
2
  module TestHelpers
3
3
  module TestCase
4
4
  def passwordless_sign_out
5
- delete Passwordless::Engine.routes.url_helpers.sign_out_path
5
+ delete(Passwordless::Engine.routes.url_helpers.sign_out_path)
6
6
  follow_redirect!
7
7
  end
8
8
 
9
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)
10
+ session = Passwordless::Session.create!(authenticatable: resource)
11
+ magic_link = Passwordless::Engine.routes.url_helpers.send(
12
+ :"confirm_#{session.authenticatable_type.tableize}_sign_in_url",
13
+ session,
14
+ session.token
15
+ )
16
+ get(magic_link)
12
17
  follow_redirect!
13
18
  end
14
19
  end
15
20
 
16
21
  module SystemTestCase
17
22
  def passwordless_sign_out
18
- visit Passwordless::Engine.routes.url_helpers.sign_out_path
23
+ visit(Passwordless::Engine.routes.url_helpers.sign_out_path)
19
24
  end
20
25
 
21
26
  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)
27
+ session = Passwordless::Session.create!(authenticatable: resource)
28
+ magic_link = Passwordless::Engine.routes.url_helpers.send(
29
+ :"confirm_#{session.authenticatable_type.tableize}_sign_in_url",
30
+ session,
31
+ session.token
32
+ )
33
+ visit(magic_link)
24
34
  end
25
35
  end
26
36
  end
@@ -36,8 +46,8 @@ end
36
46
 
37
47
  if defined?(RSpec)
38
48
  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
49
+ config.include(::Passwordless::TestHelpers::TestCase, type: :request)
50
+ config.include(::Passwordless::TestHelpers::TestCase, type: :controller)
51
+ config.include(::Passwordless::TestHelpers::SystemTestCase, type: :system)
42
52
  end
43
53
  end
@@ -0,0 +1,18 @@
1
+ module Passwordless
2
+ class TokenDigest
3
+ ALGORITHM = "SHA256"
4
+
5
+ def initialize(str)
6
+ @str = str
7
+ end
8
+
9
+ def digest
10
+ key = self.class.key()
11
+ OpenSSL::HMAC.hexdigest(ALGORITHM, key, @str)
12
+ end
13
+
14
+ def self.key
15
+ @key ||= ActiveSupport::KeyGenerator.new(Rails.application.secret_key_base).generate_key("passwordless")
16
+ end
17
+ end
18
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Passwordless
4
4
  # :nodoc:
5
- VERSION = "0.12.0"
5
+ VERSION = "1.0.0.beta1"
6
6
  end
data/lib/passwordless.rb CHANGED
@@ -1,30 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
+ require "passwordless/config"
4
5
  require "passwordless/errors"
5
6
  require "passwordless/engine"
6
- require "passwordless/url_safe_base_64_generator"
7
+ require "passwordless/token_digest"
7
8
 
8
9
  # The main Passwordless module
9
10
  module Passwordless
10
- mattr_accessor(:parent_mailer) { "ActionMailer::Base" }
11
- mattr_accessor(:default_from_address) { "CHANGE_ME@example.com" }
12
- mattr_accessor(:token_generator) { UrlSafeBase64Generator.new }
13
- mattr_accessor(:restrict_token_reuse) { false }
14
- mattr_accessor(:redirect_back_after_sign_in) { true }
15
- mattr_accessor(:mounted_as) { :configured_when_mounting_passwordless }
11
+ extend Configurable
16
12
 
17
- mattr_accessor(:expires_at) { lambda { 1.year.from_now } }
18
- mattr_accessor(:timeout_at) { lambda { 1.hour.from_now } }
19
- mattr_accessor(:redirect_to_response_options) { {} }
20
- mattr_accessor(:success_redirect_path) { "/" }
21
- mattr_accessor(:failure_redirect_path) { "/" }
22
- mattr_accessor(:sign_out_redirect_path) { "/" }
23
-
24
- mattr_accessor(:after_session_save) do
25
- lambda { |session, _request| Mailer.magic_link(session).deliver_now }
13
+ def self.digest(token)
14
+ TokenDigest.new(token).digest
26
15
  end
27
-
28
- CookieDeprecation = ActiveSupport::Deprecation.new("0.9", "passwordless")
29
- SessionValidDeprecation = ActiveSupport::Deprecation.new("0.9", "passwordless")
30
16
  end
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.12.0
4
+ version: 1.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikkel Malmberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-16 00:00:00.000000000 Z
11
+ date: 2023-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -28,58 +28,16 @@ dependencies:
28
28
  name: bcrypt
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 3.1.11
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 3.1.11
41
- - !ruby/object:Gem::Dependency
42
- name: sqlite3
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 1.4.1
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: 1.4.1
55
- - !ruby/object:Gem::Dependency
56
- name: yard
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: standard
71
- requirement: !ruby/object:Gem::Requirement
72
37
  requirements:
73
38
  - - ">="
74
39
  - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
40
+ version: 3.1.11
83
41
  description:
84
42
  email:
85
43
  - mikkel@brnbw.com
@@ -95,20 +53,23 @@ files:
95
53
  - app/mailers/passwordless/mailer.rb
96
54
  - app/models/passwordless/application_record.rb
97
55
  - app/models/passwordless/session.rb
98
- - app/views/passwordless/mailer/magic_link.text.erb
56
+ - app/views/passwordless/mailer/sign_in.text.erb
99
57
  - app/views/passwordless/sessions/new.html.erb
58
+ - app/views/passwordless/sessions/show.html.erb
100
59
  - config/locales/en.yml
101
60
  - config/routes.rb
102
61
  - db/migrate/20171104221735_create_passwordless_sessions.rb
103
62
  - lib/generators/passwordless/views_generator.rb
104
63
  - lib/passwordless.rb
64
+ - lib/passwordless/config.rb
105
65
  - lib/passwordless/controller_helpers.rb
106
66
  - lib/passwordless/engine.rb
107
67
  - lib/passwordless/errors.rb
108
68
  - lib/passwordless/model_helpers.rb
109
69
  - lib/passwordless/router_helpers.rb
70
+ - lib/passwordless/short_token_generator.rb
110
71
  - lib/passwordless/test_helpers.rb
111
- - lib/passwordless/url_safe_base_64_generator.rb
72
+ - lib/passwordless/token_digest.rb
112
73
  - lib/passwordless/version.rb
113
74
  homepage: https://github.com/mikker/passwordless
114
75
  licenses:
@@ -125,11 +86,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
86
  version: '0'
126
87
  required_rubygems_version: !ruby/object:Gem::Requirement
127
88
  requirements:
128
- - - ">="
89
+ - - ">"
129
90
  - !ruby/object:Gem::Version
130
- version: '0'
91
+ version: 1.3.1
131
92
  requirements: []
132
- rubygems_version: 3.4.14
93
+ rubygems_version: 3.4.19
133
94
  signing_key:
134
95
  specification_version: 4
135
96
  summary: Add authentication to your app without all the ickyness of passwords.
@@ -1 +0,0 @@
1
- <%= I18n.t('passwordless.mailer.magic_link', link: @magic_link) %>
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Passwordless
4
- # Generates random numbers for Session records
5
- class UrlSafeBase64Generator
6
- # Passwordless' default random string strategy. Generates a url safe
7
- # base64 random string.
8
- # @param _session [Session] Optional - Passwordless passes the sesion Record
9
- # to generators so you can (optionally) use it for generating your tokens.
10
- # @return [String] 32 byte base64 string
11
- def call(_session)
12
- SecureRandom.urlsafe_base64(32)
13
- end
14
- end
15
- end