heroku-bouncer 0.4.0.pre2 → 0.4.0.pre3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +16 -5
- data/README.md +45 -17
- data/Rakefile +9 -0
- data/lib/heroku/bouncer/middleware.rb +43 -4
- metadata +63 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e309a66ada0259f78b01d8524259250a0cbba5ab
|
4
|
+
data.tar.gz: 66b98fb8a1fa87093aa6b632c1097077a3d13792
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c334ee9efd1af0e1c6e9e9d9ea72c3c26f63b514179cb6a163cd7bb58859b3b6497811f7fada20ff59ae571f963ec430d4f4929d5d9c99d282e81c9d65cd4143
|
7
|
+
data.tar.gz: dd1ce965ccea7181e62189b361b5125d730a0cb30c5fdf3cbb543d07cae42b5bd8604b546ca7cc70d3f6c04e1a4c1747316664cd018396e4d1268963385fa77c
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
heroku-bouncer (0.
|
4
|
+
heroku-bouncer (0.4.0.pre2)
|
5
5
|
faraday (~> 0.8)
|
6
|
-
multi_json (~> 1.0)
|
7
6
|
omniauth-heroku (>= 0.1.0)
|
7
|
+
rack (~> 1.0)
|
8
8
|
sinatra (~> 1.0)
|
9
9
|
|
10
10
|
GEM
|
@@ -12,12 +12,16 @@ GEM
|
|
12
12
|
specs:
|
13
13
|
faraday (0.8.8)
|
14
14
|
multipart-post (~> 1.2.0)
|
15
|
-
hashie (2.0
|
15
|
+
hashie (1.2.0)
|
16
16
|
httpauth (0.2.0)
|
17
17
|
jwt (0.1.8)
|
18
18
|
multi_json (>= 1.5)
|
19
|
+
metaclass (0.0.1)
|
19
20
|
minitest (5.0.8)
|
20
|
-
|
21
|
+
minitest-spec-context (0.0.3)
|
22
|
+
mocha (0.14.0)
|
23
|
+
metaclass (~> 0.0.1)
|
24
|
+
multi_json (1.8.2)
|
21
25
|
multipart-post (1.2.0)
|
22
26
|
oauth2 (0.8.1)
|
23
27
|
faraday (~> 0.8)
|
@@ -37,7 +41,10 @@ GEM
|
|
37
41
|
rack (1.5.2)
|
38
42
|
rack-protection (1.5.0)
|
39
43
|
rack
|
40
|
-
|
44
|
+
rack-test (0.6.2)
|
45
|
+
rack (>= 1.0)
|
46
|
+
rake (10.1.0)
|
47
|
+
sinatra (1.4.4)
|
41
48
|
rack (~> 1.4)
|
42
49
|
rack-protection (~> 1.4)
|
43
50
|
tilt (~> 1.3, >= 1.3.4)
|
@@ -49,3 +56,7 @@ PLATFORMS
|
|
49
56
|
DEPENDENCIES
|
50
57
|
heroku-bouncer!
|
51
58
|
minitest (~> 5.0)
|
59
|
+
minitest-spec-context
|
60
|
+
mocha
|
61
|
+
rack-test
|
62
|
+
rake
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/heroku/heroku-bouncer.png)](https://travis-ci.org/heroku/heroku-bouncer)
|
2
|
+
|
1
3
|
# Heroku Bouncer
|
2
4
|
|
3
5
|
Heroku Bouncer is a Rack middleware (implemented in Sinatra) that
|
@@ -40,7 +42,8 @@ Sinatra app that uses heroku-bouncer.
|
|
40
42
|
|
41
43
|
# use `openssl rand -base64 32` to generate a secret
|
42
44
|
use Rack::Session::Cookie, secret: "..."
|
43
|
-
use Heroku::Bouncer
|
45
|
+
use Heroku::Bouncer,
|
46
|
+
oauth: { id: "...", secret: "..." }, secret: "..."
|
44
47
|
run MyApp
|
45
48
|
```
|
46
49
|
|
@@ -54,7 +57,8 @@ Sinatra app that uses heroku-bouncer.
|
|
54
57
|
class MyApp < Sinatra::Base
|
55
58
|
...
|
56
59
|
enable :sessions, secret: "..."
|
57
|
-
use ::Heroku::Bouncer
|
60
|
+
use ::Heroku::Bouncer,
|
61
|
+
oauth: { id: "...", secret: "..." }, secret: "..."
|
58
62
|
...
|
59
63
|
```
|
60
64
|
|
@@ -63,10 +67,11 @@ Sinatra app that uses heroku-bouncer.
|
|
63
67
|
Add a middleware configuration line to `config/application.rb`:
|
64
68
|
|
65
69
|
```ruby
|
66
|
-
config.middleware.use ::Heroku::Bouncer
|
70
|
+
config.middleware.use ::Heroku::Bouncer,
|
71
|
+
oauth: { id: "...", secret: "..." }, secret: "..."
|
67
72
|
```
|
68
73
|
|
69
|
-
4.
|
74
|
+
4. Fill in the required settings `:oauth` and `:secret` as explained
|
70
75
|
below.
|
71
76
|
|
72
77
|
## Settings
|
@@ -77,15 +82,15 @@ Two settings are **required**:
|
|
77
82
|
* `secret`: A random string used as an encryption secret used to secure
|
78
83
|
the user information in the session.
|
79
84
|
|
80
|
-
|
85
|
+
Using environment variables for these is recommended, for example:
|
81
86
|
|
82
87
|
```ruby
|
83
88
|
use Heroku::Bouncer,
|
84
|
-
oauth: { id:
|
85
|
-
secret:
|
89
|
+
oauth: { id: ENV['HEROKU_OAUTH_ID'], secret: ENV['HEROKU_OAUTH_SECRET'] },
|
90
|
+
secret: ENV['HEROKU_BOUNCER_SECRET']
|
86
91
|
```
|
87
92
|
|
88
|
-
There are
|
93
|
+
There are 7 additional options you can pass to the middleware:
|
89
94
|
|
90
95
|
* `oauth[:scope]`: The [OAuth scope][] to use when requesting the OAuth
|
91
96
|
token. Default: `identity`.
|
@@ -98,9 +103,13 @@ There are 5 options you can pass to the middleware:
|
|
98
103
|
Default: `true`
|
99
104
|
* `expose_user`: Expose the user attributes in the session. Default:
|
100
105
|
`true`
|
106
|
+
* `session_sync_nonce`: If present, determines the name of a cookie shared across properties under a same domain in order to keep their sessions synchronized. Default: `nil`
|
107
|
+
* `allow_anonymous`: Accepts a lambda that gets called with each request. If the lambda evals to true, the request will not enforce authentication (e.g: `allow_anonymous: lambda { |req| !/\A\/admin/.match(req.fullpath) }` will allow anonymous requests except those with under the `/admin` path). Default: `nil`, which does not allow anonymous access to any URL.
|
108
|
+
|
101
109
|
|
102
110
|
You use these by passing a hash to the `use` call, for example:
|
103
111
|
|
112
|
+
|
104
113
|
```ruby
|
105
114
|
use Heroku::Bouncer,
|
106
115
|
oauth: { id: "...", secret: "...", scope: "global" },
|
@@ -124,7 +133,7 @@ You can access this in Sinatra and Rails by `request.env[key]`, e.g.
|
|
124
133
|
|
125
134
|
If you set `expose_token` to `true`, you'll get an API token that you
|
126
135
|
can use to make Heroku API calls on behalf of the logged-in user using
|
127
|
-
[heroku.rb]
|
136
|
+
[heroku.rb][] .
|
128
137
|
|
129
138
|
```ruby
|
130
139
|
heroku = Heroku::API.new(:api_key => request.env["bouncer.token"])
|
@@ -143,16 +152,35 @@ logging in again.
|
|
143
152
|
|
144
153
|
## Conditionally enable the middleware
|
145
154
|
|
146
|
-
Don't want to OAuth on every request? Use a middleware to conditionally
|
147
|
-
enable this middleware, like [Rack::Builder][].
|
148
|
-
Alternatively, [use inheritance to extend the middleware to act any way
|
149
|
-
you like][inheritance].
|
155
|
+
> Don't want to OAuth on every request? Use a middleware to conditionally
|
156
|
+
> enable this middleware, like [Rack::Builder][].
|
157
|
+
> Alternatively, [use inheritance to extend the middleware to act any way
|
158
|
+
> you like][inheritance].
|
150
159
|
|
151
|
-
|
160
|
+
Due to changes in how the middleware stack is built, this is currently
|
161
|
+
broken in the 0.4.0 prereleases.
|
152
162
|
|
153
|
-
|
154
|
-
|
163
|
+
## Security Model: A Tale of Three Secrets
|
164
|
+
|
165
|
+
There are three secrets in use:
|
166
|
+
|
167
|
+
* A OAuth secret. Paired with the OAuth ID, this is how the Heroku
|
168
|
+
authorizes your OAuth requests with your particular OAuth client.
|
169
|
+
* A bouncer secret. Bouncer encrypts and signs all user data placed in
|
170
|
+
the session. This ensures such data cannot be viewed by anyone but the
|
171
|
+
application.
|
172
|
+
* A session secret. This is used to sign the session, which validates
|
173
|
+
the session was generated by your application. Strictly speaking,
|
174
|
+
however, this is outside of Bouncer and is not a part of its security
|
175
|
+
model.
|
176
|
+
|
177
|
+
In totality, Heroku Bouncer ensures session data can only be generated
|
178
|
+
and read by your application. However, they do not protect against
|
179
|
+
replay attacks if the data is obtained in its entirety. SSL and session
|
180
|
+
timeouts should be used to help mitigate this risk. CSRF tokens for any
|
181
|
+
actions that modify data are also recommended.
|
155
182
|
|
156
|
-
[OAuth scope]: https://devcenter.heroku.com/articles/oauth#scopes
|
157
183
|
[Rack::Builder]: http://rack.rubyforge.org/doc/Rack/Builder.html
|
158
184
|
[inheritance]: https://gist.github.com/wuputah/5534428
|
185
|
+
[OAuth scope]: https://devcenter.heroku.com/articles/oauth#scopes
|
186
|
+
[heroku.rb]: https://github.com/heroku/heroku.rb
|
data/Rakefile
CHANGED
@@ -24,6 +24,8 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
|
|
24
24
|
@expose_token = extract_option(options, :expose_token, false)
|
25
25
|
@expose_email = extract_option(options, :expose_email, true)
|
26
26
|
@expose_user = extract_option(options, :expose_user, true)
|
27
|
+
@session_sync_nonce = extract_option(options, :session_sync_nonce, nil)
|
28
|
+
@allow_anonymous = extract_option(options, :allow_anonymous, nil)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -44,10 +46,36 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
|
|
44
46
|
return_value
|
45
47
|
end
|
46
48
|
|
49
|
+
def auth_request?
|
50
|
+
%w[/auth/heroku/callback /auth/heroku /auth/failure /auth/sso-logout /auth/logout /auth/login].include?(request.path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def session_nonce_mismatch?
|
54
|
+
(store_read(@session_sync_nonce.to_sym).to_s != session_nonce_cookie.to_s) && !auth_request?
|
55
|
+
end
|
56
|
+
|
57
|
+
def session_nonce_cookie
|
58
|
+
@session_sync_nonce && request.cookies[@session_sync_nonce]
|
59
|
+
end
|
60
|
+
|
61
|
+
def anonymous_request_allowed?
|
62
|
+
auth_request? || (@allow_anonymous && @allow_anonymous.call(request))
|
63
|
+
end
|
64
|
+
|
47
65
|
before do
|
66
|
+
if @session_sync_nonce && session_nonce_mismatch?
|
67
|
+
if session_nonce_cookie.to_s.empty?
|
68
|
+
destroy_session
|
69
|
+
redirect to(request.url)
|
70
|
+
else
|
71
|
+
store_write(:return_to, request.url)
|
72
|
+
redirect to('/auth/heroku')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
48
76
|
if store_read(:user)
|
49
77
|
expose_store
|
50
|
-
elsif !
|
78
|
+
elsif !anonymous_request_allowed?
|
51
79
|
store_write(:return_to, request.url)
|
52
80
|
redirect to('/auth/heroku')
|
53
81
|
end
|
@@ -67,7 +95,7 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
|
|
67
95
|
else
|
68
96
|
store_write(:user, true)
|
69
97
|
end
|
70
|
-
|
98
|
+
store_write(@session_sync_nonce.to_sym, session_nonce_cookie) if @session_sync_nonce
|
71
99
|
store_write(:token, token) if @expose_token
|
72
100
|
redirect to(store_delete(:return_to) || '/')
|
73
101
|
end
|
@@ -82,7 +110,12 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
|
|
82
110
|
get '/auth/sso-logout' do
|
83
111
|
destroy_session
|
84
112
|
auth_url = ENV["HEROKU_AUTH_URL"] || "https://id.heroku.com"
|
85
|
-
|
113
|
+
logout_url = "#{auth_url}/logout"
|
114
|
+
|
115
|
+
# id.heroku.com whitelists this return_to param, as any auth provider should do
|
116
|
+
logout_url += "?url=#{params['return_to']}" if params['return_to']
|
117
|
+
|
118
|
+
redirect to(logout_url)
|
86
119
|
end
|
87
120
|
|
88
121
|
# logout but only locally
|
@@ -91,6 +124,12 @@ class Heroku::Bouncer::Middleware < Sinatra::Base
|
|
91
124
|
redirect to("/")
|
92
125
|
end
|
93
126
|
|
127
|
+
# login, setting the URL to return to
|
128
|
+
get '/auth/login' do
|
129
|
+
store_write(:return_to, params['return_to'])
|
130
|
+
redirect to('/auth/heroku')
|
131
|
+
end
|
132
|
+
|
94
133
|
private
|
95
134
|
|
96
135
|
def extract_option(options, option, default = nil)
|
@@ -132,7 +171,7 @@ private
|
|
132
171
|
end
|
133
172
|
|
134
173
|
def destroy_session
|
135
|
-
|
174
|
+
store.keys.each { |k| store_delete(k) }
|
136
175
|
end
|
137
176
|
|
138
177
|
def expose_store
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heroku-bouncer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.0.
|
4
|
+
version: 0.4.0.pre3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Dance
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: omniauth-heroku
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: minitest
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,48 @@ dependencies:
|
|
80
94
|
- - ~>
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '5.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest-spec-context
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rack-test
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mocha
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
83
139
|
description: ID please.
|
84
140
|
email:
|
85
141
|
- jd@heroku.com
|
@@ -88,12 +144,12 @@ extensions: []
|
|
88
144
|
extra_rdoc_files:
|
89
145
|
- README.md
|
90
146
|
files:
|
91
|
-
- lib/heroku/bouncer.rb
|
92
|
-
- lib/heroku/bouncer/decrypted_hash.rb
|
93
147
|
- lib/heroku/bouncer/lockbox.rb
|
94
|
-
- lib/heroku/bouncer/
|
148
|
+
- lib/heroku/bouncer/decrypted_hash.rb
|
95
149
|
- lib/heroku/bouncer/builder.rb
|
150
|
+
- lib/heroku/bouncer/json_parser.rb
|
96
151
|
- lib/heroku/bouncer/middleware.rb
|
152
|
+
- lib/heroku/bouncer.rb
|
97
153
|
- README.md
|
98
154
|
- Gemfile
|
99
155
|
- Gemfile.lock
|
@@ -118,10 +174,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
174
|
version: 1.3.1
|
119
175
|
requirements: []
|
120
176
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.0.
|
177
|
+
rubygems_version: 2.0.14
|
122
178
|
signing_key:
|
123
179
|
specification_version: 4
|
124
|
-
summary:
|
180
|
+
summary: Rapidly add Heroku OAuth to your Ruby app.
|
125
181
|
test_files:
|
126
182
|
- Gemfile
|
127
183
|
- Gemfile.lock
|