omniauth-heroku 0.2.0.pre → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://github.com/heroku/omniauth-heroku/actions/workflows/ci.yml/badge.svg)](https://github.com/heroku/omniauth-heroku/actions)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/omniauth-heroku.svg)](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
|