google_sign_in 0.1.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile.lock +130 -10
  5. data/README.md +114 -47
  6. data/Rakefile +31 -1
  7. data/SECURITY.md +15 -0
  8. data/app/controllers/google_sign_in/authorizations_controller.rb +17 -0
  9. data/app/controllers/google_sign_in/base_controller.rb +15 -0
  10. data/app/controllers/google_sign_in/callbacks_controller.rb +27 -0
  11. data/app/helpers/google_sign_in/button_helper.rb +7 -0
  12. data/bin/rails +16 -0
  13. data/config/routes.rb +4 -0
  14. data/google_sign_in.gemspec +9 -6
  15. data/lib/google_sign_in.rb +9 -1
  16. data/lib/google_sign_in/engine.rb +28 -0
  17. data/lib/google_sign_in/identity.rb +10 -21
  18. data/lib/google_sign_in/redirect_protector.rb +25 -0
  19. data/test/certificate.pem +19 -0
  20. data/test/controllers/authorizations_controller_test.rb +26 -0
  21. data/test/controllers/callbacks_controller_test.rb +36 -0
  22. data/test/dummy/.ruby-version +1 -0
  23. data/test/dummy/Rakefile +6 -0
  24. data/test/dummy/app/assets/config/manifest.js +3 -0
  25. data/test/dummy/app/assets/images/.keep +0 -0
  26. data/test/dummy/app/assets/javascripts/application.js +15 -0
  27. data/test/dummy/app/assets/javascripts/cable.js +13 -0
  28. data/test/dummy/app/assets/javascripts/channels/.keep +0 -0
  29. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  30. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  31. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  32. data/test/dummy/app/controllers/application_controller.rb +2 -0
  33. data/test/dummy/app/controllers/concerns/.keep +0 -0
  34. data/test/dummy/app/helpers/application_helper.rb +2 -0
  35. data/test/dummy/app/jobs/application_job.rb +2 -0
  36. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  37. data/test/dummy/app/models/application_record.rb +3 -0
  38. data/test/dummy/app/models/concerns/.keep +0 -0
  39. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  40. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  41. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  42. data/test/dummy/bin/bundle +3 -0
  43. data/test/dummy/bin/rails +4 -0
  44. data/test/dummy/bin/rake +4 -0
  45. data/test/dummy/bin/setup +36 -0
  46. data/test/dummy/bin/update +31 -0
  47. data/test/dummy/bin/yarn +11 -0
  48. data/test/dummy/config.ru +5 -0
  49. data/test/dummy/config/application.rb +20 -0
  50. data/test/dummy/config/boot.rb +5 -0
  51. data/test/dummy/config/cable.yml +10 -0
  52. data/test/dummy/config/database.yml +25 -0
  53. data/test/dummy/config/environment.rb +5 -0
  54. data/test/dummy/config/environments/development.rb +32 -0
  55. data/test/dummy/config/environments/production.rb +57 -0
  56. data/test/dummy/config/environments/test.rb +33 -0
  57. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  58. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  59. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  60. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  61. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  62. data/test/dummy/config/initializers/google_sign_in.rb +4 -0
  63. data/test/dummy/config/initializers/inflections.rb +16 -0
  64. data/test/dummy/config/initializers/mime_types.rb +4 -0
  65. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  66. data/test/dummy/config/locales/en.yml +33 -0
  67. data/test/dummy/config/puma.rb +34 -0
  68. data/test/dummy/config/routes.rb +2 -0
  69. data/test/dummy/config/spring.rb +6 -0
  70. data/test/dummy/config/storage.yml +34 -0
  71. data/test/dummy/lib/assets/.keep +0 -0
  72. data/test/dummy/log/.keep +0 -0
  73. data/test/dummy/package.json +5 -0
  74. data/test/dummy/public/404.html +67 -0
  75. data/test/dummy/public/422.html +67 -0
  76. data/test/dummy/public/500.html +66 -0
  77. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  78. data/test/dummy/public/apple-touch-icon.png +0 -0
  79. data/test/dummy/public/favicon.ico +0 -0
  80. data/test/helpers/button_helper_test.rb +36 -0
  81. data/test/key.pem +27 -0
  82. data/test/models/identity_test.rb +76 -0
  83. data/test/models/redirect_protector_test.rb +34 -0
  84. data/test/test_helper.rb +27 -3
  85. metadata +200 -10
  86. data/lib/google_sign_in/helper.rb +0 -76
  87. data/lib/google_sign_in/railtie.rb +0 -12
  88. data/test/identity_test.rb +0 -13
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+ require 'google_sign_in/redirect_protector'
3
+
4
+ class GoogleSignIn::RedirectProtectorTest < ActiveSupport::TestCase
5
+ test "disallows URL target with different host than source" do
6
+ assert_raises GoogleSignIn::RedirectProtector::Violation do
7
+ GoogleSignIn::RedirectProtector.ensure_same_origin 'https://malicious.example.com', 'https://basecamp.com'
8
+ end
9
+ end
10
+
11
+ test "disallows URL target with different port than source" do
12
+ assert_raises GoogleSignIn::RedirectProtector::Violation do
13
+ GoogleSignIn::RedirectProtector.ensure_same_origin 'https://basecamp.com:10443', 'https://basecamp.com'
14
+ end
15
+ end
16
+
17
+ test "disallows URL target with different protocol than source" do
18
+ assert_raises GoogleSignIn::RedirectProtector::Violation do
19
+ GoogleSignIn::RedirectProtector.ensure_same_origin 'http://basecamp.com', 'https://basecamp.com'
20
+ end
21
+ end
22
+
23
+ test "allows URL target with same origin as source" do
24
+ assert_nothing_raised do
25
+ GoogleSignIn::RedirectProtector.ensure_same_origin 'https://basecamp.com', 'https://basecamp.com'
26
+ end
27
+ end
28
+
29
+ test "allows path target" do
30
+ assert_nothing_raised do
31
+ GoogleSignIn::RedirectProtector.ensure_same_origin '/callback', 'https://basecamp.com'
32
+ end
33
+ end
34
+ end
@@ -1,4 +1,28 @@
1
- require 'bundler/setup'
2
- require 'active_support'
3
- require 'active_support/testing/autorun'
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ FAKE_GOOGLE_CLIENT_ID = '86179201039-eks5VfVc46WoFYyZVUDpQHeZFDRCqno3.apps.googleusercontent.com'
4
+ FAKE_GOOGLE_CLIENT_SECRET = 'r(XsBajmyMddruvf$jDgLyPK'
5
+
6
+ require_relative '../test/dummy/config/environment'
7
+
8
+ require 'rails/test_help'
9
+ require 'webmock/minitest'
4
10
  require 'byebug'
11
+
12
+ require 'openssl'
13
+ GOOGLE_PRIVATE_KEY = OpenSSL::PKey::RSA.new(File.read(File.expand_path('key.pem', __dir__)))
14
+ GOOGLE_X509_CERTIFICATE = OpenSSL::X509::Certificate.new(File.read(File.expand_path('certificate.pem', __dir__)))
15
+
16
+ if GOOGLE_X509_CERTIFICATE.not_after <= Time.now
17
+ raise "Test certificate is expired. Generate a new one and run the tests again: `bundle exec rake test:certificate:generate`."
18
+ end
19
+
20
+ require 'google-id-token'
21
+ GoogleSignIn::Identity.validator = GoogleIDToken::Validator.new(x509_cert: GOOGLE_X509_CERTIFICATE)
22
+
23
+ class ActionView::TestCase
24
+ private
25
+ def assert_dom_equal(expected, actual, message = nil)
26
+ super expected.remove(/(\A|\n)\s*/), actual, message
27
+ end
28
+ end
metadata CHANGED
@@ -1,29 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google_sign_in
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
+ - George Claghorn
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2018-01-14 00:00:00.000000000 Z
12
+ date: 2018-09-05 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: activesupport
15
+ name: rails
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - ">="
18
19
  - !ruby/object:Gem::Version
19
- version: '5.1'
20
+ version: 5.2.0
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
25
  - - ">="
25
26
  - !ruby/object:Gem::Version
26
- version: '5.1'
27
+ version: 5.2.0
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: google-id-token
29
30
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +39,20 @@ dependencies:
38
39
  - - ">="
39
40
  - !ruby/object:Gem::Version
40
41
  version: 1.4.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: oauth2
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 1.4.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: 1.4.0
41
56
  - !ruby/object:Gem::Dependency
42
57
  name: bundler
43
58
  requirement: !ruby/object:Gem::Requirement
@@ -52,23 +67,130 @@ dependencies:
52
67
  - - "~>"
53
68
  - !ruby/object:Gem::Version
54
69
  version: '1.15'
70
+ - !ruby/object:Gem::Dependency
71
+ name: jwt
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: webmock
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
55
98
  description:
56
- email: david@basecamp.com
99
+ email:
100
+ - david@basecamp.com
101
+ - george@basecamp.com
57
102
  executables: []
58
103
  extensions: []
59
104
  extra_rdoc_files: []
60
105
  files:
106
+ - ".gitignore"
107
+ - ".travis.yml"
61
108
  - Gemfile
62
109
  - Gemfile.lock
63
110
  - MIT-LICENSE
64
111
  - README.md
65
112
  - Rakefile
113
+ - SECURITY.md
114
+ - app/controllers/google_sign_in/authorizations_controller.rb
115
+ - app/controllers/google_sign_in/base_controller.rb
116
+ - app/controllers/google_sign_in/callbacks_controller.rb
117
+ - app/helpers/google_sign_in/button_helper.rb
118
+ - bin/rails
119
+ - config/routes.rb
66
120
  - google_sign_in.gemspec
67
121
  - lib/google_sign_in.rb
68
- - lib/google_sign_in/helper.rb
122
+ - lib/google_sign_in/engine.rb
69
123
  - lib/google_sign_in/identity.rb
70
- - lib/google_sign_in/railtie.rb
71
- - test/identity_test.rb
124
+ - lib/google_sign_in/redirect_protector.rb
125
+ - test/certificate.pem
126
+ - test/controllers/authorizations_controller_test.rb
127
+ - test/controllers/callbacks_controller_test.rb
128
+ - test/dummy/.ruby-version
129
+ - test/dummy/Rakefile
130
+ - test/dummy/app/assets/config/manifest.js
131
+ - test/dummy/app/assets/images/.keep
132
+ - test/dummy/app/assets/javascripts/application.js
133
+ - test/dummy/app/assets/javascripts/cable.js
134
+ - test/dummy/app/assets/javascripts/channels/.keep
135
+ - test/dummy/app/assets/stylesheets/application.css
136
+ - test/dummy/app/channels/application_cable/channel.rb
137
+ - test/dummy/app/channels/application_cable/connection.rb
138
+ - test/dummy/app/controllers/application_controller.rb
139
+ - test/dummy/app/controllers/concerns/.keep
140
+ - test/dummy/app/helpers/application_helper.rb
141
+ - test/dummy/app/jobs/application_job.rb
142
+ - test/dummy/app/mailers/application_mailer.rb
143
+ - test/dummy/app/models/application_record.rb
144
+ - test/dummy/app/models/concerns/.keep
145
+ - test/dummy/app/views/layouts/application.html.erb
146
+ - test/dummy/app/views/layouts/mailer.html.erb
147
+ - test/dummy/app/views/layouts/mailer.text.erb
148
+ - test/dummy/bin/bundle
149
+ - test/dummy/bin/rails
150
+ - test/dummy/bin/rake
151
+ - test/dummy/bin/setup
152
+ - test/dummy/bin/update
153
+ - test/dummy/bin/yarn
154
+ - test/dummy/config.ru
155
+ - test/dummy/config/application.rb
156
+ - test/dummy/config/boot.rb
157
+ - test/dummy/config/cable.yml
158
+ - test/dummy/config/database.yml
159
+ - test/dummy/config/environment.rb
160
+ - test/dummy/config/environments/development.rb
161
+ - test/dummy/config/environments/production.rb
162
+ - test/dummy/config/environments/test.rb
163
+ - test/dummy/config/initializers/application_controller_renderer.rb
164
+ - test/dummy/config/initializers/backtrace_silencers.rb
165
+ - test/dummy/config/initializers/content_security_policy.rb
166
+ - test/dummy/config/initializers/cookies_serializer.rb
167
+ - test/dummy/config/initializers/filter_parameter_logging.rb
168
+ - test/dummy/config/initializers/google_sign_in.rb
169
+ - test/dummy/config/initializers/inflections.rb
170
+ - test/dummy/config/initializers/mime_types.rb
171
+ - test/dummy/config/initializers/wrap_parameters.rb
172
+ - test/dummy/config/locales/en.yml
173
+ - test/dummy/config/puma.rb
174
+ - test/dummy/config/routes.rb
175
+ - test/dummy/config/spring.rb
176
+ - test/dummy/config/storage.yml
177
+ - test/dummy/db/test.sqlite3
178
+ - test/dummy/lib/assets/.keep
179
+ - test/dummy/log/.keep
180
+ - test/dummy/package.json
181
+ - test/dummy/public/404.html
182
+ - test/dummy/public/422.html
183
+ - test/dummy/public/500.html
184
+ - test/dummy/public/apple-touch-icon-precomposed.png
185
+ - test/dummy/public/apple-touch-icon.png
186
+ - test/dummy/public/favicon.ico
187
+ - test/dummy/storage/.keep
188
+ - test/dummy/tmp/.keep
189
+ - test/dummy/tmp/storage/.keep
190
+ - test/helpers/button_helper_test.rb
191
+ - test/key.pem
192
+ - test/models/identity_test.rb
193
+ - test/models/redirect_protector_test.rb
72
194
  - test/test_helper.rb
73
195
  homepage: https://github.com/basecamp/google_sign_in
74
196
  licenses:
@@ -95,5 +217,73 @@ signing_key:
95
217
  specification_version: 4
96
218
  summary: Sign in (or up) with Google for Rails applications
97
219
  test_files:
98
- - test/identity_test.rb
220
+ - test/certificate.pem
221
+ - test/controllers/authorizations_controller_test.rb
222
+ - test/controllers/callbacks_controller_test.rb
223
+ - test/dummy/.ruby-version
224
+ - test/dummy/Rakefile
225
+ - test/dummy/app/assets/config/manifest.js
226
+ - test/dummy/app/assets/images/.keep
227
+ - test/dummy/app/assets/javascripts/application.js
228
+ - test/dummy/app/assets/javascripts/cable.js
229
+ - test/dummy/app/assets/javascripts/channels/.keep
230
+ - test/dummy/app/assets/stylesheets/application.css
231
+ - test/dummy/app/channels/application_cable/channel.rb
232
+ - test/dummy/app/channels/application_cable/connection.rb
233
+ - test/dummy/app/controllers/application_controller.rb
234
+ - test/dummy/app/controllers/concerns/.keep
235
+ - test/dummy/app/helpers/application_helper.rb
236
+ - test/dummy/app/jobs/application_job.rb
237
+ - test/dummy/app/mailers/application_mailer.rb
238
+ - test/dummy/app/models/application_record.rb
239
+ - test/dummy/app/models/concerns/.keep
240
+ - test/dummy/app/views/layouts/application.html.erb
241
+ - test/dummy/app/views/layouts/mailer.html.erb
242
+ - test/dummy/app/views/layouts/mailer.text.erb
243
+ - test/dummy/bin/bundle
244
+ - test/dummy/bin/rails
245
+ - test/dummy/bin/rake
246
+ - test/dummy/bin/setup
247
+ - test/dummy/bin/update
248
+ - test/dummy/bin/yarn
249
+ - test/dummy/config.ru
250
+ - test/dummy/config/application.rb
251
+ - test/dummy/config/boot.rb
252
+ - test/dummy/config/cable.yml
253
+ - test/dummy/config/database.yml
254
+ - test/dummy/config/environment.rb
255
+ - test/dummy/config/environments/development.rb
256
+ - test/dummy/config/environments/production.rb
257
+ - test/dummy/config/environments/test.rb
258
+ - test/dummy/config/initializers/application_controller_renderer.rb
259
+ - test/dummy/config/initializers/backtrace_silencers.rb
260
+ - test/dummy/config/initializers/content_security_policy.rb
261
+ - test/dummy/config/initializers/cookies_serializer.rb
262
+ - test/dummy/config/initializers/filter_parameter_logging.rb
263
+ - test/dummy/config/initializers/google_sign_in.rb
264
+ - test/dummy/config/initializers/inflections.rb
265
+ - test/dummy/config/initializers/mime_types.rb
266
+ - test/dummy/config/initializers/wrap_parameters.rb
267
+ - test/dummy/config/locales/en.yml
268
+ - test/dummy/config/puma.rb
269
+ - test/dummy/config/routes.rb
270
+ - test/dummy/config/spring.rb
271
+ - test/dummy/config/storage.yml
272
+ - test/dummy/db/test.sqlite3
273
+ - test/dummy/lib/assets/.keep
274
+ - test/dummy/log/.keep
275
+ - test/dummy/package.json
276
+ - test/dummy/public/404.html
277
+ - test/dummy/public/422.html
278
+ - test/dummy/public/500.html
279
+ - test/dummy/public/apple-touch-icon-precomposed.png
280
+ - test/dummy/public/apple-touch-icon.png
281
+ - test/dummy/public/favicon.ico
282
+ - test/dummy/storage/.keep
283
+ - test/dummy/tmp/.keep
284
+ - test/dummy/tmp/storage/.keep
285
+ - test/helpers/button_helper_test.rb
286
+ - test/key.pem
287
+ - test/models/identity_test.rb
288
+ - test/models/redirect_protector_test.rb
99
289
  - test/test_helper.rb
@@ -1,76 +0,0 @@
1
- require 'google_sign_in/identity'
2
-
3
- module GoogleSignIn
4
- module Helper
5
- def google_sign_in(**form_options, &block)
6
- content_for :head,
7
- google_sign_in_javacript_include_tag +
8
- google_sign_in_client_id_meta_tag +
9
- turbolinks_reload_meta_tag
10
-
11
- google_sign_in_javascript_tag +
12
- google_sign_in_hidden_form_tag(**form_options) +
13
- google_sign_in_click_handler(&block)
14
- end
15
-
16
- private
17
- def google_sign_in_javacript_include_tag
18
- javascript_include_tag "https://apis.google.com/js/api.js", async: true, defer: true,
19
- onload: "this.onload=function(){};setupGoogleSignIn()",
20
- onreadystatechange: "if (this.readyState === 'complete') this.onload()"
21
- end
22
-
23
- def google_sign_in_client_id_meta_tag
24
- tag.meta name: "google-signin-client_id", content: GoogleSignIn::Identity.client_id
25
- end
26
-
27
- def turbolinks_reload_meta_tag
28
- tag.meta name: "turbolinks-visit-control", content: "reload"
29
- end
30
-
31
- def google_sign_in_hidden_form_tag(**options)
32
- options.reverse_merge!(html: { style: "display: none" })
33
-
34
- form_with(**options) do |form|
35
- form.hidden_field(:google_id_token, id: "google_sign_in_token") + form.submit(id: "google_sign_in_submit")
36
- end
37
- end
38
-
39
- def google_sign_in_click_handler(&block)
40
- tag.div(id: "google_sign_in_container", style: "visibility: hidden") { capture(&block) }
41
- end
42
-
43
- def google_sign_in_javascript_tag
44
- javascript_tag <<-JS.strip_heredoc
45
- (function() {
46
- function installAuthClient(callback) {
47
- gapi.load("client:auth2", function() {
48
- gapi.auth2.init().then(callback)
49
- })
50
- }
51
-
52
- function installClickHandler() {
53
- var element = document.getElementById("google_sign_in_container")
54
- var options = new gapi.auth2.SigninOptionsBuilder()
55
- options.setPrompt("select_account")
56
- gapi.auth2.getAuthInstance().attachClickHandler(element, options, handleSignIn)
57
- element.style.visibility = "visible"
58
- }
59
-
60
- function handleSignIn(googleUser) {
61
- var token = googleUser.getAuthResponse().id_token
62
- if (token) {
63
- document.getElementById("google_sign_in_token").value = token
64
- document.getElementById("google_sign_in_submit").click()
65
- gapi.auth2.getAuthInstance().signOut()
66
- }
67
- }
68
-
69
- window.setupGoogleSignIn = function() {
70
- installAuthClient(installClickHandler)
71
- }
72
- })()
73
- JS
74
- end
75
- end
76
- end
@@ -1,12 +0,0 @@
1
- require 'rails/railtie'
2
- require 'google_sign_in/helper'
3
-
4
- module GoogleSignIn
5
- class Engine < ::Rails::Engine
6
- initializer :google_sign_in do |app|
7
- ActiveSupport.on_load :action_controller do
8
- ActionController::Base.send :helper, GoogleSignIn::Helper
9
- end
10
- end
11
- end
12
- end
@@ -1,13 +0,0 @@
1
- require 'test_helper'
2
- require 'google_sign_in/identity'
3
-
4
- class GoogleSignIn::IdentityTest < ActiveSupport::TestCase
5
- test "client_id must be set" do
6
- GoogleSignIn::Identity.client_id = nil
7
- assert_raises(ArgumentError) { GoogleSignIn::Identity.new("some_fake_token") }
8
- end
9
-
10
- test "client_id must be part of the audience" do
11
- # FIXME: Need to build a mock/simulate the google token to test this
12
- end
13
- end