omniauth-scalus-oauth2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 27b7808c4cf45ad0b7b7c83236a35260905b6e84
4
+ data.tar.gz: b42ffd12f573939198e782785e8f44d329fda7a6
5
+ SHA512:
6
+ metadata.gz: b50cd40f95d04eb2ed3ae24d8ee08fca743978764ffd2096ae17dbbaf56779e3167c14eda064450050a1d913cfb2797792e9d567e47e0a2b626055ad39976f31
7
+ data.tar.gz: 33cc5a0f50bb686d506c18812b793a2b6b2cae48b389f13af4dfcecc3c274dfc8d3b55549abb81f04be2a44b35178310698e955217e1b0d0ce4decdd57dfaf48
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg/*
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # OmniAuth Scalus
2
+
3
+ Scalus OAuth2 Strategy for OmniAuth 1.0.
4
+
5
+ ## Installing
6
+
7
+ Add to your `Gemfile`:
8
+
9
+ ```ruby
10
+ gem 'omniauth-scalus-oauth2'
11
+ ```
12
+
13
+ Then `bundle install`.
14
+
15
+ ## Usage
16
+
17
+ `OmniAuth::Strategies::Scalus` is simply a Rack middleware. Read [the OmniAuth 1.0 docs](https://github.com/intridea/omniauth) for detailed instructions.
18
+
19
+ Here's a quick example, adding the middleware to a Rails app in `config/initializers/omniauth.rb`:
20
+
21
+ ```ruby
22
+ Rails.application.config.middleware.use OmniAuth::Builder do
23
+ provider :scalus, ENV['SCALUS_API_KEY'], ENV['SCALUS_SHARED_SECRET']
24
+ end
25
+ ```
26
+
27
+ ## Configuring
28
+
29
+ You can configure the scope, which you pass in to the `provider` method via a `Hash`:
30
+
31
+ * `scope`: A comma-separated list of permissions you want to request from the user. See [the Scalus API docs](http://docs.scalus.com/api/tutorials/oauth) for a full list of available permissions.
32
+
33
+ For example, to request `read_products`, `read_orders` and `write_content` permissions and display the authentication page:
34
+
35
+ ```ruby
36
+ Rails.application.config.middleware.use OmniAuth::Builder do
37
+ provider :scalus, ENV['SCALUS_API_KEY'], ENV['SCALUS_SHARED_SECRET'], :scope => 'read_products,read_orders,write_content'
38
+ end
39
+ ```
40
+
41
+ ## Authentication Hash
42
+
43
+ Here's an example *Authentication Hash* available in `request.env['omniauth.auth']`:
44
+
45
+ ```ruby
46
+ {
47
+ :provider => 'scalus',
48
+ :uid => 'example.myscalus.com',
49
+ :credentials => {
50
+ :token => 'afasd923kjh0934kf', # OAuth 2.0 access_token, which you store and use to authenticate API requests
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## License
56
+
57
+ Copyright (c) 2015 by Scalus Inc
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
62
+
63
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.pattern = 'test/**/*_test.rb'
8
+ t.verbose = true
9
+ end
data/example/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gem 'rack', '~> 1.6'
4
+
5
+ gem 'sinatra', '~> 1.4'
6
+ gem 'omniauth-scalus-oauth2', :path => '../'
data/example/config.ru ADDED
@@ -0,0 +1,67 @@
1
+ require 'bundler/setup'
2
+ require 'sinatra/base'
3
+ require 'omniauth-scalus-oauth2'
4
+
5
+ SCOPE = 'read_products,read_orders,read_customers,write_shipping'
6
+ SCALUS_API_KEY = ENV['SCALUS_API_KEY']
7
+ SCALUS_SHARED_SECRET = ENV['SCALUS_SHARED_SECRET']
8
+
9
+ unless SCALUS_API_KEY && SCALUS_SHARED_SECRET
10
+ abort("SCALUS_API_KEY and SCALUS_SHARED_SECRET environment variables must be set")
11
+ end
12
+
13
+ class App < Sinatra::Base
14
+ get '/' do
15
+ <<-HTML
16
+ <html>
17
+ <head>
18
+ <title>Scalus Oauth2</title>
19
+ </head>
20
+ <body>
21
+ <form action="/auth/scalus" method="get">
22
+ <label for="shop">Enter your store's URL:</label>
23
+ <input type="text" name="shop" placeholder="your-shop-url.myscalus.com">
24
+ <button type="submit">Log In</button>
25
+ </form>
26
+ </body>
27
+ </html>
28
+ HTML
29
+ end
30
+
31
+ get '/auth/:provider/callback' do
32
+ <<-HTML
33
+ <html>
34
+ <head>
35
+ <title>Scalus Oauth2</title>
36
+ </head>
37
+ <body>
38
+ <h3>Authorized</h3>
39
+ <p>Shop: #{request.env['omniauth.auth'].uid}</p>
40
+ <p>Token: #{request.env['omniauth.auth']['credentials']['token']}</p>
41
+ </body>
42
+ </html>
43
+ HTML
44
+ end
45
+
46
+ get '/auth/failure' do
47
+ <<-HTML
48
+ <html>
49
+ <head>
50
+ <title>Scalus Oauth2</title>
51
+ </head>
52
+ <body>
53
+ <h3>Failed Authorization</h3>
54
+ <p>Message: #{params[:message]}</p>
55
+ </body>
56
+ </html>
57
+ HTML
58
+ end
59
+ end
60
+
61
+ use Rack::Session::Cookie, secret: SecureRandom.hex(64)
62
+
63
+ use OmniAuth::Builder do
64
+ provider :scalus, SCALUS_API_KEY, SCALUS_SHARED_SECRET, :scope => SCOPE
65
+ end
66
+
67
+ run App.new
@@ -0,0 +1,5 @@
1
+ module OmniAuth
2
+ module Scalus
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'omniauth/scalus/version'
2
+ require 'omniauth/strategies/scalus'
@@ -0,0 +1,90 @@
1
+ require 'omniauth/strategies/oauth2'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class Scalus < OmniAuth::Strategies::OAuth2
6
+ # Available scopes: content themes products customers orders script_tags shipping
7
+ # read_* or write_*
8
+ DEFAULT_SCOPE = 'read_products'
9
+ MINUTE = 60
10
+ CODE_EXPIRES_AFTER = 10 * MINUTE
11
+
12
+ option :client_options, {
13
+ :authorize_url => '/admin/oauth/authorize',
14
+ :token_url => '/admin/oauth/access_token'
15
+ }
16
+
17
+ option :callback_url
18
+ option :myscalus_domain, 'myscalus.com'
19
+
20
+ option :setup, proc { |env|
21
+ request = Rack::Request.new(env)
22
+ env['omniauth.strategy'].options[:client_options][:site] = "https://#{request.GET['shop']}"
23
+ }
24
+
25
+ uid { URI.parse(options[:client_options][:site]).host }
26
+
27
+ def valid_site?
28
+ !!(/\A(https|http)\:\/\/[a-zA-Z0-9][a-zA-Z0-9\-]*\.#{Regexp.quote(options[:myscalus_domain])}[\/]?\z/ =~ options[:client_options][:site])
29
+ end
30
+
31
+ def valid_signature?
32
+ return false unless request.POST.empty?
33
+
34
+ params = request.GET
35
+ signature = params['hmac']
36
+ timestamp = params['timestamp']
37
+ return false unless signature && timestamp
38
+
39
+ return false unless timestamp.to_i > Time.now.to_i - CODE_EXPIRES_AFTER
40
+
41
+ calculated_signature = self.class.hmac_sign(self.class.encoded_params_for_signature(params), options.client_secret)
42
+ Rack::Utils.secure_compare(calculated_signature, signature)
43
+ end
44
+
45
+ def self.encoded_params_for_signature(params)
46
+ params = params.dup
47
+ params.delete('hmac')
48
+ params.delete('signature') # deprecated signature
49
+ params.map{|k,v| "#{URI.escape(k.to_s, '&=%')}=#{URI.escape(v.to_s, '&%')}"}.sort.join('&')
50
+ end
51
+
52
+ def self.hmac_sign(encoded_params, secret)
53
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, encoded_params)
54
+ end
55
+
56
+ def fix_https
57
+ options[:client_options][:site].gsub!(/\Ahttp\:/, 'https:')
58
+ end
59
+
60
+ def setup_phase
61
+ super
62
+ fix_https
63
+ end
64
+
65
+ def request_phase
66
+ if valid_site?
67
+ super
68
+ else
69
+ fail!(:invalid_site)
70
+ end
71
+ end
72
+
73
+ def callback_phase
74
+ return fail!(:invalid_site) unless valid_site?
75
+ return fail!(:invalid_signature) unless valid_signature?
76
+ super
77
+ end
78
+
79
+ def authorize_params
80
+ super.tap do |params|
81
+ params[:scope] ||= DEFAULT_SCOPE
82
+ end
83
+ end
84
+
85
+ def callback_url
86
+ options[:callback_url] || full_host + script_name + callback_path
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1 @@
1
+ require 'omniauth/scalus'
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'omniauth/scalus/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'omniauth-scalus-oauth2'
7
+ s.version = OmniAuth::Scalus::VERSION
8
+ s.authors = ['David Henner']
9
+ s.email = ['drhenner@scalus.com']
10
+ s.summary = 'Scalus strategy for OmniAuth'
11
+ s.homepage = 'https://github.com/scalus/omniauth-scalus-oauth2'
12
+ s.license = 'MIT'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+
19
+ s.add_runtime_dependency 'omniauth-oauth2', '~> 1.2'
20
+
21
+ s.add_development_dependency 'minitest', '~> 5.6'
22
+ s.add_development_dependency 'fakeweb', '~> 1.3'
23
+ s.add_development_dependency 'rake'
24
+ end
@@ -0,0 +1 @@
1
+ # using the default shipit config
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+ require 'omniauth-scalus-oauth2'
3
+ require 'base64'
4
+
5
+ describe OmniAuth::Strategies::Scalus do
6
+ before :each do
7
+ @request = double('Request',
8
+ :env => { })
9
+ @request.stub(:params) { {} }
10
+ @request.stub(:cookies) { {} }
11
+
12
+ @client_id = '123'
13
+ @client_secret = '53cr3tz'
14
+ @options = {:client_options => {:site => 'https://example.myscalus.com'}}
15
+ end
16
+
17
+ subject do
18
+ args = [@client_id, @client_secret, @options].compact
19
+ OmniAuth::Strategies::Scalus.new(nil, *args).tap do |strategy|
20
+ strategy.stub(:request) { @request }
21
+ strategy.stub(:session) { {} }
22
+ end
23
+ end
24
+
25
+ describe '#fix_https' do
26
+ it 'replaces http scheme by https' do
27
+ @options = {:client_options => {:site => 'http://foo.bar/'}}
28
+ subject.fix_https
29
+ subject.options[:client_options][:site].should eq('https://foo.bar/')
30
+ end
31
+
32
+ it 'does not replace https scheme' do
33
+ @options = {:client_options => {:site => 'https://foo.bar/'}}
34
+ subject.fix_https
35
+ subject.options[:client_options][:site].should eq('https://foo.bar/')
36
+ end
37
+ end
38
+
39
+ describe '#client' do
40
+ it 'has correct scalus site' do
41
+ subject.client.site.should eq('https://example.myscalus.com')
42
+ end
43
+
44
+ it 'has correct authorize url' do
45
+ subject.client.options[:authorize_url].should eq('/admin/oauth/authorize')
46
+ end
47
+
48
+ it 'has correct token url' do
49
+ subject.client.options[:token_url].should eq('/admin/oauth/access_token')
50
+ end
51
+ end
52
+
53
+ describe '#callback_url' do
54
+ it "returns value from #callback_url" do
55
+ url = 'http://auth.myapp.com/auth/callback'
56
+ @options = {:callback_url => url}
57
+ subject.callback_url.should eq(url)
58
+ end
59
+
60
+ it "defaults to callback" do
61
+ url_base = 'http://auth.request.com'
62
+ @request.stub(:url) { "#{url_base}/page/path" }
63
+ @request.stub(:scheme) { 'http' }
64
+ subject.stub(:script_name) { "" } # to not depend from Rack env
65
+ subject.callback_url.should eq("#{url_base}/auth/scalus/callback")
66
+ end
67
+ end
68
+
69
+ describe '#authorize_params' do
70
+ it 'includes default scope for read_products' do
71
+ subject.authorize_params.should be_a(Hash)
72
+ subject.authorize_params[:scope].should eq('read_products')
73
+ end
74
+
75
+ it 'includes custom scope' do
76
+ @options = {:scope => 'write_products'}
77
+ subject.authorize_params.should be_a(Hash)
78
+ subject.authorize_params[:scope].should eq('write_products')
79
+ end
80
+ end
81
+
82
+ describe '#uid' do
83
+ it 'returns the shop' do
84
+ subject.uid.should eq('example.myscalus.com')
85
+ end
86
+ end
87
+
88
+ describe '#credentials' do
89
+ before :each do
90
+ @access_token = double('OAuth2::AccessToken')
91
+ @access_token.stub(:token)
92
+ @access_token.stub(:expires?)
93
+ @access_token.stub(:expires_at)
94
+ @access_token.stub(:refresh_token)
95
+ subject.stub(:access_token) { @access_token }
96
+ end
97
+
98
+ it 'returns a Hash' do
99
+ subject.credentials.should be_a(Hash)
100
+ end
101
+
102
+ it 'returns the token' do
103
+ @access_token.stub(:token) { '123' }
104
+ subject.credentials['token'].should eq('123')
105
+ end
106
+
107
+ it 'returns the expiry status' do
108
+ @access_token.stub(:expires?) { true }
109
+ subject.credentials['expires'].should eq(true)
110
+
111
+ @access_token.stub(:expires?) { false }
112
+ subject.credentials['expires'].should eq(false)
113
+ end
114
+
115
+ end
116
+
117
+ describe '#valid_site?' do
118
+ it 'returns true if the site contains .myscalus.com' do
119
+ @options = {:client_options => {:site => 'http://foo.myscalus.com/'}}
120
+ subject.valid_site?.should eq(true)
121
+ end
122
+
123
+ it 'returns false if the site does not contain .myscalus.com' do
124
+ @options = {:client_options => {:site => 'http://foo.example.com/'}}
125
+ subject.valid_site?.should eq(false)
126
+ end
127
+
128
+ it 'uses configurable option for myscalus_domain' do
129
+ @options = {:client_options => {:site => 'http://foo.example.com/'}, :myscalus_domain => 'example.com'}
130
+ subject.valid_site?.should eq(true)
131
+ end
132
+
133
+ it 'allows custom port for myscalus_domain' do
134
+ @options = {:client_options => {:site => 'http://foo.example.com:3456/'}, :myscalus_domain => 'example.com:3456'}
135
+ subject.valid_site?.should eq(true)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,218 @@
1
+ require_relative 'test_helper'
2
+
3
+ class IntegrationTest < Minitest::Test
4
+ def setup
5
+ build_app
6
+ end
7
+
8
+ def teardown
9
+ FakeWeb.clean_registry
10
+ FakeWeb.last_request = nil
11
+ end
12
+
13
+ def test_authorize
14
+ response = authorize('snowdevil.myscalus.com')
15
+ assert_equal 302, response.status
16
+ assert_match /\A#{Regexp.quote("https://snowdevil.myscalus.com/admin/oauth/authorize?")}/, response.location
17
+ redirect_params = Rack::Utils.parse_query(URI(response.location).query)
18
+ assert_equal "123", redirect_params['client_id']
19
+ assert_equal "https://app.example.com/auth/scalus/callback", redirect_params['redirect_uri']
20
+ assert_equal "read_products", redirect_params['scope']
21
+ end
22
+
23
+ def test_authorize_overrides_site_with_https_scheme
24
+ build_app setup: lambda { |env|
25
+ params = Rack::Utils.parse_query(env['QUERY_STRING'])
26
+ env['omniauth.strategy'].options[:client_options][:site] = "http://#{params['shop']}"
27
+ }
28
+
29
+ response = authorize('snowdevil.myscalus.com')
30
+ assert_match /\A#{Regexp.quote("https://snowdevil.myscalus.com/admin/oauth/authorize?")}/, response.location
31
+ end
32
+
33
+ def test_site_validation
34
+ code = SecureRandom.hex(16)
35
+
36
+ [
37
+ 'foo.example.com', # shop doesn't end with .myscalus.com
38
+ 'http://snowdevil.myscalus.com', # shop contains protocol
39
+ 'snowdevil.myscalus.com/path', # shop contains path
40
+ 'user@snowdevil.myscalus.com', # shop contains user
41
+ 'snowdevil.myscalus.com:22', # shop contains port
42
+ ].each do |shop, valid|
43
+ response = authorize(shop)
44
+ assert_auth_failure(response, 'invalid_site')
45
+
46
+ response = callback(sign_params(shop: shop, code: code))
47
+ assert_auth_failure(response, 'invalid_site')
48
+ end
49
+ end
50
+
51
+ def test_callback
52
+ access_token = SecureRandom.hex(16)
53
+ code = SecureRandom.hex(16)
54
+ expect_access_token_request(access_token)
55
+
56
+ response = callback(sign_params(shop: 'snowdevil.myscalus.com', code: code, state: opts["rack.session"]["omniauth.state"]))
57
+
58
+ assert_callback_success(response, access_token, code)
59
+ end
60
+
61
+ def test_callback_with_legacy_signature
62
+ access_token = SecureRandom.hex(16)
63
+ code = SecureRandom.hex(16)
64
+ expect_access_token_request(access_token)
65
+
66
+ response = callback(sign_params(shop: 'snowdevil.myscalus.com', code: code, state: opts["rack.session"]["omniauth.state"]).merge(signature: 'ignored'))
67
+
68
+ assert_callback_success(response, access_token, code)
69
+ end
70
+
71
+ def test_callback_custom_params
72
+ access_token = SecureRandom.hex(16)
73
+ code = SecureRandom.hex(16)
74
+ FakeWeb.register_uri(:post, "https://snowdevil.myscalus.com/admin/oauth/access_token",
75
+ body: JSON.dump(access_token: access_token),
76
+ content_type: 'application/json')
77
+
78
+ now = Time.now.to_i
79
+ params = { shop: 'snowdevil.myscalus.com', code: code, timestamp: now, next: '/products?page=2&q=red%20shirt', state: opts["rack.session"]["omniauth.state"] }
80
+ encoded_params = "code=#{code}&next=/products?page=2%26q=red%2520shirt&shop=snowdevil.myscalus.com&state=#{opts["rack.session"]["omniauth.state"]}&timestamp=#{now}"
81
+ params[:hmac] = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @secret, encoded_params)
82
+
83
+ response = callback(params)
84
+
85
+ assert_callback_success(response, access_token, code)
86
+ end
87
+
88
+ def test_callback_rejects_invalid_hmac
89
+ @secret = 'wrong_secret'
90
+ response = callback(sign_params(shop: 'snowdevil.myscalus.com', code: SecureRandom.hex(16)))
91
+
92
+ assert_auth_failure(response, 'invalid_signature')
93
+ end
94
+
95
+ def test_callback_rejects_old_timestamps
96
+ expired_timestamp = Time.now.to_i - OmniAuth::Strategies::Scalus::CODE_EXPIRES_AFTER - 1
97
+ response = callback(sign_params(shop: 'snowdevil.myscalus.com', code: SecureRandom.hex(16), timestamp: expired_timestamp))
98
+
99
+ assert_auth_failure(response, 'invalid_signature')
100
+ end
101
+
102
+ def test_callback_rejects_missing_hmac
103
+ code = SecureRandom.hex(16)
104
+
105
+ response = callback(shop: 'snowdevil.myscalus.com', code: code, timestamp: Time.now.to_i)
106
+
107
+ assert_auth_failure(response, 'invalid_signature')
108
+ end
109
+
110
+ def test_callback_rejects_body_params
111
+ code = SecureRandom.hex(16)
112
+ params = sign_params(shop: 'snowdevil.myscalus.com', code: code)
113
+ body = Rack::Utils.build_nested_query(unsigned: 'value')
114
+
115
+ response = request.get("https://app.example.com/auth/scalus/callback?#{Rack::Utils.build_query(params)}",
116
+ input: body,
117
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded')
118
+
119
+ assert_auth_failure(response, 'invalid_signature')
120
+ end
121
+
122
+ def test_provider_options
123
+ build_app scope: 'read_products,read_orders,write_content',
124
+ callback_path: '/admin/auth/legacy/callback',
125
+ myscalus_domain: 'myscalus.dev:3000',
126
+ setup: lambda { |env|
127
+ shop = Rack::Request.new(env).GET['shop']
128
+ shop += ".myscalus.dev:3000" unless shop.include?(".")
129
+ env['omniauth.strategy'].options[:client_options][:site] = "https://#{shop}"
130
+ }
131
+
132
+ response = authorize('snowdevil')
133
+ assert_equal 302, response.status
134
+ assert_match /\A#{Regexp.quote("https://snowdevil.myscalus.dev:3000/admin/oauth/authorize?")}/, response.location
135
+ redirect_params = Rack::Utils.parse_query(URI(response.location).query)
136
+ assert_equal 'read_products,read_orders,write_content', redirect_params['scope']
137
+ assert_equal 'https://app.example.com/admin/auth/legacy/callback', redirect_params['redirect_uri']
138
+ end
139
+ def test_callback_with_invalid_state_fails
140
+ access_token = SecureRandom.hex(16)
141
+ code = SecureRandom.hex(16)
142
+ FakeWeb.register_uri(:post, "https://snowdevil.myscalus.com/admin/oauth/access_token",
143
+ body: JSON.dump(access_token: access_token),
144
+ content_type: 'application/json')
145
+
146
+ response = callback(sign_params(shop: 'snowdevil.myscalus.com', code: code, state: 'invalid'))
147
+
148
+ assert_equal 302, response.status
149
+ assert_equal '/auth/failure?message=csrf_detected&strategy=scalus', response.location
150
+ end
151
+ private
152
+
153
+ def sign_params(params)
154
+ params = params.dup
155
+
156
+ params[:timestamp] ||= Time.now.to_i
157
+
158
+ encoded_params = OmniAuth::Strategies::Scalus.encoded_params_for_signature(params)
159
+ params['hmac'] = OmniAuth::Strategies::Scalus.hmac_sign(encoded_params, @secret)
160
+ params
161
+ end
162
+
163
+ def expect_access_token_request(access_token)
164
+ FakeWeb.register_uri(:post, "https://snowdevil.myscalus.com/admin/oauth/access_token",
165
+ body: JSON.dump(access_token: access_token),
166
+ content_type: 'application/json')
167
+ end
168
+
169
+ def assert_callback_success(response, access_token, code)
170
+ token_request_params = Rack::Utils.parse_query(FakeWeb.last_request.body)
171
+ assert_equal token_request_params['client_id'], '123'
172
+ assert_equal token_request_params['client_secret'], @secret
173
+ assert_equal token_request_params['code'], code
174
+
175
+ assert_equal 'snowdevil.myscalus.com', @omniauth_result.uid
176
+ assert_equal access_token, @omniauth_result.credentials.token
177
+ assert_equal false, @omniauth_result.credentials.expires
178
+
179
+ assert_equal 200, response.status
180
+ assert_equal "OK", response.body
181
+ end
182
+
183
+ def assert_auth_failure(response, reason)
184
+ assert_nil FakeWeb.last_request
185
+ assert_equal 302, response.status
186
+ assert_match /\A#{Regexp.quote("/auth/failure?message=#{reason}")}/, response.location
187
+ end
188
+
189
+ def build_app(options={})
190
+ app = proc { |env|
191
+ @omniauth_result = env['omniauth.auth']
192
+ [200, {Rack::CONTENT_TYPE => "text/plain"}, "OK"]
193
+ }
194
+
195
+ opts["rack.session"]["omniauth.state"] = SecureRandom.hex(32)
196
+ app = OmniAuth::Builder.new(app) do
197
+ provider :scalus, '123', '53cr3tz', options
198
+ end
199
+ @secret = '53cr3tz'
200
+ @app = Rack::Session::Cookie.new(app, secret: SecureRandom.hex(64))
201
+ end
202
+
203
+ def authorize(shop)
204
+ request.get("https://app.example.com/auth/scalus?shop=#{CGI.escape(shop)}", opts)
205
+ end
206
+
207
+ def callback(params)
208
+ request.get("https://app.example.com/auth/scalus/callback?#{Rack::Utils.build_query(params)}", opts)
209
+ end
210
+
211
+ def opts
212
+ @opts ||= { "rack.session" => {} }
213
+ end
214
+
215
+ def request
216
+ Rack::MockRequest.new(@app)
217
+ end
218
+ end
@@ -0,0 +1,10 @@
1
+ $: << File.expand_path("../../lib", __FILE__)
2
+ require 'bundler/setup'
3
+ require 'omniauth-scalus-oauth2'
4
+
5
+ require 'minitest/autorun'
6
+ require 'fakeweb'
7
+ require 'json'
8
+
9
+ OmniAuth.config.logger = Logger.new(nil)
10
+ FakeWeb.allow_net_connect = false
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-scalus-oauth2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - David Henner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: omniauth-oauth2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fakeweb
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - drhenner@scalus.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - README.md
79
+ - Rakefile
80
+ - example/Gemfile
81
+ - example/config.ru
82
+ - lib/omniauth-scalus-oauth2.rb
83
+ - lib/omniauth/scalus.rb
84
+ - lib/omniauth/scalus/version.rb
85
+ - lib/omniauth/strategies/scalus.rb
86
+ - omniauth-scalus-oauth2.gemspec
87
+ - shipit.rubygems.yml
88
+ - spec/omniauth/strategies/scalus_spec.rb
89
+ - test/integration_test.rb
90
+ - test/test_helper.rb
91
+ homepage: https://github.com/scalus/omniauth-scalus-oauth2
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.2.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Scalus strategy for OmniAuth
115
+ test_files:
116
+ - spec/omniauth/strategies/scalus_spec.rb
117
+ - test/integration_test.rb
118
+ - test/test_helper.rb