passwordless 0.1.0 → 0.2.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 +123 -2
- data/Rakefile +0 -4
- data/app/controllers/passwordless/sessions_controller.rb +18 -5
- data/app/lib/passwordless/controller_helpers.rb +11 -12
- data/app/mailers/passwordless/mailer.rb +6 -1
- data/app/views/passwordless/mailer/magic_link.text.erb +1 -1
- data/app/views/passwordless/sessions/new.html.erb +1 -1
- data/config/routes.rb +0 -1
- data/lib/passwordless.rb +1 -1
- data/lib/passwordless/engine.rb +7 -0
- data/lib/passwordless/model_helpers.rb +7 -0
- data/lib/passwordless/router_helpers.rb +12 -0
- data/lib/passwordless/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3bdc9fe84110cc587576344e493cbe16a0a9a74
|
4
|
+
data.tar.gz: 1d7558d9be99a4764c21eb6fda029a765a9540bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ec72945db7eec026bd640a3116c82fc9b5eea790cea4405bf9593815ed2e02edc3d91e58c82bdc52122a6190e512eb8013e51ca198d0b47cd961491d5f471aa
|
7
|
+
data.tar.gz: 0ca8c6e2e4d6bed6fab0351200acef5ac9e65a1526c2cd7503d751d967da8fd96d7200d005310600af693af00fde068aee7541b185e8969612342f32155dca4a
|
data/README.md
CHANGED
@@ -1,8 +1,129 @@
|
|
1
|
-
|
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
|
-
|
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:
|
34
|
+
authenticatable_type: authenticatable_classname,
|
33
35
|
token: params[:token]
|
34
36
|
)
|
35
37
|
|
36
|
-
sign_in
|
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
|
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
|
-
|
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
|
-
|
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
|
13
|
-
key =
|
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
|
19
|
-
key =
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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:
|
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
|
-
<%=
|
2
|
+
<%= @magic_link %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%= form_for @session, url:
|
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' %>
|
data/config/routes.rb
CHANGED
@@ -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
|
data/lib/passwordless.rb
CHANGED
data/lib/passwordless/engine.rb
CHANGED
@@ -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,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
|
data/lib/passwordless/version.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: 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-
|
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:
|
91
|
+
summary: Summary of Passwordless.
|
90
92
|
test_files: []
|