userbin 1.6.1 → 1.7.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 +17 -256
- data/lib/userbin.rb +1 -0
- data/lib/userbin/client.rb +6 -1
- data/lib/userbin/request.rb +4 -2
- data/lib/userbin/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a4569c81702c9644066b6bdaa74246a068632ba
|
4
|
+
data.tar.gz: 17a9fd3f2c51969565ca485adf025601e74b4029
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79a9d2ac54fd2dafed09ca930e769f48b01d8b1b8896cde859f7ea98c2fa599b77529018409dac5ab41b902da43882878d43df8160079d324c4ffe78206b3b5b
|
7
|
+
data.tar.gz: b7024885c8ee6fca6566550b763ca2f81f7684fbda42bce710c90bf1e27378ade15597ab93a0b4db6b2384a3be06811f0b7e0bd166b1a526b2af0f4f08623d7e
|
data/README.md
CHANGED
@@ -5,286 +5,47 @@
|
|
5
5
|
[![Dependency Status](https://gemnasium.com/userbin/userbin-ruby.png)](https://gemnasium.com/userbin/userbin-ruby)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/userbin/userbin-ruby/badge.png)](https://coveralls.io/r/userbin/userbin-ruby)
|
7
7
|
|
8
|
-
**[Userbin](https://userbin.com) adds
|
9
|
-
|
10
|
-
Your users **do not** need to be signed up or registered for Userbin before using the service and there's no need for them to download any proprietary apps. Also, Userbin requires **no modification of your current database schema** as it uses your local user IDs.
|
11
|
-
|
12
|
-
## Table of Contents
|
13
|
-
|
14
|
-
- [Installation](#installation)
|
15
|
-
- [Getting Started](#getting-started)
|
16
|
-
- [Active Sessions](#active-sessions)
|
17
|
-
- [Security Events](#security-events)
|
18
|
-
- [Two-factor Authentication](#two-factor-authentication)
|
19
|
-
- [Pairing with Google Authenticator](#pairing-with-google-authenticator)
|
20
|
-
- [Pairing with Phone Number (SMS)](#pairing-with-phone-number-sms)
|
21
|
-
- [Pairing with YubiKey](#pairing-with-yubikey)
|
22
|
-
- [Enabling and Disabling](#enabling-and-disabling)
|
23
|
-
- [Authenticating](#authenticating)
|
24
|
-
- [Backup Codes](#backup-codes)
|
25
|
-
- [List Pairings](#list-pairings)
|
8
|
+
**[Userbin](https://userbin.com) adds real-time monitoring of your authentication stack, instantly notifying you and your users on potential account hijacks.**
|
26
9
|
|
27
10
|
## Installation
|
28
11
|
|
29
12
|
Add the `userbin` gem to your `Gemfile`
|
30
13
|
|
31
14
|
```ruby
|
32
|
-
gem
|
15
|
+
gem 'userbin'
|
33
16
|
```
|
34
17
|
|
35
18
|
Load and configure the library with your Userbin API secret in an initializer or similar.
|
36
19
|
|
37
20
|
```ruby
|
38
|
-
Userbin.api_secret =
|
21
|
+
Userbin.api_secret = 'YOUR_API_SECRET'
|
39
22
|
```
|
40
23
|
|
41
|
-
|
42
|
-
|
43
|
-
### 1. Logging in and out
|
24
|
+
A Userbin client instance will automatically be made available as `userbin` in your Rails, Sinatra or Padrino controllers.
|
44
25
|
|
45
|
-
|
26
|
+
## Tracking security events
|
46
27
|
|
47
|
-
|
48
|
-
def your_after_login_hook
|
49
|
-
userbin.login(current_user.id, email: current_user.email)
|
50
|
-
end
|
51
|
-
```
|
28
|
+
`track` lets you record the security-related actions your users perform. The more actions you track, the more accurate Userbin is in identifying fraudsters.
|
52
29
|
|
53
|
-
When
|
30
|
+
When you have access to a logged in user, send along the same user identifier as when you initiated Userbin.js.
|
54
31
|
|
55
32
|
```ruby
|
56
|
-
|
57
|
-
|
58
|
-
|
33
|
+
userbin.track(
|
34
|
+
user_id: user.id,
|
35
|
+
name: 'login.succeeded')
|
59
36
|
```
|
60
37
|
|
61
|
-
|
62
|
-
|
63
|
-
### 2. Protecting routes
|
64
|
-
|
65
|
-
Call `authorize!` just before your `current_user` is being initialized. Usually you'll want to override your normal authentication filter, e.g. `authenticate_user!` if you're using Devise.
|
66
|
-
|
67
|
-
- `UserUnauthorizedError` will be raised if `login` has not yet been called, or if the session is no longer valid.
|
68
|
-
- `ChallengeRequiredError` will be raised when the user has enabled two-factor authentication and is logging in from an untrusted device.
|
38
|
+
If you don't have access to a logged in user just omit `user_id`, typically when tracking failed logins.
|
69
39
|
|
70
40
|
```ruby
|
71
|
-
|
72
|
-
rescue_from Userbin::UserUnauthorizedError, with: :user_unauthorized
|
73
|
-
rescue_from Userbin::ChallengeRequiredError, with: :challenge_required
|
74
|
-
|
75
|
-
# IMPLEMENT: Override the authentication method from your framework
|
76
|
-
def authenticate_user!
|
77
|
-
userbin.authorize!
|
78
|
-
super
|
79
|
-
end
|
80
|
-
|
81
|
-
# IMPLEMENT: Log out your user locally
|
82
|
-
def user_unauthorized
|
83
|
-
sign_out
|
84
|
-
redirect_to root_path
|
85
|
-
end
|
86
|
-
|
87
|
-
# IMPLEMENT: Redirect to two-factor authentication login
|
88
|
-
def challenge_required
|
89
|
-
redirect_to show_challenge_path
|
90
|
-
end
|
91
|
-
end
|
41
|
+
userbin.track(name: 'login.failed')
|
92
42
|
```
|
93
43
|
|
94
|
-
|
95
|
-
|
96
|
-
```ruby
|
97
|
-
class AccountController < ApplicationController
|
98
|
-
before_filter :authenticate_user!
|
99
|
-
end
|
100
|
-
```
|
101
|
-
|
102
|
-
## Active Sessions
|
103
|
-
|
104
|
-
Show a list of sessions currently signed to a user's account.
|
105
|
-
|
106
|
-
The *context* is from the last recorded [security event](#security-events) on a session.
|
107
|
-
|
108
|
-
```ruby
|
109
|
-
userbin.sessions.each do |session|
|
110
|
-
puts session.id # => 'yt9BkoHzcQoou4jqbQbJUqqMdxyxvCBr'
|
111
|
-
puts session.context.ip # => '88.12.129.1'
|
112
|
-
end
|
113
|
-
```
|
114
|
-
|
115
|
-
Destroy a session to revoke access and trigger a `UserUnauthorizedError` the next time `authorize!` refreshes the session token, which is within 5 minutes.
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
userbin.sessions.destroy('yt9BkoHzcQoou4jqbQbJUqqMdxyxvCBr')
|
119
|
-
```
|
120
|
-
|
121
|
-
## Security Events
|
122
|
-
|
123
|
-
List a user's recent account activity, which include security events such as user logins and failed two-factor attempts. See the [Event API](https://api.userbin.com/#events) for a list of all the available events.
|
124
|
-
|
125
|
-
```ruby
|
126
|
-
userbin.events.each do |event|
|
127
|
-
puts event.name # => 'session.created'
|
128
|
-
puts event.context.ip # => '88.12.129.1'
|
129
|
-
puts event.context.location.country # => 'Sweden'
|
130
|
-
puts event.context.user_agent.browser # => 'Chrome'
|
131
|
-
end
|
132
|
-
```
|
133
|
-
|
134
|
-
## Two-factor Authentication
|
135
|
-
|
136
|
-
Using two-factor authentication involves two steps: **pairing** and **authenticating**.
|
137
|
-
|
138
|
-
### Pairing
|
139
|
-
|
140
|
-
Before your users can protect their account with two-factor authentication, they will need to pair their their preferred way of authenticating. The [Pairing API](https://api.userbin.com/#pairings) lets users add, verify, and remove authentication channels. Only *verified* pairings are valid for authentication.
|
141
|
-
|
142
|
-
#### Pairing with Google Authenticator
|
143
|
-
|
144
|
-
The user visits a page to add Google Authenticator to their account. First create a new Authenticator pairing to generate a QR code image.
|
145
|
-
|
146
|
-
```ruby
|
147
|
-
@authenticator = userbin.pairings.create(type: 'authenticator')
|
148
|
-
```
|
149
|
-
|
150
|
-
Render a page containing the QR code, which the user scans with Google Authenticator.
|
151
|
-
|
152
|
-
```erb
|
153
|
-
<img src="<%= @authenticator[:qr_url] %>">
|
154
|
-
```
|
155
|
-
|
156
|
-
After scanning the QR code, the user will enter the 6 digit token that Google Authenticator displays, and submit the form. Capture the response and verify the pairing.
|
157
|
-
|
158
|
-
```ruby
|
159
|
-
begin
|
160
|
-
userbin.pairings.verify(params[:pairing_id], response: params[:code])
|
161
|
-
rescue Userbin::InvalidParametersError
|
162
|
-
flash.notice = 'Wrong code, try again'
|
163
|
-
end
|
164
|
-
```
|
165
|
-
|
166
|
-
#### Pairing with Phone Number (SMS)
|
167
|
-
|
168
|
-
Create a new phone number pairing which will send out a verification SMS.
|
169
|
-
|
170
|
-
```ruby
|
171
|
-
@phone_number = userbin.pairings.create(
|
172
|
-
type: 'phone_number', number: '+1739855455')
|
173
|
-
```
|
174
|
-
|
175
|
-
Catch the code from the user to pair the phone number.
|
176
|
-
|
177
|
-
```ruby
|
178
|
-
begin
|
179
|
-
userbin.pairings.verify(params[:pairing_id], response: params[:code])
|
180
|
-
rescue Userbin::InvalidParametersError
|
181
|
-
flash.notice = 'Wrong code, try again'
|
182
|
-
end
|
183
|
-
```
|
184
|
-
|
185
|
-
#### Pairing with YubiKey
|
186
|
-
|
187
|
-
YubiKeys are immediately verified for two-factor authentication.
|
188
|
-
|
189
|
-
```ruby
|
190
|
-
begin
|
191
|
-
userbin.pairings.create(type: 'yubikey', otp: params[:code])
|
192
|
-
rescue Userbin::InvalidParametersError
|
193
|
-
flash.notice = 'Wrong code, try again'
|
194
|
-
end
|
195
|
-
```
|
196
|
-
|
197
|
-
#### Enabling and Disabling
|
198
|
-
|
199
|
-
For the sake of flexibility, two-factor authentication isn't enabled automatically when you add your first pairing.
|
200
|
-
|
201
|
-
```ruby
|
202
|
-
userbin.enable_mfa!
|
203
|
-
userbin.disable_mfa!
|
204
|
-
```
|
205
|
-
|
206
|
-
### Authenticating
|
207
|
-
|
208
|
-
If the user has enabled two-factor authentication, `authorize!` might raise `ChallengeRequiredError`, which means they'll have to verify a challenge to proceed.
|
209
|
-
|
210
|
-
Capture this error just as with UserUnauthorizedError and redirect the user to a path **not protected** by `authorize!`.
|
211
|
-
|
212
|
-
If the user tries to reach a path protected by `authorize!` after a challenge has been created but still not verified, the session will be destroyed and UserUnauthorizedError raised.
|
213
|
-
|
214
|
-
```ruby
|
215
|
-
class ApplicationController < ActionController::Base
|
216
|
-
rescue_from Userbin::ChallengeRequiredError do |exception|
|
217
|
-
redirect_to show_challenge_path
|
218
|
-
end
|
219
|
-
# ...
|
220
|
-
end
|
221
|
-
```
|
222
|
-
|
223
|
-
Create a challenge, which will send the user and SMS if this is the default pairing. After the challenge has been verified, `authorize!` will not throw any further exceptions until any suspicious behavior is detected.
|
224
|
-
|
225
|
-
When you call `trust_device`, the user will not be challenged for secondary authentication when they log in to your application from that device for a set period of time. You could add this to your form as a checkbox option.
|
226
|
-
|
227
|
-
```ruby
|
228
|
-
class ChallengeController < ApplicationController
|
229
|
-
def show
|
230
|
-
@challenge = userbin.challenges.create
|
231
|
-
end
|
232
|
-
|
233
|
-
def verify
|
234
|
-
challenge_id = params.require(:challenge_id)
|
235
|
-
code = params.require(:code)
|
236
|
-
|
237
|
-
userbin.challenges.verify(challenge_id, response: code)
|
238
|
-
|
239
|
-
# Avoid verification on next login for better experience
|
240
|
-
userbin.trust_device if params[:trust_device]
|
241
|
-
|
242
|
-
# Yay, the challenge was verified!
|
243
|
-
redirect_to root_url
|
244
|
-
|
245
|
-
rescue Userbin::ForbiddenError => e
|
246
|
-
sign_out # log out your user locally
|
247
|
-
|
248
|
-
flash.notice = 'Wrong code, bye!'
|
249
|
-
redirect_to root_path
|
250
|
-
end
|
251
|
-
end
|
252
|
-
```
|
253
|
-
|
254
|
-
### Backup Codes
|
255
|
-
|
256
|
-
List or generate new backup codes used for when the user didn't bring their authentication device.
|
257
|
-
|
258
|
-
```ruby
|
259
|
-
userbin.backup_codes
|
260
|
-
userbin.generate_backup_codes(count: 8)
|
261
|
-
```
|
262
|
-
|
263
|
-
### List Pairings
|
264
|
-
|
265
|
-
List all pairings.
|
266
|
-
|
267
|
-
```ruby
|
268
|
-
# List all pairings
|
269
|
-
userbin.pairings.each do |pairing|
|
270
|
-
puts pairing.id # => 'yt9BkoHzcQoou4jqbQbJUqqMdxyxvCBr'
|
271
|
-
puts pairing.type # => 'authenticator'
|
272
|
-
puts pairing.default # => true
|
273
|
-
end
|
274
|
-
```
|
275
|
-
|
276
|
-
Set a pairing as the default one.
|
277
|
-
|
278
|
-
```ruby
|
279
|
-
userbin.pairings.set_default!('yt9BkoHzcQoou4jqbQbJUqqMdxyxvCBr')
|
280
|
-
```
|
281
|
-
|
282
|
-
Remove a pairing. If you remove the default pairing, two-factor authentication will be disabled.
|
283
|
-
|
284
|
-
```ruby
|
285
|
-
userbin.pairings.destroy('yt9BkoHzcQoou4jqbQbJUqqMdxyxvCBr')
|
286
|
-
```
|
44
|
+
All the available events are:
|
287
45
|
|
46
|
+
- `login.succeeded`
|
47
|
+
- `login.failed`
|
48
|
+
- `logout.succeeded`
|
288
49
|
|
289
50
|
## Configuration
|
290
51
|
|
@@ -293,7 +54,7 @@ Userbin.configure do |config|
|
|
293
54
|
# Same as setting it through Userbin.api_secret
|
294
55
|
config.api_secret = 'secret'
|
295
56
|
|
296
|
-
# Userbin::RequestError is raised when timing out (default:
|
57
|
+
# Userbin::RequestError is raised when timing out (default: 30.0)
|
297
58
|
config.request_timeout = 2.0
|
298
59
|
end
|
299
60
|
```
|
data/lib/userbin.rb
CHANGED
data/lib/userbin/client.rb
CHANGED
@@ -32,7 +32,8 @@ module Userbin
|
|
32
32
|
|
33
33
|
@request_context = {
|
34
34
|
ip: request.ip,
|
35
|
-
user_agent: request.user_agent
|
35
|
+
user_agent: request.user_agent,
|
36
|
+
cookie_id: cookies['__cid']
|
36
37
|
}
|
37
38
|
end
|
38
39
|
|
@@ -126,5 +127,9 @@ module Userbin
|
|
126
127
|
def has_default_pairing?
|
127
128
|
@store.session_token ? @store.session_token.has_default_pairing? : false
|
128
129
|
end
|
130
|
+
|
131
|
+
def track(opts = {})
|
132
|
+
Userbin::Event.post('/v1/events', opts)
|
133
|
+
end
|
129
134
|
end
|
130
135
|
end
|
data/lib/userbin/request.rb
CHANGED
@@ -106,9 +106,11 @@ module Userbin
|
|
106
106
|
return @app.call(env) unless userbin
|
107
107
|
|
108
108
|
userbin.request_context.each do |key, value|
|
109
|
-
|
109
|
+
if value
|
110
|
+
header =
|
110
111
|
"X-Userbin-#{key.to_s.gsub('_', '-').gsub(/\w+/) {|m| m.capitalize}}"
|
111
|
-
|
112
|
+
env[:request_headers][header] = value
|
113
|
+
end
|
112
114
|
end
|
113
115
|
@app.call(env)
|
114
116
|
end
|
data/lib/userbin/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: userbin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: her
|