userbin 0.2.4 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +90 -20
- data/lib/userbin/authentication.rb +49 -52
- data/lib/userbin/configuration.rb +8 -2
- data/lib/userbin/rails/auth_helpers.rb +9 -6
- data/lib/userbin/userbin.rb +27 -5
- data/lib/userbin/version.rb +1 -1
- data/lib/userbin.rb +2 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8e6296ed0a839326618e50de24bdc44370bd0b5
|
4
|
+
data.tar.gz: 0780de9c998812f4b60d7f6df082cfa8168c5633
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34e266fbe71560862f5d21095f27c73ec9ab50927a7ccb6c657490114cd6e417f397e3a3f1367f17ab6792d0a2867786241762d4b348e4de8c2c935ea9b7b743
|
7
|
+
data.tar.gz: bfaa4aa0595a9ed47f77e37306463d98974cf4391e33bfa361c63c2825f768155d006a0d382a4590ddc33d5c3fc4ebd2d4122e70e179373149692d34de40ef19
|
data/README.md
CHANGED
@@ -3,7 +3,9 @@ Userbin for Ruby
|
|
3
3
|
|
4
4
|
Userbin for Ruby adds user authentication, login flows and user management to your **Rails**, **Sinatra** or **Rack** app.
|
5
5
|
|
6
|
-
[Userbin](https://userbin.com) provides a set of login, signup, and password reset forms that drop right into your application without any need of styling or writing markup. Connect your users via traditional logins or third party social networks. We take care of linking accounts across networks, resetting passwords, and keeping everything safe and secure.
|
6
|
+
[Userbin](https://userbin.com) provides a set of login, signup, and password reset forms that drop right into your application without any need of styling or writing markup. Connect your users via traditional logins or third party social networks. We take care of linking accounts across networks, resetting passwords, and keeping everything safe and secure.
|
7
|
+
|
8
|
+
[Create a free account](https://userbin.com) at Userbin to start accepting users in your application.
|
7
9
|
|
8
10
|
Installation
|
9
11
|
------------
|
@@ -19,72 +21,140 @@ Installation
|
|
19
21
|
```shell
|
20
22
|
bundle install
|
21
23
|
```
|
22
|
-
|
24
|
+
|
23
25
|
2. Configure the Userbin module with the credentials you got from signing up.
|
24
26
|
|
25
|
-
In a Rails app, put the following code into a new file at `config/initializers/userbin.rb`
|
27
|
+
In a Rails app, put the following code into a new file at `config/initializers/userbin.rb`, and in Sinatra put it in your main application file and add `require "userbin"`.
|
26
28
|
|
27
29
|
```ruby
|
28
30
|
Userbin.configure do |config|
|
29
31
|
config.app_id = "YOUR_APP_ID"
|
30
32
|
config.api_secret = "YOUR_API_SECRET"
|
31
|
-
config.restricted_path = "/admin"
|
32
33
|
end
|
33
34
|
```
|
34
35
|
|
35
36
|
If you don't configure the `app_id` and `api_secret`, the Userbin module will read the `USERBIN_APP_ID` and `USERBIN_API_SECRET` environment variables. This may come in handy on Heroku.
|
36
|
-
|
37
|
-
Set up your `restricted_path` to which users will be redirected on a successful login. Browsing to this path or a sub-path will require the user to login. Logging out redirects the user back to the root path.
|
38
37
|
|
39
38
|
3. **Rack/Sinatra apps only**: Activate the Userbin Rack middleware
|
40
39
|
|
41
40
|
```ruby
|
42
41
|
use Userbin::Authentication
|
43
42
|
```
|
44
|
-
|
45
|
-
That's it! People are now able sign up and log in to your application.
|
43
|
+
|
46
44
|
|
47
45
|
Usage
|
48
46
|
-----
|
49
47
|
|
50
|
-
###
|
48
|
+
### Forms
|
51
49
|
|
52
|
-
|
50
|
+
An easy way to integrate Userbin is via the [Widget](https://userbin.com/docs/javascript#widget), which will take care of building forms, validating input and provides a drop-in design that adapts nicely to all devices.
|
53
51
|
|
54
|
-
|
52
|
+
The Widget is fairly high level, so remember that you can still use Userbin with your [own forms](https://userbin.com) if it doesn't fit your use-case.
|
53
|
+
|
54
|
+
The following links will open up the Widget with the login or the signup form respectively.
|
55
55
|
|
56
56
|
```html
|
57
|
-
<!-- put this on a public page -->
|
58
57
|
<a class="ub-login">Log in</a>
|
59
|
-
|
58
|
+
```
|
59
|
+
|
60
|
+
```html
|
60
61
|
<a class="ub-signup">Sign up</a>
|
61
62
|
```
|
62
63
|
|
63
64
|
The logout link will clear the session and redirect the user back to your root path:
|
64
65
|
|
65
66
|
```html
|
66
|
-
<!-- put this on a restricted page -->
|
67
67
|
<a class="ub-logout">Log out</a>
|
68
68
|
```
|
69
69
|
|
70
|
-
See the [Javascript reference](https://userbin.com/docs/javascript#markup) for more info on this markup.
|
71
|
-
|
72
70
|
### The current user
|
73
71
|
|
74
|
-
Userbin keeps track of the currently logged in user
|
72
|
+
Userbin keeps track of the currently logged in user which can be accessed through the `current_user` property. This automatically taps into libraries such as the authorization solution [CanCan](https://github.com/ryanb/cancan).
|
75
73
|
|
76
74
|
```erb
|
77
|
-
Welcome to your account, <%=
|
75
|
+
Welcome to your account, <%= current_user.email %>
|
78
76
|
```
|
79
77
|
|
80
|
-
To check if a user is logged in, use
|
78
|
+
To check if a user is logged in, use `user_logged_in?` (or its alias `user_signed_in?` if you prefer Devise conventions)
|
81
79
|
|
82
80
|
```erb
|
83
|
-
<% if
|
81
|
+
<% if user_logged_in? %>
|
84
82
|
You are logged in!
|
85
83
|
<% end %>
|
86
84
|
```
|
87
85
|
|
86
|
+
**Rack/Sinatra apps only**: Since above helpers aren't available outside Rails, instead use `Userbin.current_user` and `Userbin.user_logged_in?`.
|
87
|
+
|
88
|
+
Configuration
|
89
|
+
-------------
|
90
|
+
|
91
|
+
The `Userbin.configure` block supports a range of options additional to the Userbin credentials. None of the following options are mandatory.
|
92
|
+
|
93
|
+
### protected_path
|
94
|
+
|
95
|
+
By default, Userbin reloads the current page on a successful login. If you set the `protected_path` option, users will be redirected to this path instead.
|
96
|
+
|
97
|
+
Once set, this path and any sub-path of it will be protected from unauthenticated users by instead rendering a login form.
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
config.protected_path = '/dashboard'
|
101
|
+
```
|
102
|
+
|
103
|
+
### root_path
|
104
|
+
|
105
|
+
By default, Userbin reloads the current page on a successful logout. If you set the `root_path` option, users will be redirected to this path instead.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
config.root_path = '/login'
|
109
|
+
```
|
110
|
+
|
111
|
+
### create_user and find_user
|
112
|
+
|
113
|
+
By default, `current_user` will reference a *limited* Userbin profile, enabling you to work without a database. If you override the functions `create_user` and `find_user`, the current user will instead reference one of your models. The `profile` object is an *extended* Userbin profile. For more information about the available attributes in the profile see the [Userbin profile](https://userbin.com/docs/concepts) documentation.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
config.create_user = Proc.new { |profile|
|
117
|
+
User.create! do |user|
|
118
|
+
user.userbin_id = profile.id
|
119
|
+
user.email = profile.email
|
120
|
+
user.photo = profile.image
|
121
|
+
end
|
122
|
+
}
|
123
|
+
|
124
|
+
config.find_user = Proc.new { |userbin_id|
|
125
|
+
User.find_by_userbin_id(userbin_id)
|
126
|
+
}
|
127
|
+
```
|
128
|
+
|
129
|
+
You'll need to migrate your users and add a reference to the Userbin profile:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
rails g migration AddUserbinIdToUsers userbin_id:integer:index
|
133
|
+
```
|
134
|
+
|
135
|
+
### auto_include_tags
|
136
|
+
|
137
|
+
By default, the Userbin middleware will automatically insert a `<script>` tag before the closing `</body>` in your HTML files in order to handle forms, sessions and user tracking. This script loads everything asynchronously, so it won't affect your page load speed. However if you want to have control of this procedure, set `auto_include_tags` to false and initialize the library yourself. To do that, checkout the [Userbin.js configuration guide](https://userbin.com/docs/javascript#configuration).
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
config.auto_include_tags = false
|
141
|
+
```
|
142
|
+
|
143
|
+
|
144
|
+
Further configuration and customization
|
145
|
+
---------------------------------------
|
146
|
+
|
147
|
+
Your Userbin dashboard gives you access to a range of functionality:
|
148
|
+
|
149
|
+
- Configure the appearance of the login widget to feel more integrated with your service
|
150
|
+
- Connect 10+ OAuth providers like Facebook, Github and Google.
|
151
|
+
- Use Markdown to generate mobile-ready transactional emails
|
152
|
+
- Invite users to your application
|
153
|
+
- See who is logging in and when
|
154
|
+
- User management: block, remove and impersonate users
|
155
|
+
- Export all your user data from Userbin
|
156
|
+
|
157
|
+
|
88
158
|
Documentation
|
89
159
|
-------------
|
90
160
|
For complete documentation go to [userbin.com/docs](https://userbin.com/docs)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Userbin
|
2
2
|
class Authentication
|
3
3
|
|
4
|
-
CLOSING_HEAD_TAG = %r{</head>}
|
5
4
|
CLOSING_BODY_TAG = %r{</body>}
|
6
5
|
|
7
6
|
def initialize(app, options = {})
|
@@ -25,9 +24,10 @@ module Userbin
|
|
25
24
|
else
|
26
25
|
signature, data = Userbin.authenticate!(request)
|
27
26
|
|
28
|
-
if
|
29
|
-
|
30
|
-
|
27
|
+
if !Userbin.authenticated? && Userbin.config.protected_path &&
|
28
|
+
env["PATH_INFO"].start_with?(Userbin.config.protected_path)
|
29
|
+
|
30
|
+
return render_gateway(env["PATH_INFO"])
|
31
31
|
end
|
32
32
|
|
33
33
|
generate_response(env, signature, data)
|
@@ -36,8 +36,7 @@ module Userbin
|
|
36
36
|
message =
|
37
37
|
'Userbin::SecurityError: Invalid signature. Refresh to try again.'
|
38
38
|
headers = {
|
39
|
-
'Content-Type' => 'text/text'
|
40
|
-
'Content-Length' => message.length.to_s
|
39
|
+
'Content-Type' => 'text/text'
|
41
40
|
}
|
42
41
|
|
43
42
|
Rack::Utils.delete_cookie_header!(
|
@@ -49,28 +48,36 @@ module Userbin
|
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
|
-
def
|
53
|
-
Userbin.config.restricted_path
|
54
|
-
end
|
55
|
-
|
56
|
-
def link_tags(login_path)
|
57
|
-
<<-LINK_TAGS
|
58
|
-
<link rel="userbin:root" href="/" />
|
59
|
-
<link rel="userbin:login" href="#{login_path}" />
|
60
|
-
LINK_TAGS
|
61
|
-
end
|
62
|
-
|
63
|
-
def script_tag
|
51
|
+
def script_tag(login_path)
|
64
52
|
script_url = ENV.fetch('USERBIN_SCRIPT_URL') {
|
65
53
|
"//js.userbin.com"
|
66
54
|
}
|
67
|
-
|
68
|
-
|
69
|
-
|
55
|
+
path = login_path || Userbin.config.protected_path
|
56
|
+
|
57
|
+
tag = "<script src='#{script_url}?#{Userbin.config.app_id}'></script>\n"
|
58
|
+
tag += "<script type='text/javascript'>\n"
|
59
|
+
tag += " Userbin.config({\n"
|
60
|
+
if Userbin.config.root_path
|
61
|
+
tag += " logoutRedirectUrl: '#{Userbin.config.root_path}',\n"
|
62
|
+
end
|
63
|
+
tag += " loginRedirectUrl: '#{path}',\n" if path
|
64
|
+
tag += " reloadOnSuccess: true\n"
|
65
|
+
tag += " });\n"
|
66
|
+
tag += "</script>\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
def inject_tags(body, login_path = nil)
|
70
|
+
if body[CLOSING_BODY_TAG]
|
71
|
+
body = body.gsub(CLOSING_BODY_TAG, script_tag(login_path) + '\\0')
|
72
|
+
end
|
73
|
+
body
|
70
74
|
end
|
71
75
|
|
72
76
|
def render_gateway(current_path)
|
77
|
+
script_url = ENV["USERBIN_SCRIPT_URL"] || '//js.userbin.com'
|
78
|
+
|
73
79
|
login_page = <<-LOGIN_PAGE
|
80
|
+
<!DOCTYPE html>
|
74
81
|
<html>
|
75
82
|
<head>
|
76
83
|
<title>Log in</title>
|
@@ -80,44 +87,34 @@ module Userbin
|
|
80
87
|
</body>
|
81
88
|
</html>
|
82
89
|
LOGIN_PAGE
|
90
|
+
|
83
91
|
login_page = inject_tags(login_page, current_path)
|
84
|
-
[ 403,
|
85
|
-
{ 'Content-Type' => 'text/html',
|
86
|
-
'Content-Length' => login_page.length.to_s },
|
87
|
-
[login_page]
|
88
|
-
]
|
89
|
-
end
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
if body[CLOSING_BODY_TAG]
|
96
|
-
body = body.gsub(CLOSING_BODY_TAG, script_tag + '\\0')
|
97
|
-
end
|
98
|
-
body
|
93
|
+
headers = { 'Content-Type' => 'text/html' }
|
94
|
+
|
95
|
+
[403, headers, [login_page]]
|
99
96
|
end
|
100
97
|
|
101
98
|
def generate_response(env, signature, data)
|
102
99
|
status, headers, response = @app.call(env)
|
103
|
-
if headers['Content-Type'] && headers['Content-Type']['text/html']
|
104
|
-
if response.respond_to?(:body)
|
105
|
-
body = [*response.body]
|
106
|
-
else
|
107
|
-
body = response
|
108
|
-
end
|
109
|
-
|
110
|
-
if Userbin.config.auto_include_tags
|
111
|
-
body = body.each.map do |chunk|
|
112
|
-
inject_tags(chunk)
|
113
|
-
end
|
114
|
-
end
|
115
100
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
101
|
+
if headers['Content-Type'] && headers['Content-Type']['text/html']
|
102
|
+
if response.respond_to?(:body)
|
103
|
+
body = [*response.body]
|
104
|
+
else
|
105
|
+
body = response
|
106
|
+
end
|
107
|
+
if Userbin.config.auto_include_tags
|
108
|
+
body = body.each.map do |chunk|
|
109
|
+
inject_tags(chunk)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
if response.respond_to?(:body)
|
113
|
+
response.body = body
|
114
|
+
else
|
115
|
+
response = body
|
116
|
+
end
|
117
|
+
headers['Content-Length'] = body.flatten[0].length.to_s
|
121
118
|
end
|
122
119
|
|
123
120
|
if signature && data
|
@@ -2,9 +2,15 @@ module Userbin
|
|
2
2
|
class Configuration
|
3
3
|
attr_accessor :app_id
|
4
4
|
attr_accessor :api_secret
|
5
|
-
attr_accessor :current_user
|
6
5
|
attr_accessor :auto_include_tags
|
7
|
-
attr_accessor :
|
6
|
+
attr_accessor :create_user
|
7
|
+
attr_accessor :find_user
|
8
|
+
attr_accessor :protected_path
|
9
|
+
attr_accessor :root_path
|
10
|
+
|
11
|
+
# restricted_path is obsolete
|
12
|
+
alias :restricted_path :protected_path
|
13
|
+
alias :restricted_path= :protected_path=
|
8
14
|
|
9
15
|
def initialize
|
10
16
|
self.app_id = ENV["USERBIN_APP_ID"]
|
@@ -1,12 +1,15 @@
|
|
1
1
|
module Userbin
|
2
2
|
module AuthHelpers
|
3
3
|
def current_user
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
Userbin.current_user
|
5
|
+
end
|
6
|
+
|
7
|
+
def user_logged_in?
|
8
|
+
Userbin.user_logged_in?
|
9
|
+
end
|
10
|
+
|
11
|
+
def user_signed_in?
|
12
|
+
Userbin.user_signed_in?
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
data/lib/userbin/userbin.rb
CHANGED
@@ -18,10 +18,8 @@ module Userbin
|
|
18
18
|
|
19
19
|
current = Userbin::Session.new(MultiJson.decode(data))
|
20
20
|
|
21
|
-
if current.
|
22
|
-
|
23
|
-
signature, data = refresh_session(current.id)
|
24
|
-
end
|
21
|
+
if now > Time.at(current.expires_at / 1000)
|
22
|
+
signature, data = refresh_session(current.id)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
@@ -53,10 +51,34 @@ module Userbin
|
|
53
51
|
current.authenticated? rescue false
|
54
52
|
end
|
55
53
|
|
56
|
-
def self.
|
54
|
+
def self.user_logged_in?
|
55
|
+
authenticated?
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.user_signed_in?
|
59
|
+
authenticated?
|
60
|
+
end
|
61
|
+
|
62
|
+
def self._current_user
|
57
63
|
current.user if current
|
58
64
|
end
|
59
65
|
|
66
|
+
def self.current_user
|
67
|
+
if Userbin.config.find_user
|
68
|
+
u = Userbin.config.find_user.call(_current_user.id)
|
69
|
+
return u if u
|
70
|
+
if Userbin.config.create_user
|
71
|
+
u = Userbin.config.create_user.call(_current_user)
|
72
|
+
return u if u
|
73
|
+
_current_user
|
74
|
+
else
|
75
|
+
raise UnimplementedError, "You need to implement create_user"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
_current_user
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
60
82
|
def self.user
|
61
83
|
current_user
|
62
84
|
end
|
data/lib/userbin/version.rb
CHANGED
data/lib/userbin.rb
CHANGED
@@ -8,7 +8,7 @@ require "userbin/basic_auth"
|
|
8
8
|
|
9
9
|
require "userbin/railtie" if defined?(Rails::Railtie)
|
10
10
|
|
11
|
-
api_endpoint = ENV.fetch('USERBIN_API_ENDPOINT') {
|
11
|
+
api_endpoint = ENV.fetch('USERBIN_API_ENDPOINT') {3
|
12
12
|
"https://api.userbin.com"
|
13
13
|
}
|
14
14
|
|
@@ -28,6 +28,7 @@ require "userbin/authentication"
|
|
28
28
|
|
29
29
|
class Userbin::Error < Exception; end
|
30
30
|
class Userbin::SecurityError < Userbin::Error; end
|
31
|
+
class Userbin::UnimplementedError < Userbin::Error; end
|
31
32
|
|
32
33
|
module Userbin
|
33
34
|
class << self
|