omniauth-heroku 0.1.2.pre → 0.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ M2ZlY2IwZjQ5NmNhN2QyMjRhODI0MGE4MmQ5ODFkNzIxZDZiNThlYg==
5
+ data.tar.gz: !binary |-
6
+ MDEyNDQ1MjUzMThjMzQzYjU4NDFmMjQzNjYxYzBkZDVlOTE0OGU4Mw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MDJkMTc0MTBiNTIzZGU4ZDI4Y2YwYWRkOGEwNDJkNWFlYTczNzJiNzJmODRk
10
+ YjJjNmNiZTk5NjU0MTNhNWE4ZDcyNTAyZGQwNWJjMGU5NTIzNjI0ZDIwYjVk
11
+ YmRjODNlZGZlZDY5YjU1OGQ0ZTAzYWMxMTk2NGNlMGM3Njg4MTU=
12
+ data.tar.gz: !binary |-
13
+ NDFiYjc5NzNmMzE0MWZhYWFjYTdlZWE5MDdlM2ViOGU5MDNkNGFiZThjYmQz
14
+ NTQ1NjczYzg2MDQ1MjMxNzU0OGIzNGNkMDUzMTMyZDI0ZWI0NGViOWFlNzJi
15
+ YzI2NWNiNTViMTAwYzU1ZDBmMzRiYTE3YTc2MzNlMWYyNTQ4MzE=
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
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
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://www.rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem "multi_json"
7
+ gem "rake"
8
+ gem "rack-test"
9
+ gem "rspec"
10
+ gem "sinatra"
11
+ gem "webmock"
12
+ end
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # OmniAuth Heroku
2
2
 
3
- [OmniAuth](https://github.com/intridea/omniauth) strategy for authenticating to Heroku.
3
+ [OmniAuth](https://github.com/intridea/omniauth) strategy for authenticating Heroku users.
4
+
5
+ [![Build Status](https://travis-ci.org/heroku/omniauth-heroku.svg?branch=master)](https://travis-ci.org/heroku/omniauth-heroku)
6
+
7
+ Mount this with your Rack application (be it Rails or Sinatra) to simplify the [OAuth flow with Heroku](https://devcenter.heroku.com/articles/oauth).
8
+
9
+ This is intended for apps already using OmniAuth, for apps that authenticate against more than one service (eg: Heroku and GitHub), or apps that have specific needs on session management. If your app doesn't fall in any of these you should consider using [Heroku Bouncer](https://github.com/heroku/heroku-bouncer) instead.
4
10
 
5
11
 
6
12
  ## Configuration
@@ -13,6 +19,8 @@ use OmniAuth::Builder do
13
19
  end
14
20
  ```
15
21
 
22
+ Obtain a `HEROKU_OAUTH_ID` and `HEROKU_OAUTH_SECRET` by creating a client with the [Heroku OAuth CLI plugin](https://github.com/heroku/heroku-oauth).
23
+
16
24
  Your Heroku OAuth client should be set to receive callbacks on `/auth/heroku/callback`.
17
25
 
18
26
 
@@ -24,6 +32,63 @@ Once the authorization flow is complete and the user is bounced back to your app
24
32
 
25
33
  We recommend using this access token together with [Heroku.rb](https://github.com/heroku/heroku.rb) to make API calls on behalf of the user.
26
34
 
35
+ Refer to the examples below to see how these work.
36
+
37
+
38
+ ### Basic account information
39
+
40
+ If you want this middleware to fetch additional Heroku account information like the user email address and name, use the `fetch_info` option, like:
41
+
42
+ ```ruby
43
+ use OmniAuth::Builder do
44
+ provider :heroku, ENV['HEROKU_OAUTH_ID'], ENV['HEROKU_OAUTH_SECRET'],
45
+ fetch_info: true
46
+ end
47
+ ```
48
+
49
+ This sets name and email in the [omniauth auth hash](https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema). You can access it from your app via `env["omniauth.auth"]["info"]`.
50
+
51
+ It will also add [additional Heroku account info](https://devcenter.heroku.com/articles/platform-api-reference#account) to `env["omniauth.auth"]["extra"]`.
52
+
53
+ ### OAuth scopes
54
+
55
+ [Heroku supports different OAuth scopes](https://devcenter.heroku.com/articles/oauth#scopes). By default this strategy will request global access to the account, but you're encouraged to request for less permissions when possible.
56
+
57
+ To do so, configure it like:
58
+
59
+ ```ruby
60
+ use OmniAuth::Builder do
61
+ provider :heroku, ENV['HEROKU_OAUTH_ID'], ENV['HEROKU_OAUTH_SECRET'],
62
+ scope: "identity"
63
+ end
64
+ ```
65
+
66
+ This will trim down the permissions associated to the access token given back to you.
67
+
68
+
69
+ ## Example - Sinatra
70
+
71
+ ```ruby
72
+ class Myapp < Sinatra::Application
73
+ configure do
74
+ enable :sessions
75
+ end
76
+
77
+ use OmniAuth::Builder do
78
+ provider :heroku, ENV["HEROKU_OAUTH_ID"], ENV["HEROKU_OAUTH_SECRET"]
79
+ end
80
+
81
+ get "/" do
82
+ redirect "/auth/heroku"
83
+ end
84
+
85
+ get "/auth/heroku/callback" do
86
+ access_token = env['omniauth.auth']['credentials']['token']
87
+ heroku_api = Heroku::API.new(api_key: access_token)
88
+ "You have #{heroku_api.get_apps.body.size} apps"
89
+ end
90
+ end
91
+ ```
27
92
 
28
93
  ## Example - Rails
29
94
 
@@ -54,8 +119,8 @@ class SessionsController < ApplicationController
54
119
 
55
120
  def create
56
121
  access_token = request.env['omniauth.auth']['credentials']['token']
57
- heroku_api = Heroku::API.new(:api_key => access_token)
58
- @apps = api.get_apps.body
122
+ heroku_api = Heroku::API.new(api_key: access_token)
123
+ @apps = heroku_api.get_apps.body
59
124
  end
60
125
  end
61
126
  ```
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ task default: :spec
6
+
7
+ desc "Run the specs"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.pattern = "spec/**/*_spec.rb"
10
+ end
@@ -1,13 +1,61 @@
1
+ require 'omniauth-oauth2'
2
+
1
3
  module OmniAuth
2
4
  module Strategies
3
- class Heroku < OmniAuth::Strategies::HerokuOAuth2
4
- BaseAuthUrl = ENV["HEROKU_AUTH_URL"] || "https://id.heroku.com"
5
+ class Heroku < OmniAuth::Strategies::OAuth2
6
+ AuthUrl = ENV["HEROKU_AUTH_URL"] || "https://id.heroku.com"
7
+ ApiUrl = ENV["HEROKU_API_URL"] || "https://api.heroku.com"
5
8
 
6
9
  option :client_options, {
7
- :site => BaseAuthUrl,
8
- :authorize_url => "#{BaseAuthUrl}/oauth/authorize",
9
- :token_url => "#{BaseAuthUrl}/oauth/token"
10
+ site: AuthUrl,
11
+ authorize_url: "#{AuthUrl}/oauth/authorize",
12
+ token_url: "#{AuthUrl}/oauth/token"
10
13
  }
14
+
15
+ # whether we should make another API call to Heroku to fetch
16
+ # additional account info like the real user name and email
17
+ option :fetch_info
18
+
19
+ uid do
20
+ access_token.params["user_id"]
21
+ end
22
+
23
+ info do
24
+ if options.fetch_info
25
+ email_hash = Digest::MD5.hexdigest(account_info['email'].to_s)
26
+ default_image_url = "https://dashboard.heroku.com/ninja-avatar-48x48.png"
27
+ image_url = "https://secure.gravatar.com/avatar/#{email_hash}.png?d=#{default_image_url}"
28
+
29
+ {
30
+ name: account_info["name"],
31
+ email: account_info["email"],
32
+ image: image_url,
33
+ }
34
+ else
35
+ { name: "Heroku user" } # only mandatory field
36
+ end
37
+ end
38
+
39
+ extra do
40
+ if options.fetch_info
41
+ account_info
42
+ else
43
+ {}
44
+ end
45
+ end
46
+
47
+ def account_info
48
+ @account_info ||= MultiJson.decode(heroku_api.get("/account").body)
49
+ end
50
+
51
+ def heroku_api
52
+ @heroku_api ||= Faraday.new(
53
+ url: ApiUrl,
54
+ headers: {
55
+ "Accept" => "application/vnd.heroku+json; version=3",
56
+ "Authorization" => "Bearer #{access_token.token}",
57
+ })
58
+ end
11
59
  end
12
60
  end
13
61
  end
@@ -1,2 +1 @@
1
- require 'omniauth/strategies/heroku_oauth2'
2
1
  require 'omniauth/strategies/heroku'
@@ -9,8 +9,8 @@ Gem::Specification.new do |gem|
9
9
  gem.files = `git ls-files`.split("\n")
10
10
  gem.name = "omniauth-heroku"
11
11
  gem.require_paths = ["lib"]
12
- gem.version = "0.1.2.pre"
12
+ gem.version = "0.2.0.pre"
13
13
 
14
- gem.add_dependency 'oauth2', '~> 0.8.0'
15
- gem.add_dependency 'omniauth', '~> 1.0'
14
+ gem.add_dependency 'omniauth', '~> 1.2'
15
+ gem.add_dependency 'omniauth-oauth2', '~> 1.2'
16
16
  end
@@ -0,0 +1,78 @@
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
@@ -0,0 +1,48 @@
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
metadata CHANGED
@@ -1,48 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniauth-heroku
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2.pre
5
- prerelease: 6
4
+ version: 0.2.0.pre
6
5
  platform: ruby
7
6
  authors:
8
7
  - Pedro Belo
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-08-23 00:00:00.000000000 Z
11
+ date: 2014-09-11 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: oauth2
14
+ name: omniauth
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
- version: 0.8.0
19
+ version: '1.2'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
29
- version: 0.8.0
26
+ version: '1.2'
30
27
  - !ruby/object:Gem::Dependency
31
- name: omniauth
28
+ name: omniauth-oauth2
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
37
- version: '1.0'
33
+ version: '1.2'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
45
- version: '1.0'
40
+ version: '1.2'
46
41
  description: OmniAuth strategy for Heroku.
47
42
  email:
48
43
  - pedro@heroku.com
@@ -52,34 +47,36 @@ extra_rdoc_files: []
52
47
  files:
53
48
  - .gitignore
54
49
  - .rspec
50
+ - .travis.yml
51
+ - Gemfile
55
52
  - README.md
56
53
  - Rakefile
57
54
  - lib/omniauth-heroku.rb
58
55
  - lib/omniauth/strategies/heroku.rb
59
- - lib/omniauth/strategies/heroku_oauth2.rb
60
56
  - omniauth-heroku.gemspec
57
+ - spec/omniauth_heroku_spec.rb
58
+ - spec/spec_helper.rb
61
59
  homepage: https://github.com/heroku/omniauth-heroku
62
60
  licenses: []
61
+ metadata: {}
63
62
  post_install_message:
64
63
  rdoc_options: []
65
64
  require_paths:
66
65
  - lib
67
66
  required_ruby_version: !ruby/object:Gem::Requirement
68
- none: false
69
67
  requirements:
70
68
  - - ! '>='
71
69
  - !ruby/object:Gem::Version
72
70
  version: '0'
73
71
  required_rubygems_version: !ruby/object:Gem::Requirement
74
- none: false
75
72
  requirements:
76
73
  - - ! '>'
77
74
  - !ruby/object:Gem::Version
78
75
  version: 1.3.1
79
76
  requirements: []
80
77
  rubyforge_project:
81
- rubygems_version: 1.8.24
78
+ rubygems_version: 2.0.7
82
79
  signing_key:
83
- specification_version: 3
80
+ specification_version: 4
84
81
  summary: OmniAuth strategy for Heroku.
85
82
  test_files: []
@@ -1,124 +0,0 @@
1
- require 'cgi'
2
- require 'uri'
3
- require 'oauth2'
4
- require 'omniauth'
5
- require 'timeout'
6
- require 'securerandom'
7
-
8
- module OmniAuth
9
- module Strategies
10
- # Vendored in from `omniauth-oauth2`, but with slight modifications to
11
- # allow request IDs to be passed through during the token phase so that the
12
- # OAuth 2 dance can be more easily debugged.
13
- class HerokuOAuth2
14
- include OmniAuth::Strategy
15
-
16
- args [:client_id, :client_secret]
17
-
18
- option :client_id, nil
19
- option :client_secret, nil
20
- option :client_options, {}
21
- option :authorize_params, {}
22
- option :authorize_options, [:scope]
23
- option :token_params, {}
24
- option :token_options, []
25
- option :provider_ignores_state, false
26
- option :request_id, lambda { |env| nil }
27
-
28
- attr_accessor :access_token
29
-
30
- def client
31
- ::OAuth2::Client.new(options.client_id, options.client_secret, deep_symbolize(options.client_options))
32
- end
33
-
34
- def callback_url
35
- full_host + script_name + callback_path
36
- end
37
-
38
- credentials do
39
- hash = {'token' => access_token.token}
40
- hash.merge!('refresh_token' => access_token.refresh_token) if access_token.expires? && access_token.refresh_token
41
- hash.merge!('expires_at' => access_token.expires_at) if access_token.expires?
42
- hash.merge!('expires' => access_token.expires?)
43
- hash
44
- end
45
-
46
- def request_phase
47
- redirect client.auth_code.authorize_url({:redirect_uri => callback_url}.merge(authorize_params))
48
- end
49
-
50
- def authorize_params
51
- options.authorize_params[:state] = SecureRandom.hex(24)
52
- params = options.authorize_params.merge(options.authorize_options.inject({}){|h,k| h[k.to_sym] = options[k] if options[k]; h})
53
- if OmniAuth.config.test_mode
54
- @env ||= {}
55
- @env['rack.session'] ||= {}
56
- end
57
- session['omniauth.state'] = params[:state]
58
- params
59
- end
60
-
61
- def token_params
62
- options.token_params.merge(options.token_options.inject({}){|h,k| h[k.to_sym] = options[k] if options[k]; h})
63
- end
64
-
65
- def callback_phase
66
- if request.params['error'] || request.params['error_reason']
67
- raise CallbackError.new(request.params['error'], request.params['error_description'] || request.params['error_reason'], request.params['error_uri'])
68
- end
69
- # if !options.provider_ignores_state && (request.params['state'].to_s.empty? || request.params['state'] != session.delete('omniauth.state'))
70
- # raise CallbackError.new(nil, :csrf_detected)
71
- # end
72
-
73
- self.access_token = build_access_token
74
- params = {}
75
- if request_id = options.request_id.call(@env)
76
- params.merge!(:headers => { "Request-Id" => request_id })
77
- end
78
- self.access_token = access_token.refresh!(params) if access_token.expired?
79
-
80
- super
81
- rescue ::OAuth2::Error, CallbackError => e
82
- fail!(:invalid_credentials, e)
83
- rescue ::MultiJson::DecodeError => e
84
- fail!(:invalid_response, e)
85
- rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
86
- fail!(:timeout, e)
87
- rescue ::SocketError => e
88
- fail!(:failed_to_connect, e)
89
- end
90
-
91
- protected
92
-
93
- def deep_symbolize(hash)
94
- hash.inject({}) do |h, (k,v)|
95
- h[k.to_sym] = v.is_a?(Hash) ? deep_symbolize(v) : v
96
- h
97
- end
98
- end
99
-
100
- def build_access_token
101
- verifier = request.params['code']
102
- params = {:redirect_uri => callback_url}.
103
- merge(token_params.to_hash(:symbolize_keys => true))
104
- # inject a request ID if one is available
105
- if request_id = options.request_id.call(@env)
106
- params.merge!(:headers => { "Request-Id" => request_id })
107
- end
108
- client.auth_code.get_token(verifier, params)
109
- end
110
-
111
- # An error that is indicated in the OAuth 2.0 callback.
112
- # This could be a `redirect_uri_mismatch` or other
113
- class CallbackError < StandardError
114
- attr_accessor :error, :error_reason, :error_uri
115
-
116
- def initialize(error, error_reason=nil, error_uri=nil)
117
- self.error = error
118
- self.error_reason = error_reason
119
- self.error_uri = error_uri
120
- end
121
- end
122
- end
123
- end
124
- end