googleauth 0.5.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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