googleauth 0.5.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +5 -4
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  6. data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
  7. data/.kokoro/build.bat +16 -0
  8. data/.kokoro/build.sh +4 -0
  9. data/.kokoro/continuous/common.cfg +24 -0
  10. data/.kokoro/continuous/linux.cfg +25 -0
  11. data/.kokoro/continuous/osx.cfg +8 -0
  12. data/.kokoro/continuous/post.cfg +30 -0
  13. data/.kokoro/continuous/windows.cfg +29 -0
  14. data/.kokoro/osx.sh +4 -0
  15. data/.kokoro/presubmit/common.cfg +24 -0
  16. data/.kokoro/presubmit/linux.cfg +24 -0
  17. data/.kokoro/presubmit/osx.cfg +8 -0
  18. data/.kokoro/presubmit/windows.cfg +29 -0
  19. data/.kokoro/release.cfg +94 -0
  20. data/.kokoro/trampoline.bat +10 -0
  21. data/.kokoro/trampoline.sh +4 -0
  22. data/.repo-metadata.json +5 -0
  23. data/.rubocop.yml +19 -1
  24. data/CHANGELOG.md +112 -19
  25. data/CODE_OF_CONDUCT.md +43 -0
  26. data/Gemfile +19 -13
  27. data/{COPYING → LICENSE} +0 -0
  28. data/README.md +58 -18
  29. data/Rakefile +126 -9
  30. data/googleauth.gemspec +28 -25
  31. data/integration/helper.rb +31 -0
  32. data/integration/id_tokens/key_source_test.rb +74 -0
  33. data/lib/googleauth.rb +7 -96
  34. data/lib/googleauth/application_default.rb +81 -0
  35. data/lib/googleauth/client_id.rb +21 -19
  36. data/lib/googleauth/compute_engine.rb +70 -43
  37. data/lib/googleauth/credentials.rb +442 -0
  38. data/lib/googleauth/credentials_loader.rb +117 -43
  39. data/lib/googleauth/default_credentials.rb +93 -0
  40. data/lib/googleauth/iam.rb +11 -11
  41. data/lib/googleauth/id_tokens.rb +233 -0
  42. data/lib/googleauth/id_tokens/errors.rb +71 -0
  43. data/lib/googleauth/id_tokens/key_sources.rb +394 -0
  44. data/lib/googleauth/id_tokens/verifier.rb +144 -0
  45. data/lib/googleauth/json_key_reader.rb +50 -0
  46. data/lib/googleauth/scope_util.rb +12 -12
  47. data/lib/googleauth/service_account.rb +74 -63
  48. data/lib/googleauth/signet.rb +55 -13
  49. data/lib/googleauth/stores/file_token_store.rb +8 -8
  50. data/lib/googleauth/stores/redis_token_store.rb +22 -22
  51. data/lib/googleauth/token_store.rb +6 -6
  52. data/lib/googleauth/user_authorizer.rb +80 -68
  53. data/lib/googleauth/user_refresh.rb +44 -35
  54. data/lib/googleauth/version.rb +1 -1
  55. data/lib/googleauth/web_user_authorizer.rb +77 -68
  56. data/rakelib/devsite_builder.rb +45 -0
  57. data/rakelib/link_checker.rb +64 -0
  58. data/rakelib/repo_metadata.rb +59 -0
  59. data/spec/googleauth/apply_auth_examples.rb +74 -50
  60. data/spec/googleauth/client_id_spec.rb +75 -55
  61. data/spec/googleauth/compute_engine_spec.rb +98 -46
  62. data/spec/googleauth/credentials_spec.rb +478 -0
  63. data/spec/googleauth/get_application_default_spec.rb +149 -111
  64. data/spec/googleauth/iam_spec.rb +25 -25
  65. data/spec/googleauth/scope_util_spec.rb +26 -24
  66. data/spec/googleauth/service_account_spec.rb +269 -144
  67. data/spec/googleauth/signet_spec.rb +101 -30
  68. data/spec/googleauth/stores/file_token_store_spec.rb +12 -13
  69. data/spec/googleauth/stores/redis_token_store_spec.rb +11 -11
  70. data/spec/googleauth/stores/store_examples.rb +16 -16
  71. data/spec/googleauth/user_authorizer_spec.rb +153 -124
  72. data/spec/googleauth/user_refresh_spec.rb +186 -121
  73. data/spec/googleauth/web_user_authorizer_spec.rb +82 -69
  74. data/spec/spec_helper.rb +21 -19
  75. data/test/helper.rb +33 -0
  76. data/test/id_tokens/key_sources_test.rb +240 -0
  77. data/test/id_tokens/verifier_test.rb +269 -0
  78. metadata +87 -34
  79. data/.rubocop_todo.yml +0 -32
  80. data/.travis.yml +0 -37
@@ -27,45 +27,116 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'apply_auth_examples'
35
- require 'googleauth/signet'
36
- require 'jwt'
37
- require 'openssl'
38
- require 'spec_helper'
34
+ require "apply_auth_examples"
35
+ require "googleauth/signet"
36
+ require "jwt"
37
+ require "openssl"
38
+ require "spec_helper"
39
39
 
40
40
  describe Signet::OAuth2::Client do
41
- before(:example) do
42
- @key = OpenSSL::PKey::RSA.new(2048)
41
+ before :example do
42
+ @key = OpenSSL::PKey::RSA.new 2048
43
43
  @client = Signet::OAuth2::Client.new(
44
- token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
45
- scope: 'https://www.googleapis.com/auth/userinfo.profile',
46
- issuer: 'app@example.com',
47
- audience: 'https://accounts.google.com/o/oauth2/token',
48
- signing_key: @key)
44
+ token_credential_uri: "https://oauth2.googleapis.com/token",
45
+ scope: "https://www.googleapis.com/auth/userinfo.profile",
46
+ issuer: "app@example.com",
47
+ audience: "https://oauth2.googleapis.com/token",
48
+ signing_key: @key
49
+ )
50
+ @id_client = Signet::OAuth2::Client.new(
51
+ token_credential_uri: "https://oauth2.googleapis.com/token",
52
+ target_audience: "https://pubsub.googleapis.com/",
53
+ issuer: "app@example.com",
54
+ audience: "https://oauth2.googleapis.com/token",
55
+ signing_key: @key
56
+ )
49
57
  end
50
58
 
51
- def make_auth_stubs(opts)
52
- access_token = opts[:access_token] || ''
53
- body = MultiJson.dump('access_token' => access_token,
54
- 'token_type' => 'Bearer',
55
- 'expires_in' => 3600)
59
+ def make_auth_stubs opts
60
+ body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
61
+ body_fields["access_token"] = opts[:access_token] if opts[:access_token]
62
+ body_fields["id_token"] = opts[:id_token] if opts[:id_token]
63
+ body = MultiJson.dump body_fields
56
64
  blk = proc do |request|
57
- params = Addressable::URI.form_unencode(request.body)
58
- _claim, _header = JWT.decode(params.assoc('assertion').last,
59
- @key.public_key)
65
+ params = Addressable::URI.form_unencode request.body
66
+ claim, _header = JWT.decode(params.assoc("assertion").last,
67
+ @key.public_key, true,
68
+ algorithm: "RS256")
69
+ !opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
60
70
  end
61
- stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
62
- .with(body: hash_including(
63
- 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'),
64
- &blk)
65
- .to_return(body: body,
66
- status: 200,
67
- headers: { 'Content-Type' => 'application/json' })
71
+ with_params = { body: hash_including(
72
+ "grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
73
+ ) }
74
+ with_params[:headers] = { "User-Agent" => opts[:user_agent] } if opts[:user_agent]
75
+ stub_request(:post, "https://oauth2.googleapis.com/token")
76
+ .with(with_params, &blk)
77
+ .to_return(body: body,
78
+ status: 200,
79
+ headers: { "Content-Type" => "application/json" })
68
80
  end
69
81
 
70
- it_behaves_like 'apply/apply! are OK'
82
+ it_behaves_like "apply/apply! are OK"
83
+
84
+ describe "#configure_connection" do
85
+ it "honors default_connection" do
86
+ token = "1/abcdef1234567890"
87
+ stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/1.0"
88
+ conn = Faraday.new headers: { "User-Agent" => "RubyRocks/1.0" }
89
+ @client.configure_connection default_connection: conn
90
+ md = { foo: "bar" }
91
+ @client.apply! md
92
+ want = { foo: "bar", authorization: "Bearer #{token}" }
93
+ expect(md).to eq(want)
94
+ expect(stub).to have_been_requested
95
+ end
96
+
97
+ it "honors connection_builder" do
98
+ token = "1/abcdef1234567890"
99
+ stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/2.0"
100
+ connection_builder = proc do
101
+ Faraday.new headers: { "User-Agent" => "RubyRocks/2.0" }
102
+ end
103
+ @client.configure_connection connection_builder: connection_builder
104
+ md = { foo: "bar" }
105
+ @client.apply! md
106
+ want = { foo: "bar", authorization: "Bearer #{token}" }
107
+ expect(md).to eq(want)
108
+ expect(stub).to have_been_requested
109
+ end
110
+ end
111
+
112
+ describe "#fetch_access_token!" do
113
+ it "retries when orig_fetch_access_token! raises Signet::RemoteServerError" do
114
+ mocked_responses = [:raise, :raise, "success"]
115
+ allow(@client).to receive(:orig_fetch_access_token!).exactly(3).times do
116
+ response = mocked_responses.shift
117
+ response == :raise ? raise(Signet::RemoteServerError) : response
118
+ end
119
+ expect(@client.fetch_access_token!).to eq("success")
120
+ end
121
+
122
+ it "raises when the max retry count is exceeded" do
123
+ mocked_responses = [:raise, :raise, :raise, :raise, :raise, :raise, "success"]
124
+ allow(@client).to receive(:orig_fetch_access_token!).exactly(6).times do
125
+ response = mocked_responses.shift
126
+ response == :raise ? raise(Signet::RemoteServerError) : response
127
+ end
128
+ expect { @client.fetch_access_token! }.to raise_error Signet::AuthorizationError
129
+ end
130
+
131
+ it "does not retry and raises right away if it encounters a Signet::AuthorizationError" do
132
+ allow(@client).to receive(:orig_fetch_access_token!).at_most(:once)
133
+ .and_raise(Signet::AuthorizationError.new("Some Message"))
134
+ expect { @client.fetch_access_token! }.to raise_error Signet::AuthorizationError
135
+ end
136
+
137
+ it "does not retry and raises right away if it encounters a Signet::ParseError" do
138
+ allow(@client).to receive(:orig_fetch_access_token!).at_most(:once).and_raise(Signet::ParseError)
139
+ expect { @client.fetch_access_token! }.to raise_error Signet::ParseError
140
+ end
141
+ end
71
142
  end
@@ -27,32 +27,31 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'googleauth'
35
- require 'googleauth/stores/file_token_store'
36
- require 'spec_helper'
37
- require 'fakefs/safe'
38
- require 'fakefs/spec_helpers'
39
- require 'googleauth/stores/store_examples'
34
+ require "googleauth"
35
+ require "googleauth/stores/file_token_store"
36
+ require "spec_helper"
37
+ require "fakefs/safe"
38
+ require "fakefs/spec_helpers"
39
+ require "googleauth/stores/store_examples"
40
40
 
41
41
  module FakeFS
42
42
  class File
43
43
  # FakeFS doesn't implement. And since we don't need to actually lock,
44
44
  # just stub out...
45
- def flock(*)
46
- end
45
+ def flock *; end
47
46
  end
48
47
  end
49
48
 
50
49
  describe Google::Auth::Stores::FileTokenStore do
51
50
  include FakeFS::SpecHelpers
52
51
 
53
- let(:store) do
54
- Google::Auth::Stores::FileTokenStore.new(file: '/tokens.yaml')
52
+ let :store do
53
+ Google::Auth::Stores::FileTokenStore.new file: "/tokens.yaml"
55
54
  end
56
55
 
57
- it_behaves_like 'token store'
56
+ it_behaves_like "token store"
58
57
  end
@@ -27,24 +27,24 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'googleauth'
35
- require 'googleauth/stores/redis_token_store'
36
- require 'spec_helper'
37
- require 'fakeredis/rspec'
38
- require 'googleauth/stores/store_examples'
34
+ require "googleauth"
35
+ require "googleauth/stores/redis_token_store"
36
+ require "spec_helper"
37
+ require "fakeredis/rspec"
38
+ require "googleauth/stores/store_examples"
39
39
 
40
40
  describe Google::Auth::Stores::RedisTokenStore do
41
- let(:redis) do
41
+ let :redis do
42
42
  Redis.new
43
43
  end
44
44
 
45
- let(:store) do
46
- Google::Auth::Stores::RedisTokenStore.new(redis: redis)
45
+ let :store do
46
+ Google::Auth::Stores::RedisTokenStore.new redis: redis
47
47
  end
48
48
 
49
- it_behaves_like 'token store'
49
+ it_behaves_like "token store"
50
50
  end
@@ -27,32 +27,32 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'spec_helper'
34
+ require "spec_helper"
35
35
 
36
- shared_examples 'token store' do
37
- before(:each) do
38
- store.store('default', 'test')
36
+ shared_examples "token store" do
37
+ before :each do
38
+ store.store "default", "test"
39
39
  end
40
40
 
41
- it 'should return a stored value' do
42
- expect(store.load('default')).to eq 'test'
41
+ it "should return a stored value" do
42
+ expect(store.load("default")).to eq "test"
43
43
  end
44
44
 
45
- it 'should return nil for missing tokens' do
46
- expect(store.load('notavalidkey')).to be_nil
45
+ it "should return nil for missing tokens" do
46
+ expect(store.load("notavalidkey")).to be_nil
47
47
  end
48
48
 
49
- it 'should return nil for deleted tokens' do
50
- store.delete('default')
51
- expect(store.load('default')).to be_nil
49
+ it "should return nil for deleted tokens" do
50
+ store.delete "default"
51
+ expect(store.load("default")).to be_nil
52
52
  end
53
53
 
54
- it 'should save overwrite values on store' do
55
- store.store('default', 'test2')
56
- expect(store.load('default')).to eq 'test2'
54
+ it "should save overwrite values on store" do
55
+ store.store "default", "test2"
56
+ expect(store.load("default")).to eq "test2"
57
57
  end
58
58
  end
@@ -27,285 +27,314 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
- $LOAD_PATH.unshift(spec_dir)
30
+ spec_dir = File.expand_path File.join(File.dirname(__FILE__))
31
+ $LOAD_PATH.unshift spec_dir
32
32
  $LOAD_PATH.uniq!
33
33
 
34
- require 'googleauth'
35
- require 'googleauth/user_authorizer'
36
- require 'uri'
37
- require 'multi_json'
38
- require 'spec_helper'
34
+ require "googleauth"
35
+ require "googleauth/user_authorizer"
36
+ require "uri"
37
+ require "multi_json"
38
+ require "spec_helper"
39
39
 
40
40
  describe Google::Auth::UserAuthorizer do
41
41
  include TestHelpers
42
42
 
43
- let(:client_id) { Google::Auth::ClientId.new('testclient', 'notasecret') }
44
- let(:scope) { %w(email profile) }
43
+ let(:client_id) { Google::Auth::ClientId.new "testclient", "notasecret" }
44
+ let(:scope) { %w[email profile] }
45
45
  let(:token_store) { DummyTokenStore.new }
46
- let(:callback_uri) { 'https://www.example.com/oauth/callback' }
47
- let(:authorizer) do
46
+ let(:callback_uri) { "https://www.example.com/oauth/callback" }
47
+ let :authorizer do
48
48
  Google::Auth::UserAuthorizer.new(client_id,
49
49
  scope,
50
50
  token_store,
51
51
  callback_uri)
52
52
  end
53
53
 
54
- shared_examples 'valid authorization url' do
55
- it 'should have a valid base URI' do
54
+ shared_examples "valid authorization url" do
55
+ it "should have a valid base URI" do
56
56
  expect(uri).to match %r{https://accounts.google.com/o/oauth2/auth}
57
57
  end
58
58
 
59
- it 'should request offline access' do
59
+ it "should request offline access" do
60
60
  expect(URI(uri).query).to match(/access_type=offline/)
61
61
  end
62
62
 
63
- it 'should request response type code' do
63
+ it "should request response type code" do
64
64
  expect(URI(uri).query).to match(/response_type=code/)
65
65
  end
66
66
 
67
- it 'should force approval' do
67
+ it "should force approval" do
68
68
  expect(URI(uri).query).to match(/approval_prompt=force/)
69
69
  end
70
70
 
71
- it 'should include granted scopes' do
71
+ it "should include granted scopes" do
72
72
  expect(URI(uri).query).to match(/include_granted_scopes=true/)
73
73
  end
74
74
 
75
- it 'should include the correct client id' do
75
+ it "should include the correct client id" do
76
76
  expect(URI(uri).query).to match(/client_id=testclient/)
77
77
  end
78
78
 
79
- it 'should not include a client secret' do
79
+ it "should not include a client secret" do
80
80
  expect(URI(uri).query).to_not match(/client_secret/)
81
81
  end
82
82
 
83
- it 'should include the callback uri' do
83
+ it "should include the redirect_uri" do
84
84
  expect(URI(uri).query).to match(
85
- %r{redirect_uri=https://www.example.com/oauth/callback})
85
+ %r{redirect_uri=https://www.example.com/oauth/callback}
86
+ )
86
87
  end
87
88
 
88
- it 'should include the scope' do
89
+ it "should include the scope" do
89
90
  expect(URI(uri).query).to match(/scope=email%20profile/)
90
91
  end
91
92
  end
92
93
 
93
- context 'when generating authorization URLs with user ID & state' do
94
- let(:uri) do
95
- authorizer.get_authorization_url(login_hint: 'user1', state: 'mystate')
94
+ context "when generating authorization URLs and callback_uri is 'postmessage'" do
95
+ let(:callback_uri) { "postmessage" }
96
+ let :authorizer do
97
+ Google::Auth::UserAuthorizer.new(client_id,
98
+ scope,
99
+ token_store,
100
+ callback_uri)
101
+ end
102
+ let :uri do
103
+ authorizer.get_authorization_url login_hint: "user1", state: "mystate"
104
+ end
105
+
106
+ it "should include the redirect_uri 'postmessage'" do
107
+ expect(URI(uri).query).to match(
108
+ %r{redirect_uri=postmessage}
109
+ )
110
+ end
111
+ end
112
+
113
+ context "when generating authorization URLs with user ID & state" do
114
+ let :uri do
115
+ authorizer.get_authorization_url login_hint: "user1", state: "mystate"
96
116
  end
97
117
 
98
- it_behaves_like 'valid authorization url'
118
+ it_behaves_like "valid authorization url"
99
119
 
100
- it 'includes a login hint' do
120
+ it "includes a login hint" do
101
121
  expect(URI(uri).query).to match(/login_hint=user1/)
102
122
  end
103
123
 
104
- it 'includes the app state' do
124
+ it "includes the app state" do
105
125
  expect(URI(uri).query).to match(/state=mystate/)
106
126
  end
107
127
  end
108
128
 
109
- context 'when generating authorization URLs with user ID and no state' do
110
- let(:uri) { authorizer.get_authorization_url(login_hint: 'user1') }
129
+ context "when generating authorization URLs with user ID and no state" do
130
+ let(:uri) { authorizer.get_authorization_url login_hint: "user1" }
111
131
 
112
- it_behaves_like 'valid authorization url'
132
+ it_behaves_like "valid authorization url"
113
133
 
114
- it 'includes a login hint' do
134
+ it "includes a login hint" do
115
135
  expect(URI(uri).query).to match(/login_hint=user1/)
116
136
  end
117
137
 
118
- it 'does not include the state parameter' do
138
+ it "does not include the state parameter" do
119
139
  expect(URI(uri).query).to_not match(/state/)
120
140
  end
121
141
  end
122
142
 
123
- context 'when generating authorization URLs with no user ID and no state' do
143
+ context "when generating authorization URLs with no user ID and no state" do
124
144
  let(:uri) { authorizer.get_authorization_url }
125
145
 
126
- it_behaves_like 'valid authorization url'
146
+ it_behaves_like "valid authorization url"
127
147
 
128
- it 'does not include the login hint parameter' do
148
+ it "does not include the login hint parameter" do
129
149
  expect(URI(uri).query).to_not match(/login_hint/)
130
150
  end
131
151
 
132
- it 'does not include the state parameter' do
152
+ it "does not include the state parameter" do
133
153
  expect(URI(uri).query).to_not match(/state/)
134
154
  end
135
155
  end
136
156
 
137
- context 'when retrieving tokens' do
138
- let(:token_json) do
157
+ context "when retrieving tokens" do
158
+ let :token_json do
139
159
  MultiJson.dump(
140
- access_token: 'accesstoken',
141
- refresh_token: 'refreshtoken',
142
- expiration_time_millis: 1_441_234_742_000)
160
+ access_token: "accesstoken",
161
+ refresh_token: "refreshtoken",
162
+ expiration_time_millis: 1_441_234_742_000
163
+ )
143
164
  end
144
165
 
145
- context 'with a valid user id' do
146
- let(:credentials) do
147
- token_store.store('user1', token_json)
148
- authorizer.get_credentials('user1')
166
+ context "with a valid user id" do
167
+ let :credentials do
168
+ token_store.store "user1", token_json
169
+ authorizer.get_credentials "user1"
149
170
  end
150
171
 
151
- it 'should return an instance of UserRefreshCredentials' do
172
+ it "should return an instance of UserRefreshCredentials" do
152
173
  expect(credentials).to be_instance_of(
153
- Google::Auth::UserRefreshCredentials)
174
+ Google::Auth::UserRefreshCredentials
175
+ )
154
176
  end
155
177
 
156
- it 'should return credentials with a valid refresh token' do
157
- expect(credentials.refresh_token).to eq 'refreshtoken'
178
+ it "should return credentials with a valid refresh token" do
179
+ expect(credentials.refresh_token).to eq "refreshtoken"
158
180
  end
159
181
 
160
- it 'should return credentials with a valid access token' do
161
- expect(credentials.access_token).to eq 'accesstoken'
182
+ it "should return credentials with a valid access token" do
183
+ expect(credentials.access_token).to eq "accesstoken"
162
184
  end
163
185
 
164
- it 'should return credentials with a valid client ID' do
165
- expect(credentials.client_id).to eq 'testclient'
186
+ it "should return credentials with a valid client ID" do
187
+ expect(credentials.client_id).to eq "testclient"
166
188
  end
167
189
 
168
- it 'should return credentials with a valid client secret' do
169
- expect(credentials.client_secret).to eq 'notasecret'
190
+ it "should return credentials with a valid client secret" do
191
+ expect(credentials.client_secret).to eq "notasecret"
170
192
  end
171
193
 
172
- it 'should return credentials with a valid scope' do
173
- expect(credentials.scope).to eq %w(email profile)
194
+ it "should return credentials with a valid scope" do
195
+ expect(credentials.scope).to eq %w[email profile]
174
196
  end
175
197
 
176
- it 'should return credentials with a valid expiration time' do
198
+ it "should return credentials with a valid expiration time" do
177
199
  expect(credentials.expires_at).to eq Time.at(1_441_234_742)
178
200
  end
179
201
  end
180
202
 
181
- context 'with an invalid user id' do
182
- it 'should return nil' do
183
- expect(authorizer.get_credentials('notauser')).to be_nil
203
+ context "with an invalid user id" do
204
+ it "should return nil" do
205
+ expect(authorizer.get_credentials("notauser")).to be_nil
184
206
  end
185
207
  end
186
208
  end
187
209
 
188
- context 'when saving tokens' do
210
+ context "when saving tokens" do
189
211
  let(:expiry) { Time.now.to_i }
190
- let(:credentials) do
212
+ let :credentials do
191
213
  Google::Auth::UserRefreshCredentials.new(
192
- client_id: client_id.id,
214
+ client_id: client_id.id,
193
215
  client_secret: client_id.secret,
194
- scope: scope,
195
- refresh_token: 'refreshtoken',
196
- access_token: 'accesstoken',
197
- expires_at: expiry
216
+ scope: scope,
217
+ refresh_token: "refreshtoken",
218
+ access_token: "accesstoken",
219
+ expires_at: expiry
198
220
  )
199
221
  end
200
222
 
201
- let(:token_json) do
202
- authorizer.store_credentials('user1', credentials)
203
- token_store.load('user1')
223
+ let :token_json do
224
+ authorizer.store_credentials "user1", credentials
225
+ token_store.load "user1"
204
226
  end
205
227
 
206
- it 'should persist in the token store' do
228
+ it "should persist in the token store" do
207
229
  expect(token_json).to_not be_nil
208
230
  end
209
231
 
210
- it 'should persist the refresh token' do
211
- expect(MultiJson.load(token_json)['refresh_token']).to eq 'refreshtoken'
232
+ it "should persist the refresh token" do
233
+ expect(MultiJson.load(token_json)["refresh_token"]).to eq "refreshtoken"
212
234
  end
213
235
 
214
- it 'should persist the access token' do
215
- expect(MultiJson.load(token_json)['access_token']).to eq 'accesstoken'
236
+ it "should persist the access token" do
237
+ expect(MultiJson.load(token_json)["access_token"]).to eq "accesstoken"
216
238
  end
217
239
 
218
- it 'should persist the client id' do
219
- expect(MultiJson.load(token_json)['client_id']).to eq 'testclient'
240
+ it "should persist the client id" do
241
+ expect(MultiJson.load(token_json)["client_id"]).to eq "testclient"
220
242
  end
221
243
 
222
- it 'should persist the scope' do
223
- expect(MultiJson.load(token_json)['scope']).to include('email', 'profile')
244
+ it "should persist the scope" do
245
+ expect(MultiJson.load(token_json)["scope"]).to include("email", "profile")
224
246
  end
225
247
 
226
- it 'should persist the expiry as milliseconds' do
248
+ it "should persist the expiry as milliseconds" do
227
249
  expected_expiry = expiry * 1000
228
- expect(MultiJson.load(token_json)['expiration_time_millis']).to eql(
229
- expected_expiry)
250
+ expect(MultiJson.load(token_json)["expiration_time_millis"]).to eql(
251
+ expected_expiry
252
+ )
230
253
  end
231
254
  end
232
255
 
233
- context 'with valid authorization code' do
234
- let(:token_json) do
235
- MultiJson.dump('access_token' => '1/abc123',
236
- 'token_type' => 'Bearer',
237
- 'expires_in' => 3600)
256
+ context "with valid authorization code" do
257
+ let :token_json do
258
+ MultiJson.dump("access_token" => "1/abc123",
259
+ "token_type" => "Bearer",
260
+ "expires_in" => 3600)
238
261
  end
239
262
 
240
- before(:example) do
241
- stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
263
+ before :example do
264
+ stub_request(:post, "https://oauth2.googleapis.com/token")
242
265
  .to_return(body: token_json, status: 200, headers: {
243
- 'Content-Type' => 'application/json' })
266
+ "Content-Type" => "application/json"
267
+ })
244
268
  end
245
269
 
246
- it 'should exchange a code for credentials' do
270
+ it "should exchange a code for credentials" do
247
271
  credentials = authorizer.get_credentials_from_code(
248
- user_id: 'user1', code: 'code')
249
- expect(credentials.access_token).to eq '1/abc123'
272
+ user_id: "user1", code: "code"
273
+ )
274
+ expect(credentials.access_token).to eq "1/abc123"
275
+ expect(credentials.redirect_uri.to_s).to eq "https://www.example.com/oauth/callback"
250
276
  end
251
277
 
252
- it 'should not store credentials when get only requested' do
253
- authorizer.get_credentials_from_code(user_id: 'user1', code: 'code')
254
- expect(token_store.load('user1')).to be_nil
278
+ it "should not store credentials when get only requested" do
279
+ authorizer.get_credentials_from_code user_id: "user1", code: "code"
280
+ expect(token_store.load("user1")).to be_nil
255
281
  end
256
282
 
257
- it 'should store credentials when requested' do
283
+ it "should store credentials when requested" do
258
284
  authorizer.get_and_store_credentials_from_code(
259
- user_id: 'user1', code: 'code')
260
- expect(token_store.load('user1')).to_not be_nil
285
+ user_id: "user1", code: "code"
286
+ )
287
+ expect(token_store.load("user1")).to_not be_nil
261
288
  end
262
289
  end
263
290
 
264
- context 'with invalid authorization code' do
265
- before(:example) do
266
- stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
291
+ context "with invalid authorization code" do
292
+ before :example do
293
+ stub_request(:post, "https://oauth2.googleapis.com/token")
267
294
  .to_return(status: 400)
268
295
  end
269
296
 
270
- it 'should raise an authorization error' do
297
+ it "should raise an authorization error" do
271
298
  expect do
272
- authorizer.get_credentials_from_code(user_id: 'user1', code: 'badcode')
299
+ authorizer.get_credentials_from_code user_id: "user1", code: "badcode"
273
300
  end.to raise_error Signet::AuthorizationError
274
301
  end
275
302
 
276
- it 'should not store credentials when exchange fails' do
303
+ it "should not store credentials when exchange fails" do
277
304
  expect do
278
- authorizer.get_credentials_from_code(user_id: 'user1', code: 'badcode')
305
+ authorizer.get_credentials_from_code user_id: "user1", code: "badcode"
279
306
  end.to raise_error Signet::AuthorizationError
280
- expect(token_store.load('user1')).to be_nil
307
+ expect(token_store.load("user1")).to be_nil
281
308
  end
282
309
  end
283
310
 
284
- context 'when reovking authorization' do
285
- let(:token_json) do
311
+ context "when reovking authorization" do
312
+ let :token_json do
286
313
  MultiJson.dump(
287
- access_token: 'accesstoken',
288
- refresh_token: 'refreshtoken',
289
- expiration_time_millis: 1_441_234_742_000)
314
+ access_token: "accesstoken",
315
+ refresh_token: "refreshtoken",
316
+ expiration_time_millis: 1_441_234_742_000
317
+ )
290
318
  end
291
319
 
292
- before(:example) do
293
- token_store.store('user1', token_json)
294
- stub_request(
295
- :get, 'https://accounts.google.com/o/oauth2/revoke?token=refreshtoken')
320
+ before :example do
321
+ token_store.store "user1", token_json
322
+ stub_request(:post, "https://oauth2.googleapis.com/revoke")
323
+ .with(body: hash_including("token" => "refreshtoken"))
296
324
  .to_return(status: 200)
297
325
  end
298
326
 
299
- it 'should revoke the grant' do
300
- authorizer.revoke_authorization('user1')
327
+ it "should revoke the grant" do
328
+ authorizer.revoke_authorization "user1"
301
329
  expect(a_request(
302
- :get, 'https://accounts.google.com/o/oauth2/revoke?token=refreshtoken'))
330
+ :post, "https://oauth2.googleapis.com/revoke"
331
+ ).with(body: hash_including("token" => "refreshtoken")))
303
332
  .to have_been_made
304
333
  end
305
334
 
306
- it 'should remove the token from storage' do
307
- authorizer.revoke_authorization('user1')
308
- expect(token_store.load('user1')).to be_nil
335
+ it "should remove the token from storage" do
336
+ authorizer.revoke_authorization "user1"
337
+ expect(token_store.load("user1")).to be_nil
309
338
  end
310
339
  end
311
340