heroku-bouncer 0.4.0.pre2 → 0.4.0.pre3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](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
|