passwordless 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00fe76a0b0247e700ff5868fcabf1dc82b1bffddd63437f21d5ec5c4dce83479
4
- data.tar.gz: ff56c5859f448221da83458c17dd30623bd335942fe77e44bede19144a7dd49e
3
+ metadata.gz: eb17c0cc08c9dd0062b47d67a470adeee2d0e7dc618e48967180eea61796f47a
4
+ data.tar.gz: 870dd7a03e131344f20bdee24ab614e1ef66ff3fe8faf9d4bd05bb08adce032d
5
5
  SHA512:
6
- metadata.gz: 2e3281f7a60b04f82796a00759a6aea284aeeafb237670c10f458a629ea02b0d55607f155b4efa257209f580aeb394b0bf33c69c5bce250ec3e179e01eecaf89
7
- data.tar.gz: 81ddaae8d5120e6e1867a8992bc9c0b0f596933ba163dac63a1b3ad39b2b4ba40d84c6b69bb1764283ea9eac39eba199f1fbced0a91f1f93f16f10f01e56bf38
6
+ metadata.gz: e43144bae67ad300ae753fe131ec6b09fcf997c975e2fcb9a819d457fb0178797d03b4e7278c3c92c4cc18664c54c5231de428be7f6fc63c05a878516e628f7a
7
+ data.tar.gz: 5aeca84a612458d6e2dce0b0e9335d80a9f4e4af730540a148c3ef0ed6056417a33b444bfc17c19adf06744a7a2fa38dccb82189206bf2c86354352540b41e7e
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <br />
5
5
  </p>
6
6
 
7
- [![Travis](https://travis-ci.org/mikker/passwordless.svg?branch=master)](https://travis-ci.org/mikker/passwordless) [![Rubygems](https://img.shields.io/gem/v/passwordless.svg)](https://rubygems.org/gems/passwordless) [![codecov](https://codecov.io/gh/mikker/passwordless/branch/master/graph/badge.svg)](https://codecov.io/gh/mikker/passwordless)
7
+ [![Travis](https://travis-ci.org/mikker/passwordless.svg?branch=master)](https://travis-ci.org/mikker/passwordless) [![Rubygems](https://img.shields.io/gem/v/passwordless.svg)](https://rubygems.org/gems/passwordless) [![codecov](https://codecov.io/gh/mikker/passwordless/branch/master/graph/badge.svg)](https://codecov.io/gh/mikker/passwordless) [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
8
8
 
9
9
  Add authentication to your Rails app without all the icky-ness of passwords.
10
10
 
@@ -22,6 +22,7 @@ Add authentication to your Rails app without all the icky-ness of passwords.
22
22
  * [Token and Session Expiry](#token-and-session-expiry)
23
23
  * [Redirecting back after sign-in](#redirecting-back-after-sign-in)
24
24
  * [URLs and links](#urls-and-links)
25
+ * [Customize the way to send magic link](#customize-the-way-to-send-magic-link)
25
26
  * [E-mail security](#e-mail-security)
26
27
  * [License](#license)
27
28
 
@@ -232,6 +233,26 @@ passwordless_for :users, at: '/', as: :auth
232
233
 
233
234
  Also be sure to [specify ActionMailer's `default_url_options.host`](http://guides.rubyonrails.org/action_mailer_basics.html#generating-urls-in-action-mailer-views).
234
235
 
236
+
237
+ ### Customize the way to send magic link
238
+
239
+ By default, magic link will send by email. You can customize this method. For example, you can send magic link via SMS.
240
+
241
+ config/initializers/passwordless.rb
242
+
243
+ ```
244
+ Passwordless.after_session_save = lambda do |session|
245
+ # Default behavior is
246
+ # Mailer.magic_link(session).deliver_now
247
+
248
+ # You can change behavior to do something with session model. For example,
249
+ # session.authenticatable.send_sms
250
+ end
251
+ ```
252
+
253
+ You can access user model through authenticatable.
254
+
255
+
235
256
  ### E-mail security
236
257
 
237
258
  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.
data/Rakefile CHANGED
@@ -1,27 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  begin
4
- require 'bundler/setup'
4
+ require "bundler/setup"
5
5
  rescue LoadError
6
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
7
  end
8
8
 
9
- require 'yard'
9
+ require "yard"
10
10
  YARD::Rake::YardocTask.new
11
11
  task docs: :yard
12
12
 
13
- APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
14
- load 'rails/tasks/engine.rake'
15
- load 'rails/tasks/statistics.rake'
13
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
14
+ load "rails/tasks/engine.rake"
15
+ load "rails/tasks/statistics.rake"
16
16
 
17
- require 'bundler/gem_tasks'
17
+ require "bundler/gem_tasks"
18
18
 
19
- require 'rake/testtask'
19
+ require "rake/testtask"
20
20
 
21
21
  Rake::TestTask.new(:test) do |t|
22
- t.libs << 'test'
23
- t.pattern = 'test/**/*_test.rb'
22
+ t.libs << "test"
23
+ t.pattern = "test/**/*_test.rb"
24
24
  t.verbose = false
25
25
  end
26
26
 
27
27
  task default: :test
28
+
29
+ require "standard/rake"
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bcrypt'
3
+ require "bcrypt"
4
4
 
5
5
  module Passwordless
6
6
  # Controller for managing Passwordless sessions
7
7
  class SessionsController < ApplicationController
8
8
  # Raise this exception when a session is expired.
9
- class ExpiredSessionError < StandardError; end
9
+ class SessionTimedOutError < StandardError; end
10
10
 
11
11
  include ControllerHelpers
12
12
 
@@ -26,14 +26,12 @@ module Passwordless
26
26
  session = build_passwordless_session(find_authenticatable)
27
27
 
28
28
  if session.save
29
- Mailer.magic_link(session).deliver_now
29
+ Passwordless.after_session_save.call(session)
30
30
  end
31
31
 
32
32
  render
33
33
  end
34
34
 
35
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
36
-
37
35
  # get '/sign_in/:token'
38
36
  # Looks up session record by provided token. Signs in user if a match
39
37
  # is found. Redirects to either the user's original destination
@@ -45,7 +43,7 @@ module Passwordless
45
43
  BCrypt::Password.create(params[:token])
46
44
 
47
45
  session = find_session
48
- raise ExpiredSessionError if session.expired?
46
+ raise SessionTimedOutError if session.timed_out?
49
47
 
50
48
  sign_in session.authenticatable
51
49
 
@@ -57,11 +55,10 @@ module Passwordless
57
55
  else
58
56
  redirect_to main_app.root_path
59
57
  end
60
- rescue ExpiredSessionError
61
- flash[:error] = I18n.t('.passwordless.sessions.create.session_expired')
58
+ rescue SessionTimedOutError
59
+ flash[:error] = I18n.t(".passwordless.sessions.create.session_expired")
62
60
  redirect_to main_app.root_path
63
61
  end
64
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
65
62
 
66
63
  # match '/sign_out', via: %i[get delete].
67
64
  # Signs user out. Redirects to root_path
@@ -102,7 +99,7 @@ module Passwordless
102
99
  end
103
100
 
104
101
  def find_session
105
- Session.valid.find_by!(
102
+ Session.find_by!(
106
103
  authenticatable_type: authenticatable_classname,
107
104
  token: params[:token]
108
105
  )
@@ -11,12 +11,12 @@ module Passwordless
11
11
  @session = session
12
12
 
13
13
  @magic_link = send(Passwordless.mounted_as)
14
- .token_sign_in_url(session.token)
14
+ .token_sign_in_url(session.token)
15
15
 
16
16
  email_field = @session.authenticatable.class.passwordless_email_field
17
17
  mail(
18
18
  to: @session.authenticatable.send(email_field),
19
- subject: I18n.t('passwordless.mailer.subject')
19
+ subject: I18n.t("passwordless.mailer.subject")
20
20
  )
21
21
  end
22
22
  end
@@ -19,22 +19,26 @@ module Passwordless
19
19
  before_validation :set_defaults
20
20
 
21
21
  scope :valid, lambda {
22
- where('timeout_at > ?', Time.current)
22
+ where("timeout_at > ?", Time.current)
23
23
  }
24
24
 
25
25
  def expired?
26
26
  expires_at <= Time.current
27
27
  end
28
28
 
29
+ def timed_out?
30
+ timeout_at <= Time.current
31
+ end
32
+
29
33
  private
30
34
 
31
35
  def set_defaults
32
36
  self.expires_at ||= Passwordless.expires_at.call
33
37
  self.timeout_at ||= Passwordless.timeout_at.call
34
- self.token ||= loop do
38
+ self.token ||= loop {
35
39
  token = Passwordless.token_generator.call(self)
36
40
  break token unless Session.find_by(token: token)
37
- end
41
+ }
38
42
  end
39
43
  end
40
44
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Passwordless::Engine.routes.draw do
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
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
@@ -6,7 +6,7 @@ class CreatePasswordlessSessions < ActiveRecord::Migration[5.1]
6
6
  t.belongs_to(
7
7
  :authenticatable,
8
8
  polymorphic: true,
9
- index: { name: 'authenticatable' }
9
+ index: {name: "authenticatable"}
10
10
  )
11
11
  t.datetime :timeout_at, null: false
12
12
  t.datetime :expires_at, null: false
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'passwordless/engine'
4
- require 'passwordless/url_safe_base_64_generator'
3
+ require "passwordless/engine"
4
+ require "passwordless/url_safe_base_64_generator"
5
5
 
6
6
  # The main Passwordless module
7
7
  module Passwordless
8
- mattr_accessor(:default_from_address) { 'CHANGE_ME@example.com' }
8
+ mattr_accessor(:default_from_address) { "CHANGE_ME@example.com" }
9
9
  mattr_accessor(:token_generator) { UrlSafeBase64Generator.new }
10
10
  mattr_accessor(:redirect_back_after_sign_in) { true }
11
11
  mattr_accessor(:mounted_as) { :configured_when_mounting_passwordless }
12
12
 
13
13
  mattr_accessor(:expires_at) { lambda { 1.year.from_now } }
14
14
  mattr_accessor(:timeout_at) { lambda { 1.hour.from_now } }
15
+
16
+ mattr_accessor(:after_session_save) { lambda { |session| Mailer.magic_link(session).deliver_now } }
15
17
  end
@@ -12,7 +12,7 @@ module Passwordless
12
12
  def build_passwordless_session(authenticatable)
13
13
  Session.new.tap do |us|
14
14
  us.remote_addr = request.remote_addr
15
- us.user_agent = request.env['HTTP_USER_AGENT']
15
+ us.user_agent = request.env["HTTP_USER_AGENT"]
16
16
  us.authenticatable = authenticatable
17
17
  end
18
18
  end
@@ -38,7 +38,7 @@ module Passwordless
38
38
  # @return [ActiveRecord::Base] the record that is passed in.
39
39
  def sign_in(authenticatable)
40
40
  key = cookie_name(authenticatable.class)
41
- cookies.encrypted.permanent[key] = { value: authenticatable.id }
41
+ cookies.encrypted.permanent[key] = {value: authenticatable.id}
42
42
  authenticatable
43
43
  end
44
44
 
@@ -47,7 +47,7 @@ module Passwordless
47
47
  # @return [boolean] Always true
48
48
  def sign_out(authenticatable_class)
49
49
  key = cookie_name(authenticatable_class)
50
- cookies.encrypted.permanent[key] = { value: nil }
50
+ cookies.encrypted.permanent[key] = {value: nil}
51
51
  cookies.delete(key)
52
52
  true
53
53
  end
@@ -6,16 +6,16 @@ module Passwordless
6
6
  isolate_namespace Passwordless
7
7
 
8
8
  config.to_prepare do
9
- require 'passwordless/router_helpers'
9
+ require "passwordless/router_helpers"
10
10
  ActionDispatch::Routing::Mapper.include RouterHelpers
11
- require 'passwordless/model_helpers'
11
+ require "passwordless/model_helpers"
12
12
  ActiveRecord::Base.extend ModelHelpers
13
- require 'passwordless/controller_helpers'
13
+ require "passwordless/controller_helpers"
14
14
  end
15
15
 
16
16
  config.before_initialize do |app|
17
17
  app.config.i18n.load_path +=
18
- Dir[Engine.root.join('config', 'locales', '*.yml')]
18
+ Dir[Engine.root.join("config", "locales", "*.yml")]
19
19
  end
20
20
  end
21
21
  end
@@ -9,7 +9,7 @@ module Passwordless
9
9
  # @param field [string] email submitted by user.
10
10
  def passwordless_with(field)
11
11
  has_many :passwordless_sessions,
12
- class_name: 'Passwordless::Session',
12
+ class_name: "Passwordless::Session",
13
13
  as: :authenticatable
14
14
 
15
15
  define_singleton_method(:passwordless_email_field) { field }
@@ -21,7 +21,7 @@ module Passwordless
21
21
  mount_as = as || resource.to_s
22
22
  mount(
23
23
  Passwordless::Engine, at: mount_at, as: mount_as,
24
- defaults: { authenticatable: resource.to_s.singularize }
24
+ defaults: {authenticatable: resource.to_s.singularize}
25
25
  )
26
26
 
27
27
  Passwordless.mounted_as = mount_as
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Passwordless
4
- VERSION = '0.6.0' # :nodoc:
4
+ VERSION = '0.7.0' # :nodoc:
5
5
  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.6.0
4
+ version: 0.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: 2019-01-29 00:00:00.000000000 Z
11
+ date: 2019-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: sqlite3
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 1.3.6
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 1.3.6
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: yard
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rubocop
70
+ name: standard
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  - !ruby/object:Gem::Version
128
128
  version: '0'
129
129
  requirements: []
130
- rubygems_version: 3.0.2
130
+ rubygems_version: 3.0.1
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: Add authentication to your app without all the ickyness of passwords.