userbin 0.3.5 → 0.4.2

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
  SHA1:
3
- metadata.gz: fe753d445c9d4a87e78d129234ccf88ab2e5ef1f
4
- data.tar.gz: 611c69c56f409d7157ada99598e4d0a4063ff125
3
+ metadata.gz: eae191f76ab1ba6ce4587ba1b815e3d8fabeab8e
4
+ data.tar.gz: f98042c6d8858878062c45851ca71489010c9738
5
5
  SHA512:
6
- metadata.gz: bb5795d97506c84c17cfc09b96c73d022ab2e685b554aab90a01c0be9f5f5443907e1c53c117068c2a3682dc5e7350eb02586394b6017694ad47eeb5193814ab
7
- data.tar.gz: 883d892aa1169d658c874b6f7dacc8400c5914ac1ac446187393eadad5fd30b992a915d5c252ffcce782fd932b3c6ab5af8871e2331324df9db14ea6ac3ddc66
6
+ metadata.gz: 6cbc7e71ac93e2f9eb8061e206451c144f378e4ec82ced8b3fda8ec94bb30bf020f6d9b5540d1c4e4c986927f14ab1735d66c331197ca99064ebc4429bb29e3f
7
+ data.tar.gz: c0b1103e93988a1a11ce39087be9ecda328824530403f77bf1fd20e68d2d343edd918aa4f7d6e883ea4f7cd959cf55d7175f03e88bff69e58755779b0c08f58b
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  [![Build Status](https://travis-ci.org/userbin/userbin-ruby.png)](https://travis-ci.org/userbin/userbin-ruby)
2
+ [![Gem Version](https://badge.fury.io/rb/userbin.png)](http://badge.fury.io/rb/userbin)
3
+ [![Dependency Status](https://gemnasium.com/userbin/userbin-ruby.png)](https://gemnasium.com/userbin/userbin-ruby)
2
4
 
3
5
  Userbin for Ruby
4
6
  ================
5
7
 
6
- Userbin for Ruby adds user authentication, login flows and user management to your **Rails**, **Sinatra** or **Rack** app.
8
+ Userbin for Ruby adds user authentication, login flows and user management to your **Rails**, **Sinatra** or **Rack** app, with users stored in your own database.
7
9
 
8
- [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.
10
+ [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. It takes care of linking accounts across networks, resetting passwords, and keeping everything safe and secure.
9
11
 
10
12
  [Create a free account](https://userbin.com) at Userbin to start accepting users in your application.
11
13
 
@@ -24,51 +26,76 @@ Installation
24
26
  bundle install
25
27
  ```
26
28
 
27
- 2. Configure the Userbin module with the credentials you got from signing up.
29
+ 1. Generate configuration
28
30
 
29
- 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"`.
30
-
31
- ```ruby
32
- Userbin.configure do |config|
33
- config.app_id = "YOUR_APP_ID"
34
- config.api_secret = "YOUR_API_SECRET"
35
- end
31
+ ```shell
32
+ curl https://lib.userbin.com/install.sh | sh YOUR_APP_ID YOUR_API_SECRET
36
33
  ```
37
34
 
38
- 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.
35
+ 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.
39
36
 
40
- 3. **Rack/Sinatra apps only**: Activate the Userbin Rack middleware
37
+ 1. **Rack/Sinatra apps only**: Load and activate the Userbin Rack middleware
41
38
 
42
39
  ```ruby
40
+ require 'userbin'
41
+
43
42
  use Userbin::Authentication
44
43
  ```
45
44
 
46
45
 
47
- Usage
48
- -----
46
+ Configuration
47
+ -------------
49
48
 
50
- ### Forms
49
+ ### Authenticating
51
50
 
52
- 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.
51
+ You need to configure Userbin in order to provide the `current_user` model:
53
52
 
54
- 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
+ ``` ruby
54
+ Userbin.configure do
55
+ current_user do |profile|
56
+ User.find(profile.uid).first_or_initialize(
57
+ email: profile.email,
58
+ image: profile.image
59
+ )
60
+ end
61
+ end
62
+ ```
55
63
 
56
- The following links will open up the Widget with the login or the signup form respectively.
64
+ This code is run in the context of your application so you have access to your models, session or routes helpers. However, since this code is not run in the context of your application's ApplicationController it doesn't have access
65
+ to the methods defined over there.
57
66
 
58
- ```html
59
- <a class="ub-login">Log in</a>
60
- ```
61
67
 
62
- ```html
63
- <a class="ub-signup">Sign up</a>
68
+ ### Protecting routes
69
+
70
+ Use `authorize` to control access in controllers:
71
+
72
+ ```ruby
73
+ class ArticlesController < ApplicationController
74
+ before_filter :authorize
75
+
76
+ def index
77
+ current_user.articles
78
+ end
79
+ end
64
80
  ```
65
81
 
66
- The logout link will clear the session and redirect the user back to your root path:
82
+ Or, you can authorize users in `config/routes.rb`:
67
83
 
68
- ```html
69
- <a class="ub-logout">Log out</a>
84
+ ```ruby
85
+ Blog::Application.routes.draw do
86
+ constraints Userbin::Protect.new { |user| user.admin? } do
87
+ root to: 'admin'
88
+ end
89
+
90
+ constraints Userbin::Protect.new do
91
+ mount MagicalWorker
92
+ end
93
+ end
70
94
  ```
71
95
 
96
+ Usage
97
+ -----
98
+
72
99
  ### The current user
73
100
 
74
101
  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).
@@ -85,8 +112,31 @@ To check if a user is logged in, use `user_logged_in?` (or its alias `user_signe
85
112
  <% end %>
86
113
  ```
87
114
 
115
+
88
116
  **Rack/Sinatra apps only**: Since above helpers aren't available outside Rails, instead use `Userbin.current_user` and `Userbin.user_logged_in?`.
89
117
 
118
+ ### Forms
119
+
120
+ 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.
121
+
122
+ 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.
123
+
124
+ Use `current_user` and `logged_in?` in controllers, views, and helpers:
125
+
126
+ ```haml
127
+ - if signed_in?
128
+ = current_user.email
129
+ = link_to 'Log out', '/', class: 'ub-logout'
130
+ - else
131
+ = link_to 'Sign up', '/dashboard', class: 'ub-signup'
132
+ = link_to 'Log in', '/dashboard', class: 'ub-login'
133
+ ```
134
+
135
+ ### Protecting resources
136
+
137
+ ...
138
+
139
+
90
140
  Configuration
91
141
  -------------
92
142
 
data/lib/userbin.rb CHANGED
@@ -16,9 +16,7 @@ api_endpoint = ENV.fetch('USERBIN_API_ENDPOINT') {
16
16
  c.use Userbin::BasicAuth
17
17
  c.use Faraday::Request::UrlEncoded
18
18
  c.use Her::Middleware::DefaultParseJSON
19
- #c.use Userbin::ParseSignedJSON
20
19
  c.use Faraday::Adapter::NetHttp
21
- #c.use Userbin::VerifySignature
22
20
  end
23
21
 
24
22
  require "userbin/configuration"
@@ -12,41 +12,29 @@ module Userbin
12
12
  raise ConfigurationError, "app_id and api_secret must be present"
13
13
  end
14
14
 
15
+ Thread.current[:userbin] = nil
16
+
15
17
  request = Rack::Request.new(env)
16
18
 
17
19
  begin
18
- if env["PATH_INFO"] == "/userbin" &&
19
- env["REQUEST_METHOD"] == "POST"
20
- signature, data = Userbin.authenticate_events!(request)
21
-
22
- MultiJson.decode(data)['events'].each do |event|
23
- Userbin::Events.trigger(event)
24
- end
25
-
26
- [ 200, { 'Content-Type' => 'text/html',
27
- 'Content-Length' => '2' }, ['OK'] ]
28
- else
29
- signature, data = Userbin.authenticate!(request)
30
-
31
- if !Userbin.authenticated? && Userbin.config.protected_path &&
32
- env["PATH_INFO"].start_with?(Userbin.config.protected_path)
20
+ jwt = Userbin.authenticate!(request)
33
21
 
34
- return render_gateway(env["PATH_INFO"])
35
- end
22
+ if !Userbin.authenticated? && Userbin.config.protected_path &&
23
+ env["PATH_INFO"].start_with?(Userbin.config.protected_path)
36
24
 
37
- generate_response(env, signature, data)
25
+ return render_gateway(env["PATH_INFO"])
38
26
  end
27
+
28
+ generate_response(env, jwt)
39
29
  rescue Userbin::SecurityError
40
30
  message =
41
- 'Userbin::SecurityError: Invalid signature. Refresh to try again.'
31
+ 'Userbin::SecurityError: Invalid session. Refresh to try again.'
42
32
  headers = {
43
33
  'Content-Type' => 'text/text'
44
34
  }
45
35
 
46
36
  Rack::Utils.delete_cookie_header!(
47
- headers, '_ubs', value = {})
48
- Rack::Utils.delete_cookie_header!(
49
- headers, '_ubd', value = {})
37
+ headers, '_ubt', value = {})
50
38
 
51
39
  [ 400, headers, [message] ]
52
40
  end
@@ -99,7 +87,7 @@ module Userbin
99
87
  [403, headers, [login_page]]
100
88
  end
101
89
 
102
- def generate_response(env, signature, data)
90
+ def generate_response(env, jwt)
103
91
  status, headers, response = @app.call(env)
104
92
 
105
93
  if headers['Content-Type'] && headers['Content-Type']['text/html']
@@ -123,16 +111,12 @@ module Userbin
123
111
  end
124
112
  end
125
113
 
126
- if signature && data
114
+ if jwt
127
115
  Rack::Utils.set_cookie_header!(
128
- headers, '_ubs', value: signature, path: '/')
129
- Rack::Utils.set_cookie_header!(
130
- headers, '_ubd', value: data, path: '/')
116
+ headers, '_ubt', value: jwt, path: '/')
131
117
  else
132
118
  Rack::Utils.delete_cookie_header!(
133
- headers, '_ubs', value = {})
134
- Rack::Utils.delete_cookie_header!(
135
- headers, '_ubd', value = {})
119
+ headers, '_ubt', value = {})
136
120
  end
137
121
 
138
122
  [status, headers, response]
@@ -11,9 +11,7 @@ module Userbin
11
11
  class VerifySignature < Faraday::Response::Middleware
12
12
  def call(env)
13
13
  @app.call(env).on_complete do
14
- signature = env[:response_headers]['x-userbin-signature']
15
- data = env[:body]
16
- Userbin.valid_signature?(signature, data)
14
+ Userbin.decode_jwt(env[:body])
17
15
  end
18
16
  end
19
17
  end
@@ -1,42 +1,37 @@
1
1
  module Userbin
2
- def self.authenticate_events!(request, now = Time.now)
3
- signature, data =
4
- request.params.values_at('signature', 'data')
5
-
6
- valid_signature?(signature, data)
7
-
8
- [signature, data]
2
+ def self.decode_jwt(jwt)
3
+ JWT.decode(jwt, Userbin.config.api_secret)
9
4
  end
10
5
 
11
- # Provide either a Rack::Request or a Hash containing :signature and :data.
12
- #
13
- def self.authenticate!(request, now = Time.now)
14
- signature, data =
15
- request.cookies.values_at('_ubs', '_ubd')
6
+ def self.authenticate!(request)
7
+ jwt = request.cookies['_ubt']
8
+ return unless jwt
16
9
 
17
- if signature && data && valid_signature?(signature, data)
10
+ decoded = Userbin.decode_jwt(jwt)
18
11
 
19
- current = Userbin::Session.new(MultiJson.decode(data))
12
+ if Time.now > Time.at(decoded['expires_at'] / 1000)
13
+ jwt = refresh_session(decoded['id'])
14
+ return unless jwt
20
15
 
21
- if now > Time.at(current.expires_at / 1000)
22
- signature, data = refresh_session(current.id)
16
+ decoded = Userbin.decode_jwt(jwt)
17
+
18
+ if Time.now > Time.at(decoded['expires_at'] / 1000)
19
+ raise Userbin::SecurityError
23
20
  end
24
21
  end
25
22
 
26
- tmp = MultiJson.decode(data) if data
27
-
28
- self.current = Userbin::Session.new(tmp)
23
+ self.current = Userbin::Session.new(decoded)
29
24
 
30
- [signature, data]
25
+ return jwt
31
26
  end
32
27
 
33
28
  def self.refresh_session(session_id)
34
29
  api_endpoint = ENV["USERBIN_API_ENDPOINT"] || 'https://api.userbin.com'
35
- uri = URI("#{api_endpoint}/sessions/#{session_id}/refresh")
30
+ uri = URI("#{api_endpoint}/sessions/#{session_id}/refresh.jwt")
36
31
  uri.user = config.app_id
37
32
  uri.password = config.api_secret
38
33
  net = Net::HTTP.post_form(uri, {})
39
- [net['X-Userbin-Signature'], net.body]
34
+ net.body
40
35
  end
41
36
 
42
37
  def self.current
@@ -107,15 +102,4 @@ module Userbin
107
102
  def self.user
108
103
  current_user
109
104
  end
110
-
111
- private
112
-
113
- # Checks signature against secret and returns boolean
114
- #
115
- def self.valid_signature?(signature, data)
116
- digest = OpenSSL::Digest::SHA256.new
117
- valid = signature == OpenSSL::HMAC.hexdigest(digest, config.api_secret, data)
118
- raise SecurityError, "Invalid signature" unless valid
119
- valid
120
- end
121
105
  end
@@ -1,3 +1,3 @@
1
1
  module Userbin
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: userbin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johan