passwordless 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7975d0dabfd39211005c2a34f3f3d967755b0a70
4
- data.tar.gz: 621a7327a0375aeeb6e4dafb990bce78d4bf32dc
3
+ metadata.gz: d3bdc9fe84110cc587576344e493cbe16a0a9a74
4
+ data.tar.gz: 1d7558d9be99a4764c21eb6fda029a765a9540bb
5
5
  SHA512:
6
- metadata.gz: 55ca6da5e10b2a0d8e92d0dad1cc048214efd7e32a9d6fecc40a2ebce102744772c96b6b128fae53d1b14b3709bb49f1225c82c6d5a08507eef8db29fc1ce0f9
7
- data.tar.gz: 925dbd8b6d98ac9a68d5c12bdc940bc38a1dc8c4e82b47c941d2de768b0cdd43edf2681a900e213eac366fd1e17dab9031650869cc2ce6801bd24881989e6320
6
+ metadata.gz: 6ec72945db7eec026bd640a3116c82fc9b5eea790cea4405bf9593815ed2e02edc3d91e58c82bdc52122a6190e512eb8013e51ca198d0b47cd961491d5f471aa
7
+ data.tar.gz: 0ca8c6e2e4d6bed6fab0351200acef5ac9e65a1526c2cd7503d751d967da8fd96d7200d005310600af693af00fde068aee7541b185e8969612342f32155dca4a
data/README.md CHANGED
@@ -1,8 +1,129 @@
1
- # Passwordless
1
+ <p align='center'>
2
+ <img src='https://s3.brnbw.com/Passwordless-title-JO71NQv7Q0.svg' alt='Passwordless' />
3
+ <br />
4
+ <br />
5
+ </p>
6
+
7
+ <img src='https://travis-ci.org/mikker/passwordless.svg?branch=master' alt='Build status' /> <img src='https://img.shields.io/gem/v/passwordless.svg' alt='Gem version' />
2
8
 
3
9
  Add authentication to your Rails app without all the icky-ness of passwords.
4
10
 
5
- _WIP_
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ Add the `passwordless` gem to your `Gemfile`:
16
+
17
+ ```ruby
18
+ gem 'passwordless'
19
+ ```
20
+
21
+ Install it and copy over the migrations:
22
+
23
+ ```sh
24
+ $ bundle
25
+ $ bin/rails passwordless:install:migrations
26
+ ```
27
+
28
+ Passwordless creates a single model called `Passwordless::Session`. It doesn't come with its own `User` model, it expects you to create one, eg.:
29
+
30
+ ```
31
+ $ bin/rails generate model User email
32
+ ```
33
+
34
+ Then specify which field on your `User` record is the email field with:
35
+
36
+ ```ruby
37
+ class User < ApplicationRecord
38
+ passwordless_with :email
39
+ end
40
+ ```
41
+
42
+ Finally, mount the engine in your routes:
43
+
44
+ ```ruby
45
+ Rails.application.routes.draw do
46
+ passwordless_for :users
47
+
48
+ # other routes
49
+ end
50
+ ```
51
+
52
+ ## Getting the current user, restricting access, the usual
53
+
54
+ Passwordless doesn't give you `current_user` automatically -- it's dead easy to add it though:
55
+
56
+ ```ruby
57
+ class ApplicationController < ActionController::Base
58
+ include Passwordless::ControllerHelpers # <-- This!
59
+
60
+ # ...
61
+
62
+ helper_method :current_user
63
+
64
+ private
65
+
66
+ def current_user
67
+ @current_user ||= authenticate_by_cookie(User)
68
+ end
69
+
70
+ def require_user!
71
+ return if current_user
72
+ redirect_to root_path, flash: {error: 'You are not worthy!'}
73
+ end
74
+ end
75
+ ```
76
+
77
+ ## Providing your own templates
78
+
79
+ Override `passwordless`' bundled views by adding your own. `passwordless` has 2 action views and 1 mailer view:
80
+
81
+ ```sh
82
+ # the form where the user inputs their email address
83
+ app/views/passwordless/sessions/new.html.erb
84
+ # shown after a user requests a magic link
85
+ app/views/passwordless/sessions/create.html.erb
86
+ # the mail with the magic link that gets sent
87
+ app/views/passwordless/mailer/magic_link.text.erb
88
+ ```
89
+
90
+ See [the bundled views](https://github.com/mikker/passwordless/tree/master/app/views/passwordless).
91
+
92
+ ## Registering new users
93
+
94
+ Because your `User` record is just any other record, you just create one like you normally would. Passwordless provides a helper method you can use to sign in the created user after it is saved like so:
95
+
96
+ ```ruby
97
+ class UsersController < ApplicationController
98
+ include Passwordless::ControllerHelpers # <-- This!
99
+ # (unless you already have it in your ApplicationController)
100
+
101
+ def create
102
+ @user = User.new user_params
103
+
104
+ if @user.save
105
+ sign_in @user # <-- And this!
106
+ redirect_to @user, flash: {notice: 'Welcome!'}
107
+ else
108
+ render :new
109
+ end
110
+ end
111
+
112
+ # ...
113
+ end
114
+ ```
115
+
116
+ Et voilá:
117
+
118
+ ```ruby
119
+ class VerySecretThingsController < ApplicationController
120
+ before_filter :require_user!
121
+
122
+ def index
123
+ @things = current_user.very_secret_things
124
+ end
125
+ end
126
+ ```
6
127
 
7
128
  # License
8
129
 
data/Rakefile CHANGED
@@ -17,11 +17,8 @@ end
17
17
  APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
-
21
20
  load 'rails/tasks/statistics.rake'
22
21
 
23
-
24
-
25
22
  require 'bundler/gem_tasks'
26
23
 
27
24
  require 'rake/testtask'
@@ -32,5 +29,4 @@ Rake::TestTask.new(:test) do |t|
32
29
  t.verbose = false
33
30
  end
34
31
 
35
-
36
32
  task default: :test
@@ -2,6 +2,8 @@ module Passwordless
2
2
  class SessionsController < ApplicationController
3
3
  include ControllerHelpers
4
4
 
5
+ helper_method :authenticatable_resource
6
+
5
7
  def new
6
8
  @email_field = authenticatable_class.passwordless_email_field
7
9
 
@@ -29,25 +31,36 @@ module Passwordless
29
31
 
30
32
  def show
31
33
  session = Session.valid.find_by!(
32
- authenticatable_type: params[:authenticatable],
34
+ authenticatable_type: authenticatable_classname,
33
35
  token: params[:token]
34
36
  )
35
37
 
36
- sign_in! session.authenticatable
38
+ sign_in session.authenticatable
37
39
 
38
40
  redirect_to main_app.root_path
39
41
  end
40
42
 
41
43
  def destroy
42
- sign_out! authenticatable_class
43
-
44
+ sign_out authenticatable_class
44
45
  redirect_to main_app.root_path
45
46
  end
46
47
 
47
48
  private
48
49
 
50
+ def authenticatable
51
+ params.fetch(:authenticatable)
52
+ end
53
+
54
+ def authenticatable_classname
55
+ authenticatable.to_s.camelize
56
+ end
57
+
49
58
  def authenticatable_class
50
- params[:authenticatable].constantize
59
+ authenticatable_classname.constantize
60
+ end
61
+
62
+ def authenticatable_resource
63
+ authenticatable.pluralize
51
64
  end
52
65
  end
53
66
  end
@@ -1,30 +1,29 @@
1
1
  module Passwordless
2
2
  module ControllerHelpers
3
-
4
- def authenticate!(authenticatable_class)
5
- key = :"#{authenticatable_class.to_s.underscore}_id"
3
+ def authenticate_by_cookie(authenticatable_class)
4
+ key = cookie_name(authenticatable_class)
6
5
  authenticatable_id = cookies.encrypted[key]
7
6
  return unless authenticatable_id
8
7
 
9
8
  authenticatable_class.find_by(id: authenticatable_id)
10
9
  end
11
10
 
12
- def sign_in!(authenticatable)
13
- key = :"#{authenticatable.class.to_s.underscore}_id"
11
+ def sign_in(authenticatable)
12
+ key = cookie_name(authenticatable.class)
14
13
  cookies.encrypted.permanent[key] = { value: authenticatable.id }
15
14
  authenticatable
16
15
  end
17
16
 
18
- def sign_out!(authenticatable_class)
19
- key = :"#{authenticatable_class.to_s.underscore}_id"
17
+ def sign_out(authenticatable_class)
18
+ key = cookie_name(authenticatable_class)
20
19
  cookies.encrypted.permanent[key] = { value: nil }
21
20
  cookies.delete(key)
22
21
  end
23
22
 
24
- # def self.included(kls)
25
- # kls.class_eval do
26
- # # helper_method :current_user
27
- # end
28
- # end
23
+ private
24
+
25
+ def cookie_name(authenticatable_class)
26
+ :"#{authenticatable_class.to_s.underscore}_id"
27
+ end
29
28
  end
30
29
  end
@@ -1,10 +1,15 @@
1
1
  module Passwordless
2
2
  class Mailer < ActionMailer::Base
3
- default from: 'from@example.com'
3
+ default from: Passwordless.default_from_address
4
4
 
5
5
  def magic_link(session)
6
6
  @session = session
7
7
 
8
+ authenticatable_resource_name =
9
+ @session.authenticatable_type.underscore.pluralize
10
+ @magic_link =
11
+ send(authenticatable_resource_name).token_sign_in_url(session.token)
12
+
8
13
  email_field = @session.authenticatable.class.passwordless_email_field
9
14
  mail to: @session.authenticatable.send(email_field), subject: "Your magic link ✨"
10
15
  end
@@ -1,2 +1,2 @@
1
1
  Here's your link:
2
- <%= passwordless.token_sign_in_url @session.token %>
2
+ <%= @magic_link %>
@@ -1,4 +1,4 @@
1
- <%= form_for @session, url: passwordless.sign_in_path do |f| %>
1
+ <%= form_for @session, url: send(authenticatable_resource).sign_in_path do |f| %>
2
2
  <% email_field_name = :"passwordless[#{@email_field}]" %>
3
3
  <%= text_field_tag email_field_name, params.fetch(email_field_name, nil) %>
4
4
  <%= f.submit 'Send magic link' %>
@@ -1,7 +1,6 @@
1
1
  Passwordless::Engine.routes.draw do
2
2
  get '/sign_in', to: 'sessions#new', as: :sign_in
3
3
  post '/sign_in', to: 'sessions#create'
4
- get '/sign_in_create', to: 'sessions#create_test'
5
4
  get '/sign_in/:token', to: 'sessions#show', as: :token_sign_in
6
5
  match '/sign_out', to: 'sessions#destroy', via: %i[get delete], as: :sign_out
7
6
  end
@@ -1,5 +1,5 @@
1
1
  require "passwordless/engine"
2
2
 
3
3
  module Passwordless
4
- # Your code goes here...
4
+ mattr_accessor(:default_from_address) { 'CHANGE_ME@example.com' }
5
5
  end
@@ -1,5 +1,12 @@
1
1
  module Passwordless
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Passwordless
4
+
5
+ config.to_prepare do
6
+ require 'passwordless/router_helpers'
7
+ ActionDispatch::Routing::Mapper.include RouterHelpers
8
+ require 'passwordless/model_helpers'
9
+ ActiveRecord::Base.extend ModelHelpers
10
+ end
4
11
  end
5
12
  end
@@ -0,0 +1,7 @@
1
+ module Passwordless
2
+ module ModelHelpers
3
+ def passwordless_with(field)
4
+ define_singleton_method(:passwordless_email_field) { field }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Passwordless
2
+ module RouterHelpers
3
+ def passwordless_for(resource, at: nil, as: nil)
4
+ mount(
5
+ Passwordless::Engine,
6
+ at: at || resource.to_s,
7
+ as: as || resource.to_s,
8
+ defaults: { authenticatable: resource.to_s.singularize }
9
+ )
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Passwordless
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  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.1.0
4
+ version: 0.2.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: 2017-11-05 00:00:00.000000000 Z
11
+ date: 2017-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description:
41
+ description: Description of Passwordless.
42
42
  email:
43
43
  - mikkel@brnbw.com
44
44
  executables: []
@@ -61,6 +61,8 @@ files:
61
61
  - db/migrate/20171104221735_create_passwordless_sessions.rb
62
62
  - lib/passwordless.rb
63
63
  - lib/passwordless/engine.rb
64
+ - lib/passwordless/model_helpers.rb
65
+ - lib/passwordless/router_helpers.rb
64
66
  - lib/passwordless/version.rb
65
67
  - lib/tasks/passwordless_tasks.rake
66
68
  homepage: https://github.com/mikker/passwordless
@@ -86,5 +88,5 @@ rubyforge_project:
86
88
  rubygems_version: 2.6.13
87
89
  signing_key:
88
90
  specification_version: 4
89
- summary: Add authentication to your Rails app without all the icky-ness of passwords.
91
+ summary: Summary of Passwordless.
90
92
  test_files: []