omniauth-heroku 0.2.0.pre → 1.0.0
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 +6 -14
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +17 -17
- data/.rspec +3 -1
- data/CHANGELOG.md +43 -0
- data/Gemfile +0 -9
- data/Gemfile-omniauth1 +5 -0
- data/Gemfile-omniauth2 +5 -0
- data/LICENSE +22 -0
- data/README.md +102 -29
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/lib/omniauth-heroku.rb +2 -1
- data/lib/omniauth/heroku/version.rb +7 -0
- data/lib/omniauth/strategies/heroku.rb +56 -19
- data/omniauth-heroku.gemspec +36 -13
- metadata +101 -28
- data/.travis.yml +0 -14
- data/spec/omniauth_heroku_spec.rb +0 -78
- data/spec/spec_helper.rb +0 -48
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
MDJkMTc0MTBiNTIzZGU4ZDI4Y2YwYWRkOGEwNDJkNWFlYTczNzJiNzJmODRk
|
10
|
-
YjJjNmNiZTk5NjU0MTNhNWE4ZDcyNTAyZGQwNWJjMGU5NTIzNjI0ZDIwYjVk
|
11
|
-
YmRjODNlZGZlZDY5YjU1OGQ0ZTAzYWMxMTk2NGNlMGM3Njg4MTU=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
NDFiYjc5NzNmMzE0MWZhYWFjYTdlZWE5MDdlM2ViOGU5MDNkNGFiZThjYmQz
|
14
|
-
NTQ1NjczYzg2MDQ1MjMxNzU0OGIzNGNkMDUzMTMyZDI0ZWI0NGViOWFlNzJi
|
15
|
-
YzI2NWNiNTViMTAwYzU1ZDBmMzRiYTE3YTc2MzNlMWYyNTQ4MzE=
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5e748c5250cbb5edbba46f40b0f38d4a8cf54cd5c266b6df4deaebc57637e9d1
|
4
|
+
data.tar.gz: 4a5af940242e0cfb0a51ba7d73983303264846bb9278839cabadaaa890cf0227
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a623e6356f151b83eb7e370f8039e51012c9a76adf17187fe279aff23c10744b03514c46195198fd6261b9bcc206639ab7a15ea48875f6c06b300e3d8c72e45c
|
7
|
+
data.tar.gz: 61a1c1b3114cbf737de9de020572c98db82d3c29a180e0b135f3188ec6938123dc29766685475eff28e833d4d58ae600435411392b0a84579396757edccf5e19
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
ruby: [ 2.3, 2.4, 2.5, 2.6, 2.7, 3.0 ]
|
13
|
+
gemfile: ["omniauth1", "omniauth2"]
|
14
|
+
|
15
|
+
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
|
16
|
+
BUNDLE_GEMFILE: Gemfile-${{ matrix.gemfile }}
|
17
|
+
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v2
|
20
|
+
|
21
|
+
- name: Setup Ruby
|
22
|
+
uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
26
|
+
|
27
|
+
- name: Build and test
|
28
|
+
run: bin/rspec
|
29
|
+
|
data/.gitignore
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
/pkg
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
1
|
+
# Ignore all logfiles and tempfiles
|
2
|
+
/coverage/
|
3
|
+
/log/
|
4
|
+
/spec/reports
|
5
|
+
/spec/rspec-status.txt
|
6
|
+
/tmp/
|
7
|
+
/tags
|
8
|
+
|
9
|
+
# Package and dependency caches
|
10
|
+
/.bundle
|
11
|
+
/Gemfile.lock
|
12
|
+
/pkg/
|
13
|
+
|
14
|
+
# Generated documentation
|
15
|
+
/doc/
|
16
|
+
/.yardoc
|
17
|
+
/_yardoc/
|
data/.rspec
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
5
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## [1.0.0] 2021-07-14
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- Support `omniauth` versions `>= 1.9` but `< 3`.
|
14
|
+
i.e., support version `2` which addresses some CVEs.
|
15
|
+
- Standardize syntax and style via [Standard.rb](https://github.com/testdouble/standard)
|
16
|
+
|
17
|
+
### Breaking
|
18
|
+
|
19
|
+
- Loosen `omniauth-oauth2` requirement to allow `>= 1.7.0`.
|
20
|
+
With this change, blocks give to dynamically determine the `:scope` argument will be passed the Rack `env`, rather than an instance of the `Rack::Request`.
|
21
|
+
See the [Upgrading to 1.0 docs](README.md#upgrading-to-10) for more.
|
22
|
+
- Remove `AuthUrl` and `ApiUrl` constants from `OmniAuth::Strategies::Heroku`.
|
23
|
+
These were internal details, not meant to be part of the public API.
|
24
|
+
- Require Ruby `>= 2.3.0`.
|
25
|
+
We were only supporting that anyway, but now it's explicit.
|
26
|
+
However, we do recommend only running on [actively supported Rubies](https://www.ruby-lang.org/en/downloads/branches/).
|
27
|
+
|
28
|
+
## [0.4.1] 2021-07-06
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
- Lock to `omniauth-oauth2 ~> 1.6.0` to fix regression in dynamic `:scope` option.
|
32
|
+
With `omniauth-oauth2 >= 1.7.0`, the block is passed the Rack `env` as the parameter.
|
33
|
+
This breaks our expectation the will receive a `Rack::Request` instance as the argument to dynamically determine the `:scope` option.
|
34
|
+
i.e., this broken:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
use OmniAuth::Builder do
|
38
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET"),
|
39
|
+
scope: ->(request) { request.params["scope"] || "identity" }
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
See [PR #22](https://github.com/heroku/omniauth-heroku/pull/22) for more context, workaround, etc...
|
data/Gemfile
CHANGED
data/Gemfile-omniauth1
ADDED
data/Gemfile-omniauth2
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Pedro Belo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
# OmniAuth Heroku
|
2
2
|
|
3
|
-
[
|
3
|
+
[](https://github.com/heroku/omniauth-heroku/actions)
|
4
|
+
[](https://badge.fury.io/rb/omniauth-heroku)
|
4
5
|
|
5
|
-
[
|
6
|
+
[OmniAuth](https://github.com/intridea/omniauth) strategy for authenticating
|
7
|
+
Heroku users.
|
6
8
|
|
7
|
-
Mount this with your Rack application (be it Rails or Sinatra) to simplify the
|
9
|
+
Mount this with your Rack application (be it Rails or Sinatra) to simplify the
|
10
|
+
[OAuth flow with Heroku](https://devcenter.heroku.com/articles/oauth).
|
8
11
|
|
9
|
-
This is intended for apps already using OmniAuth, for apps that authenticate
|
12
|
+
This is intended for apps already using OmniAuth, for apps that authenticate
|
13
|
+
against more than one service (eg: Heroku and GitHub), or apps that have
|
14
|
+
specific needs on session management. If your app doesn't fall in any of these
|
15
|
+
you should consider using [Heroku Bouncer][heroku-bouncer] instead.
|
16
|
+
|
17
|
+
[heroku-bouncer]: https://github.com/heroku/heroku-bouncer
|
10
18
|
|
11
19
|
|
12
20
|
## Configuration
|
@@ -15,67 +23,117 @@ OmniAuth works as a Rack middleware. Mount this Heroku adapter with:
|
|
15
23
|
|
16
24
|
```ruby
|
17
25
|
use OmniAuth::Builder do
|
18
|
-
provider :heroku, ENV
|
26
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET")
|
19
27
|
end
|
20
28
|
```
|
21
29
|
|
22
|
-
Obtain a `HEROKU_OAUTH_ID` and `HEROKU_OAUTH_SECRET` by creating a client with
|
30
|
+
Obtain a `HEROKU_OAUTH_ID` and `HEROKU_OAUTH_SECRET` by creating a client with
|
31
|
+
the [Heroku OAuth CLI plugin](https://github.com/heroku/heroku-oauth).
|
23
32
|
|
24
|
-
Your Heroku OAuth client should be set to receive callbacks on
|
33
|
+
Your Heroku OAuth client should be set to receive callbacks on
|
34
|
+
`/auth/heroku/callback`.
|
25
35
|
|
26
36
|
|
27
37
|
## Usage
|
28
38
|
|
29
39
|
Initiate the OAuth flow sending users to `/auth/heroku`.
|
30
40
|
|
31
|
-
Once the authorization flow is complete and the user is bounced back to your
|
41
|
+
Once the authorization flow is complete and the user is bounced back to your
|
42
|
+
application, check `env["omniauth.auth"]["credentials"]`. It contains both a
|
43
|
+
refresh token and an access token (identified just as `"token"`) to the
|
44
|
+
account.
|
45
|
+
|
46
|
+
We recommend using this access token together with
|
47
|
+
the [Heroku Platform API gem][heroku-ruby-client] to make API calls on behalf of the user.
|
32
48
|
|
33
|
-
|
49
|
+
[heroku-ruby-client]: https://github.com/heroku/platform-api
|
34
50
|
|
35
51
|
Refer to the examples below to see how these work.
|
36
52
|
|
37
53
|
|
38
54
|
### Basic account information
|
39
55
|
|
40
|
-
If you want this middleware to fetch additional Heroku account information like
|
56
|
+
If you want this middleware to fetch additional Heroku account information like
|
57
|
+
the user email address and name, use the `fetch_info` option, like:
|
41
58
|
|
42
59
|
```ruby
|
43
60
|
use OmniAuth::Builder do
|
44
|
-
provider :heroku, ENV
|
61
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET"),
|
45
62
|
fetch_info: true
|
46
63
|
end
|
47
64
|
```
|
48
65
|
|
49
|
-
This sets name and email in the [omniauth auth hash]
|
66
|
+
This sets name and email in the [omniauth auth hash][auth-hash]. You can access
|
67
|
+
it from your app via `env["omniauth.auth"]["info"]`.
|
68
|
+
|
69
|
+
[auth-hash]: https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
|
70
|
+
|
71
|
+
It will also add [additional Heroku account info][platform-api] to
|
72
|
+
`env["omniauth.auth"]["extra"]`.
|
50
73
|
|
51
|
-
|
74
|
+
[platform-api]: https://devcenter.heroku.com/articles/platform-api-reference#account
|
52
75
|
|
53
76
|
### OAuth scopes
|
54
77
|
|
55
|
-
[Heroku supports different OAuth scopes]
|
78
|
+
[Heroku supports different OAuth scopes][oauth-scopes]. By default this
|
79
|
+
strategy will request global access to the account, but you're encouraged to
|
80
|
+
request for less permissions when possible.
|
81
|
+
|
82
|
+
[oauth-scopes]: https://devcenter.heroku.com/articles/oauth#scopes
|
56
83
|
|
57
84
|
To do so, configure it like:
|
58
85
|
|
59
86
|
```ruby
|
60
87
|
use OmniAuth::Builder do
|
61
|
-
provider :heroku, ENV
|
88
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET"),
|
62
89
|
scope: "identity"
|
63
90
|
end
|
64
91
|
```
|
65
92
|
|
66
93
|
This will trim down the permissions associated to the access token given back to you.
|
67
94
|
|
95
|
+
The OAuth scope can also be decided dynamically at runtime.
|
96
|
+
For example, you could use a `scope` parameter from the request, if it exists, with a default value if it does not:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
use OmniAuth::Builder do
|
100
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET"),
|
101
|
+
scope: ->(env) { Rack::Request.new(env).params["scope"] || "identity" }
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
#### Upgrading to 1.0+
|
106
|
+
|
107
|
+
Versions before `1.0` allowed you to pass `:scope` option as a block, just as today.
|
108
|
+
However, that block received a `Rack::Request` instance, rather than the `Rack` `env`.
|
109
|
+
If you used the `Rack::Request` argument in your `:scope` block you can update your code to create a new instance, from the `env`.
|
110
|
+
|
111
|
+
For example, `< 1.0` code that looked like this:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
use OmniAuth::Builder do
|
115
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET"),
|
116
|
+
scope: ->(request) { request.params["scope"] }
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
need to be updated to something like this:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
use OmniAuth::Builder do
|
124
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET"),
|
125
|
+
scope: ->(env) { Rack::Request.new(env).params["scope"] }
|
126
|
+
end
|
127
|
+
```
|
68
128
|
|
69
129
|
## Example - Sinatra
|
70
130
|
|
71
131
|
```ruby
|
72
132
|
class Myapp < Sinatra::Application
|
73
|
-
|
74
|
-
enable :sessions
|
75
|
-
end
|
133
|
+
use Rack::Session::Cookie, secret: ENV.fetch("SESSION_SECRET")
|
76
134
|
|
77
135
|
use OmniAuth::Builder do
|
78
|
-
provider :heroku, ENV
|
136
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET")
|
79
137
|
end
|
80
138
|
|
81
139
|
get "/" do
|
@@ -83,20 +141,28 @@ class Myapp < Sinatra::Application
|
|
83
141
|
end
|
84
142
|
|
85
143
|
get "/auth/heroku/callback" do
|
86
|
-
access_token = env[
|
87
|
-
|
88
|
-
"
|
144
|
+
access_token = env["omniauth.auth"]["credentials"]["token"]
|
145
|
+
# DO NOT store this token in an unencrypted cookie session
|
146
|
+
# Please read "A note on security" below!
|
147
|
+
heroku = PlatformAPI.connect_oauth(access_token)
|
148
|
+
"You have #{heroku.app.list.count} apps"
|
89
149
|
end
|
90
150
|
end
|
91
151
|
```
|
92
152
|
|
153
|
+
Note that we're explicitly calling `Rack::Session::Cookie` with a secret. Using
|
154
|
+
`enable :sessions` is not recommended because the secret is generated randomly,
|
155
|
+
and not reused across processes – so your users can lose their session whenever
|
156
|
+
your app restarts.
|
157
|
+
|
158
|
+
|
93
159
|
## Example - Rails
|
94
160
|
|
95
161
|
Under `config/initializers/omniauth.rb`:
|
96
162
|
|
97
163
|
```ruby
|
98
164
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
99
|
-
provider :heroku, ENV
|
165
|
+
provider :heroku, ENV.fetch("HEROKU_OAUTH_ID"), ENV.fetch("HEROKU_OAUTH_SECRET")
|
100
166
|
end
|
101
167
|
```
|
102
168
|
|
@@ -118,9 +184,11 @@ class SessionsController < ApplicationController
|
|
118
184
|
end
|
119
185
|
|
120
186
|
def create
|
121
|
-
access_token = request.env[
|
122
|
-
|
123
|
-
|
187
|
+
access_token = request.env["omniauth.auth"]["credentials"]["token"]
|
188
|
+
# DO NOT store this token in an unencrypted cookie session
|
189
|
+
# Please read "A note on security" below!
|
190
|
+
heroku = PlatformAPI.connect_oauth(access_token)
|
191
|
+
@apps = heroku.app.list
|
124
192
|
end
|
125
193
|
end
|
126
194
|
```
|
@@ -139,11 +207,16 @@ And view:
|
|
139
207
|
|
140
208
|
## A note on security
|
141
209
|
|
142
|
-
|
210
|
+
**Make sure your cookie session is encrypted before storing sensitive
|
211
|
+
information on it, like access tokens**. [encrypted_cookie][encrypted-cookie]
|
212
|
+
is a popular gem to do that in Ruby.
|
143
213
|
|
144
|
-
|
214
|
+
[encrypted-cookie]: https://github.com/cvonkleist/encrypted_cookie
|
145
215
|
|
146
|
-
Rails
|
216
|
+
Both Rails and Sinatra take a cookie secret, but that is only used to protect
|
217
|
+
against tampering; any information stored on standard cookie sessions can
|
218
|
+
easily be read from the client side, which can be further exploited to leak
|
219
|
+
credentials off your app.
|
147
220
|
|
148
221
|
|
149
222
|
## Meta
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/lib/omniauth-heroku.rb
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
require
|
1
|
+
require "omniauth/heroku/version"
|
2
|
+
require "omniauth/strategies/heroku"
|
@@ -1,20 +1,25 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "omniauth-oauth2"
|
2
4
|
|
3
5
|
module OmniAuth
|
4
6
|
module Strategies
|
5
7
|
class Heroku < OmniAuth::Strategies::OAuth2
|
6
|
-
|
7
|
-
|
8
|
+
# This style of overriding the default means it can only be done when the class is loaded.
|
9
|
+
# Which is problematic for testing, and a bit against the grain of how the base class
|
10
|
+
# expects this to work, where consumers would explicitly override by passing in the option.
|
11
|
+
AUTH_URL = ENV.fetch("HEROKU_AUTH_URL", "https://id.heroku.com")
|
12
|
+
private_constant :AUTH_URL
|
8
13
|
|
9
|
-
option
|
10
|
-
site:
|
11
|
-
authorize_url: "#{
|
12
|
-
token_url:
|
13
|
-
}
|
14
|
+
option(:client_options, {
|
15
|
+
site: AUTH_URL,
|
16
|
+
authorize_url: "#{AUTH_URL}/oauth/authorize",
|
17
|
+
token_url: "#{AUTH_URL}/oauth/token"
|
18
|
+
})
|
14
19
|
|
15
|
-
# whether we
|
20
|
+
# Configure whether we make another API call to Heroku to fetch
|
16
21
|
# additional account info like the real user name and email
|
17
|
-
option :fetch_info
|
22
|
+
option :fetch_info, false
|
18
23
|
|
19
24
|
uid do
|
20
25
|
access_token.params["user_id"]
|
@@ -22,17 +27,16 @@ module OmniAuth
|
|
22
27
|
|
23
28
|
info do
|
24
29
|
if options.fetch_info
|
25
|
-
email_hash
|
26
|
-
|
27
|
-
image_url = "https://secure.gravatar.com/avatar/#{email_hash}.png?d=#{default_image_url}"
|
30
|
+
email_hash = Digest::MD5.hexdigest(account_info["email"].to_s)
|
31
|
+
image_url = "https://secure.gravatar.com/avatar/#{email_hash}.png?d=#{DEFAULT_IMAGE_URL}"
|
28
32
|
|
29
33
|
{
|
30
|
-
name:
|
34
|
+
name: account_info["name"],
|
31
35
|
email: account_info["email"],
|
32
|
-
image: image_url
|
36
|
+
image: image_url
|
33
37
|
}
|
34
38
|
else
|
35
|
-
{
|
39
|
+
{name: "Heroku user"} # only mandatory field
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
@@ -44,17 +48,50 @@ module OmniAuth
|
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
51
|
+
# override method in OmniAuth::Strategies::OAuth2 to error
|
52
|
+
# when we don't have a client_id or secret:
|
53
|
+
def request_phase
|
54
|
+
if missing_client_id?
|
55
|
+
fail!(:missing_client_id)
|
56
|
+
elsif missing_client_secret?
|
57
|
+
fail!(:missing_client_secret)
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
DEFAULT_API_URL = "https://api.heroku.com"
|
66
|
+
private_constant :DEFAULT_API_URL
|
67
|
+
|
68
|
+
DEFAULT_IMAGE_URL = "https://dashboard.heroku.com/ninja-avatar-48x48.png"
|
69
|
+
private_constant :DEFAULT_IMAGE_URL
|
70
|
+
|
47
71
|
def account_info
|
48
72
|
@account_info ||= MultiJson.decode(heroku_api.get("/account").body)
|
49
73
|
end
|
50
74
|
|
75
|
+
def api_url
|
76
|
+
@api_url ||= ENV.fetch("HEROKU_API_URL", DEFAULT_API_URL)
|
77
|
+
end
|
78
|
+
|
51
79
|
def heroku_api
|
52
80
|
@heroku_api ||= Faraday.new(
|
53
|
-
url:
|
81
|
+
url: api_url,
|
54
82
|
headers: {
|
55
83
|
"Accept" => "application/vnd.heroku+json; version=3",
|
56
|
-
"Authorization" => "Bearer #{access_token.token}"
|
57
|
-
}
|
84
|
+
"Authorization" => "Bearer #{access_token.token}"
|
85
|
+
}
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def missing_client_id?
|
90
|
+
[nil, ""].include?(options.client_id)
|
91
|
+
end
|
92
|
+
|
93
|
+
def missing_client_secret?
|
94
|
+
[nil, ""].include?(options.client_secret)
|
58
95
|
end
|
59
96
|
end
|
60
97
|
end
|
data/omniauth-heroku.gemspec
CHANGED
@@ -1,16 +1,39 @@
|
|
1
|
-
|
2
|
-
gem.authors = ["Pedro Belo"]
|
3
|
-
gem.email = ["pedro@heroku.com"]
|
4
|
-
gem.description = %q{OmniAuth strategy for Heroku.}
|
5
|
-
gem.summary = %q{OmniAuth strategy for Heroku.}
|
6
|
-
gem.homepage = "https://github.com/heroku/omniauth-heroku"
|
1
|
+
require File.expand_path("lib/omniauth/heroku/version", __dir__)
|
7
2
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "omniauth-heroku"
|
5
|
+
spec.version = OmniAuth::Heroku::VERSION
|
6
|
+
spec.authors = ["Pedro Belo"]
|
7
|
+
spec.email = ["pedro@heroku.com"]
|
13
8
|
|
14
|
-
|
15
|
-
|
9
|
+
spec.summary = "OmniAuth strategy for Heroku."
|
10
|
+
spec.description = "OmniAuth strategy for Heroku, for apps already using OmniAuth that authenticate against more than one service (eg: Heroku and GitHub), or apps that have specific needs on session management."
|
11
|
+
spec.homepage = "https://github.com/heroku/omniauth-heroku"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.metadata = {
|
15
|
+
"homepage_uri" => spec.homepage,
|
16
|
+
"source_code_uri" => spec.homepage,
|
17
|
+
"bug_tracker_uri" => "#{spec.homepage}/issues",
|
18
|
+
"changelog_uri" => "#{spec.homepage}/blob/master/CHANGELOG.md"
|
19
|
+
}
|
20
|
+
|
21
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = []
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_runtime_dependency("omniauth", [">= 1.9", "< 3"])
|
33
|
+
spec.add_runtime_dependency("omniauth-oauth2", "~> 1.7")
|
34
|
+
|
35
|
+
spec.add_development_dependency("multi_json", "~> 1.12")
|
36
|
+
spec.add_development_dependency("rake", "~> 13.0")
|
37
|
+
spec.add_development_dependency("rspec", "~> 3.4")
|
38
|
+
spec.add_development_dependency("webmock", "~> 3.13")
|
16
39
|
end
|
metadata
CHANGED
@@ -1,82 +1,155 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-heroku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pedro Belo
|
8
|
-
autorequire:
|
9
|
-
bindir:
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: omniauth
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.9'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.9'
|
30
|
+
- - "<"
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
32
|
+
version: '3'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: omniauth-oauth2
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
|
-
- - ~>
|
37
|
+
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
39
|
+
version: '1.7'
|
34
40
|
type: :runtime
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
38
|
-
- - ~>
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: multi_json
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.12'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.12'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rake
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '13.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
39
73
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
|
74
|
+
version: '13.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.4'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.4'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: webmock
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.13'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.13'
|
103
|
+
description: 'OmniAuth strategy for Heroku, for apps already using OmniAuth that authenticate
|
104
|
+
against more than one service (eg: Heroku and GitHub), or apps that have specific
|
105
|
+
needs on session management.'
|
42
106
|
email:
|
43
107
|
- pedro@heroku.com
|
44
108
|
executables: []
|
45
109
|
extensions: []
|
46
110
|
extra_rdoc_files: []
|
47
111
|
files:
|
48
|
-
- .
|
49
|
-
- .
|
50
|
-
- .
|
112
|
+
- ".github/workflows/ci.yml"
|
113
|
+
- ".gitignore"
|
114
|
+
- ".rspec"
|
115
|
+
- CHANGELOG.md
|
51
116
|
- Gemfile
|
117
|
+
- Gemfile-omniauth1
|
118
|
+
- Gemfile-omniauth2
|
119
|
+
- LICENSE
|
52
120
|
- README.md
|
53
121
|
- Rakefile
|
122
|
+
- bin/rake
|
123
|
+
- bin/rspec
|
54
124
|
- lib/omniauth-heroku.rb
|
125
|
+
- lib/omniauth/heroku/version.rb
|
55
126
|
- lib/omniauth/strategies/heroku.rb
|
56
127
|
- omniauth-heroku.gemspec
|
57
|
-
- spec/omniauth_heroku_spec.rb
|
58
|
-
- spec/spec_helper.rb
|
59
128
|
homepage: https://github.com/heroku/omniauth-heroku
|
60
|
-
licenses:
|
61
|
-
|
62
|
-
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata:
|
132
|
+
homepage_uri: https://github.com/heroku/omniauth-heroku
|
133
|
+
source_code_uri: https://github.com/heroku/omniauth-heroku
|
134
|
+
bug_tracker_uri: https://github.com/heroku/omniauth-heroku/issues
|
135
|
+
changelog_uri: https://github.com/heroku/omniauth-heroku/blob/master/CHANGELOG.md
|
136
|
+
post_install_message:
|
63
137
|
rdoc_options: []
|
64
138
|
require_paths:
|
65
139
|
- lib
|
66
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
67
141
|
requirements:
|
68
|
-
- -
|
142
|
+
- - ">="
|
69
143
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
144
|
+
version: 2.3.0
|
71
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
146
|
requirements:
|
73
|
-
- -
|
147
|
+
- - ">="
|
74
148
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
149
|
+
version: '0'
|
76
150
|
requirements: []
|
77
|
-
|
78
|
-
|
79
|
-
signing_key:
|
151
|
+
rubygems_version: 3.2.14
|
152
|
+
signing_key:
|
80
153
|
specification_version: 4
|
81
154
|
summary: OmniAuth strategy for Heroku.
|
82
155
|
test_files: []
|
data/.travis.yml
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
rvm:
|
3
|
-
- 2.1.2
|
4
|
-
- 2.1.0
|
5
|
-
- 1.9.3
|
6
|
-
cache: bundler
|
7
|
-
notifications:
|
8
|
-
hipchat:
|
9
|
-
rooms:
|
10
|
-
- 5bc7785d2feb4f25901124279daede@API
|
11
|
-
template:
|
12
|
-
- '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} (<a href="%{build_url}">Details</a> | <a href="%{compare_url}">Change view</a>)'
|
13
|
-
format: html
|
14
|
-
script: bundle exec rake
|
@@ -1,78 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe OmniAuth::Strategies::Heroku do
|
4
|
-
before do
|
5
|
-
@token = "6e441b93-4c6d-4613-abed-b9976e7cff6c"
|
6
|
-
@user_id = "ddc4beff-f08f-4856-99d2-ba5ac63c3eb9"
|
7
|
-
|
8
|
-
# stub the API call made by the strategy to start the oauth dance
|
9
|
-
stub_request(:post, "https://id.heroku.com/oauth/token").
|
10
|
-
to_return(
|
11
|
-
headers: { "Content-Type" => "application/json" },
|
12
|
-
body: MultiJson.encode(
|
13
|
-
access_token: @token,
|
14
|
-
expires_in: 3600,
|
15
|
-
user_id: @user_id))
|
16
|
-
end
|
17
|
-
|
18
|
-
it "redirects to start the OAuth flow" do
|
19
|
-
get "/auth/heroku"
|
20
|
-
assert_equal 302, last_response.status
|
21
|
-
redirect = URI.parse(last_response.headers["Location"])
|
22
|
-
redirect_params = CGI::parse(redirect.query)
|
23
|
-
assert_equal "https", redirect.scheme
|
24
|
-
assert_equal "id.heroku.com", redirect.host
|
25
|
-
assert_equal [ENV["HEROKU_OAUTH_ID"]], redirect_params["client_id"]
|
26
|
-
assert_equal ["code"], redirect_params["response_type"]
|
27
|
-
assert_equal ["http://example.org/auth/heroku/callback"],
|
28
|
-
redirect_params["redirect_uri"]
|
29
|
-
end
|
30
|
-
|
31
|
-
it "receives the callback" do
|
32
|
-
# start the callback, get the session state
|
33
|
-
get "/auth/heroku"
|
34
|
-
assert_equal 302, last_response.status
|
35
|
-
state = last_response.headers["Location"].match(/state=([\w\d]+)/)[1]
|
36
|
-
|
37
|
-
# trigger the callback setting the state as a param and in the session
|
38
|
-
get "/auth/heroku/callback", { "state" => state },
|
39
|
-
{ "rack.session" => { "omniauth.state" => state }}
|
40
|
-
assert_equal 200, last_response.status
|
41
|
-
|
42
|
-
omniauth_env = MultiJson.decode(last_response.body)
|
43
|
-
assert_equal "heroku", omniauth_env["provider"]
|
44
|
-
assert_equal @user_id, omniauth_env["uid"]
|
45
|
-
assert_equal "Heroku user", omniauth_env["info"]["name"]
|
46
|
-
end
|
47
|
-
|
48
|
-
it "fetches additional info when requested" do
|
49
|
-
# change the app being tested:
|
50
|
-
@app = make_app(fetch_info: true)
|
51
|
-
|
52
|
-
# stub the API call to heroku
|
53
|
-
account_info = {
|
54
|
-
"email" => "john@example.org",
|
55
|
-
"name" => "John"
|
56
|
-
}
|
57
|
-
stub_request(:get, "https://api.heroku.com/account").
|
58
|
-
with(headers: { "Authorization" => "Bearer #{@token}" }).
|
59
|
-
to_return(body: MultiJson.encode(account_info))
|
60
|
-
|
61
|
-
# do the oauth dance
|
62
|
-
get "/auth/heroku"
|
63
|
-
assert_equal 302, last_response.status
|
64
|
-
state = last_response.headers["Location"].match(/state=([\w\d]+)/)[1]
|
65
|
-
|
66
|
-
get "/auth/heroku/callback", { "state" => state },
|
67
|
-
{ "rack.session" => { "omniauth.state" => state }}
|
68
|
-
assert_equal 200, last_response.status
|
69
|
-
|
70
|
-
# now make sure there's additional info in the omniauth env
|
71
|
-
omniauth_env = MultiJson.decode(last_response.body)
|
72
|
-
assert_equal "heroku", omniauth_env["provider"]
|
73
|
-
assert_equal @user_id, omniauth_env["uid"]
|
74
|
-
assert_equal "john@example.org", omniauth_env["info"]["email"]
|
75
|
-
assert_equal "John", omniauth_env["info"]["name"]
|
76
|
-
assert_equal account_info, omniauth_env["extra"]
|
77
|
-
end
|
78
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
ENV["SESSION_SECRET"] = "abcdefghjij"
|
2
|
-
ENV["HEROKU_OAUTH_ID"] = "12345"
|
3
|
-
ENV["HEROKU_OAUTH_SECRET"] = "klmnopqrstu"
|
4
|
-
|
5
|
-
require "rubygems"
|
6
|
-
require "bundler"
|
7
|
-
Bundler.setup(:default, :test)
|
8
|
-
require "omniauth/strategies/heroku"
|
9
|
-
|
10
|
-
require "cgi"
|
11
|
-
require "rspec"
|
12
|
-
require "rack/test"
|
13
|
-
require "sinatra"
|
14
|
-
require "webmock/rspec"
|
15
|
-
|
16
|
-
Dir["./spec/support/*.rb"].each { |f| require f }
|
17
|
-
|
18
|
-
WebMock.disable_net_connect!
|
19
|
-
|
20
|
-
OmniAuth.config.logger = Logger.new(StringIO.new)
|
21
|
-
|
22
|
-
RSpec.configure do |config|
|
23
|
-
config.include Rack::Test::Methods
|
24
|
-
config.expect_with :minitest
|
25
|
-
|
26
|
-
def app
|
27
|
-
@app || make_app
|
28
|
-
end
|
29
|
-
|
30
|
-
def make_app(omniauth_heroku_options={})
|
31
|
-
Sinatra.new do
|
32
|
-
configure do
|
33
|
-
enable :sessions
|
34
|
-
set :show_exceptions, false
|
35
|
-
set :session_secret, ENV["SESSION_SECRET"]
|
36
|
-
end
|
37
|
-
|
38
|
-
use OmniAuth::Builder do
|
39
|
-
provider :heroku, ENV["HEROKU_OAUTH_ID"], ENV["HEROKU_OAUTH_SECRET"],
|
40
|
-
omniauth_heroku_options
|
41
|
-
end
|
42
|
-
|
43
|
-
get "/auth/heroku/callback" do
|
44
|
-
MultiJson.encode(env['omniauth.auth'])
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|