simple_google_auth 0.3.0 → 0.3.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8dd8d1cf6e697eb5d635b3118923e4f916ca5de03847c78143fb7ce8ece9c8c5
4
- data.tar.gz: 61cf025ca623785b3c150f79c5035eabd5b4606e5e95f47a48affd0bc090d790
3
+ metadata.gz: b0cddb7c5d88d861d18686ee5601d635e94f133d51863fbfe50f9aadf46c8664
4
+ data.tar.gz: 19728334ae4996d63a0547f3ec9637ee20234fc8083a95355b734b5358809c64
5
5
  SHA512:
6
- metadata.gz: 1c4a1060d4eda6cc1bdf1acc2609e40766019254c8165d756495bb145b253f3bec4ab769a69656ac6f0af46ec93330491527da2a2611ad86bead167fa381243b
7
- data.tar.gz: 870f90edbed191d998ffb364cf474a40776c436ea4ec8c25b33c5f27881edfa3c60f6f86d2e07981c362112658b9d1548155898daa1ab3eda495dcb82e0513d5
6
+ metadata.gz: 990eb2a0608217e84f01a2e5cfd5273773e758c64581930563cee30b38deae2f4becae0bf070f7edf2d3550d4b8e4ca10f793d7be144ef418f41ea8c915c21d6
7
+ data.tar.gz: 721625b234e2c0b937dca66e38bec0ce8ac5c087237d57a46831404112110f22f46e06934768c7bbb22837d829546a0868a06396d400fc37bda14049cf94e517
@@ -11,10 +11,11 @@ module SimpleGoogleAuth
11
11
  data = AuthDataPresenter.new(auth_data)
12
12
  raise Error, "Authentication failed" unless config.authenticate.call(data)
13
13
 
14
+ renew_session(request)
14
15
  request.session[config.data_session_key_name] = auth_data
15
16
 
16
17
  path = config.authentication_uri_state_path_extractor.call(request.session[config.state_session_key_name])
17
- path = "/" if path.blank?
18
+ path = "/" unless safe_redirect_path?(path)
18
19
  [302, {"Location" => path}, [" "]]
19
20
 
20
21
  rescue Error => e
@@ -26,7 +27,9 @@ module SimpleGoogleAuth
26
27
 
27
28
  protected
28
29
  def ensure_params_are_correct(request, config)
29
- if request.params["state"] != request.session[config.state_session_key_name]
30
+ expected_state = request.session[config.state_session_key_name]
31
+
32
+ if expected_state.blank? || !states_match?(request.params["state"], expected_state)
30
33
  raise Error, "Invalid state returned from Google"
31
34
  elsif request.params["error"]
32
35
  raise Error, "Authentication failed: #{request.params["error"]}"
@@ -34,5 +37,30 @@ module SimpleGoogleAuth
34
37
  raise Error, "No authentication code returned"
35
38
  end
36
39
  end
40
+
41
+ private
42
+
43
+ # Constant-time comparison so the CSRF state token isn't leaked via timing.
44
+ # A blank expected state is rejected by the caller before we get here.
45
+ def states_match?(actual, expected)
46
+ return false if actual.nil?
47
+ ActiveSupport::SecurityUtils.secure_compare(actual.to_s, expected.to_s)
48
+ end
49
+
50
+ # Only redirect back to a path on our own host. A safe path starts with a
51
+ # single "/" that isn't followed by another "/" or "\" -- both of which a
52
+ # browser can resolve to a different origin ("//evil.com", "/\evil.com").
53
+ # Written as an allowlist so we don't have to chase every dangerous form.
54
+ def safe_redirect_path?(path)
55
+ path = path.to_s
56
+ path.start_with?("/") && !path.start_with?("//", "/\\")
57
+ end
58
+
59
+ # Rotate the session on successful login to defend against session fixation.
60
+ # No-op on session stores that don't expose options (e.g. in unit tests).
61
+ def renew_session(request)
62
+ session = request.session
63
+ session.options[:renew] = true if session.respond_to?(:options) && session.options
64
+ end
37
65
  end
38
66
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleGoogleAuth
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -14,7 +14,7 @@ describe SimpleGoogleAuth::HttpClient do
14
14
  expect(http).to receive(:request).with(request).and_return(response)
15
15
 
16
16
  expect(Net::HTTP::Post).to receive(:new).with("/somepath").and_return(request)
17
- expect(request).to receive(:set_form_data).with('some' => 'data')
17
+ expect(request).to receive(:set_form_data).with({'some' => 'data'})
18
18
  end
19
19
 
20
20
  subject { SimpleGoogleAuth::HttpClient.new("https://some.host/somepath", open_timeout: 12, read_timeout: 13) }
@@ -44,6 +44,18 @@ describe SimpleGoogleAuth::Receiver do
44
44
 
45
45
  expect(subject).to eq [302, {"Location" => "/place"}, [" "]]
46
46
  end
47
+
48
+ it "does not follow a protocol-relative redirect path (open redirect)" do
49
+ expect(authentication_uri_state_path_extractor).to receive(:call).with(state).and_return('//evil.com')
50
+
51
+ expect(subject).to eq [302, {"Location" => "/"}, [" "]]
52
+ end
53
+
54
+ it "does not follow a backslash-prefixed redirect path (open redirect)" do
55
+ expect(authentication_uri_state_path_extractor).to receive(:call).with(state).and_return('/\\evil.com')
56
+
57
+ expect(subject).to eq [302, {"Location" => "/"}, [" "]]
58
+ end
47
59
  end
48
60
 
49
61
  context "and the authenticator rejects the login" do
@@ -63,6 +75,15 @@ describe SimpleGoogleAuth::Receiver do
63
75
  end
64
76
  end
65
77
 
78
+ context "when the session holds no state and the callback omits it (forged callback)" do
79
+ let(:state) { nil }
80
+ let(:params) { {"code" => code} }
81
+
82
+ it "rejects the login rather than treating two blank states as a match" do
83
+ expect(subject).to eq [302, {"Location" => "/error?message=Invalid+state+returned+from+Google"}, [" "]]
84
+ end
85
+ end
86
+
66
87
  context "when the google authentication fails" do
67
88
  let(:params) { {"state" => state, "error" => "bad stuff"} }
68
89
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_google_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roger Nesbitt
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-04 00:00:00.000000000 Z
11
+ date: 2026-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.9'
33
+ version: '6.1'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.9'
40
+ version: '6.1'
41
41
  description: An extremely easy way to protect your site by requiring Google logins
42
42
  without having to set up a traditional authentication system
43
43
  email:
@@ -73,7 +73,7 @@ homepage: https://github.com/mogest/simple_google_auth
73
73
  licenses:
74
74
  - MIT
75
75
  metadata: {}
76
- post_install_message:
76
+ post_install_message:
77
77
  rdoc_options: []
78
78
  require_paths:
79
79
  - lib
@@ -88,18 +88,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.7.6
93
- signing_key:
91
+ rubygems_version: 3.5.22
92
+ signing_key:
94
93
  specification_version: 4
95
94
  summary: Super simple Google authentication for your Rails site
96
95
  test_files:
97
- - spec/spec_helper.rb
98
- - spec/simple_google_auth/http_client_spec.rb
99
- - spec/simple_google_auth/config_spec.rb
100
96
  - spec/simple_google_auth/auth_data_presenter_spec.rb
101
- - spec/simple_google_auth/oauth_spec.rb
102
- - spec/simple_google_auth/receiver_spec.rb
103
97
  - spec/simple_google_auth/authorization_uri_builder_spec.rb
98
+ - spec/simple_google_auth/config_spec.rb
104
99
  - spec/simple_google_auth/controller_spec.rb
100
+ - spec/simple_google_auth/http_client_spec.rb
101
+ - spec/simple_google_auth/oauth_spec.rb
102
+ - spec/simple_google_auth/receiver_spec.rb
105
103
  - spec/simple_google_auth_spec.rb
104
+ - spec/spec_helper.rb