google_sign_in 0.1.4 → 1.0.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.
- checksums.yaml +4 -4
- data/.gitignore +12 -0
- data/.travis.yml +18 -0
- data/Gemfile.lock +130 -10
- data/README.md +114 -47
- data/Rakefile +31 -1
- data/SECURITY.md +15 -0
- data/app/controllers/google_sign_in/authorizations_controller.rb +17 -0
- data/app/controllers/google_sign_in/base_controller.rb +15 -0
- data/app/controllers/google_sign_in/callbacks_controller.rb +27 -0
- data/app/helpers/google_sign_in/button_helper.rb +7 -0
- data/bin/rails +16 -0
- data/config/routes.rb +4 -0
- data/google_sign_in.gemspec +9 -6
- data/lib/google_sign_in.rb +9 -1
- data/lib/google_sign_in/engine.rb +28 -0
- data/lib/google_sign_in/identity.rb +10 -21
- data/lib/google_sign_in/redirect_protector.rb +25 -0
- data/test/certificate.pem +19 -0
- data/test/controllers/authorizations_controller_test.rb +26 -0
- data/test/controllers/callbacks_controller_test.rb +36 -0
- data/test/dummy/.ruby-version +1 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/javascripts/cable.js +13 -0
- data/test/dummy/app/assets/javascripts/channels/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +36 -0
- data/test/dummy/bin/update +31 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +20 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +32 -0
- data/test/dummy/config/environments/production.rb +57 -0
- data/test/dummy/config/environments/test.rb +33 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +25 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/google_sign_in.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/package.json +5 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/helpers/button_helper_test.rb +36 -0
- data/test/key.pem +27 -0
- data/test/models/identity_test.rb +76 -0
- data/test/models/redirect_protector_test.rb +34 -0
- data/test/test_helper.rb +27 -3
- metadata +200 -10
- data/lib/google_sign_in/helper.rb +0 -76
- data/lib/google_sign_in/railtie.rb +0 -12
- data/test/identity_test.rb +0 -13
data/SECURITY.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Google-Sign in for Rails: Security
|
2
|
+
|
3
|
+
Security is of utmost importance in an authentication library like Google Sign-In for Rails. We at Basecamp use this plugin in our own apps, so we have a vested interest in investigating and mitigating all reported vulnerabilities. We welcome responsible security reviews and reports from our peers in the open-source software community and strive to acknowledge such valuable contributions.
|
4
|
+
|
5
|
+
|
6
|
+
## Reporting a vulnerability
|
7
|
+
|
8
|
+
Send urgent or sensitive reports to **<security@basecamp.com>**. If necessary, use our [public key] to protect your message and provide us with a secure way to respond. We’ll get back to you as soon as we can—usually within one business day. Please follow up or [ping us on Twitter][twitter] if you don’t hear back. For non-urgent or non-sensitive requests, please contact our [support team][support].
|
9
|
+
|
10
|
+
Read more about our security response policy [on our website][policy].
|
11
|
+
|
12
|
+
[public key]: https://basecamp.com/about/policies/security/Basecamp-security.pub
|
13
|
+
[twitter]: https://twitter.com/basecamp
|
14
|
+
[support]: https://basecamp.com/support
|
15
|
+
[policy]: https://basecamp.com/about/policies/security/response
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class GoogleSignIn::AuthorizationsController < GoogleSignIn::BaseController
|
4
|
+
def create
|
5
|
+
redirect_to login_url(scope: 'openid profile email', state: state),
|
6
|
+
flash: { proceed_to: params.require(:proceed_to), state: state }
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def login_url(**params)
|
11
|
+
client.auth_code.authorize_url(prompt: 'login', **params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def state
|
15
|
+
@state ||= SecureRandom.base64(16)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'oauth2'
|
2
|
+
|
3
|
+
class GoogleSignIn::BaseController < ActionController::Base
|
4
|
+
protect_from_forgery with: :exception
|
5
|
+
|
6
|
+
private
|
7
|
+
def client
|
8
|
+
@client ||= OAuth2::Client.new \
|
9
|
+
GoogleSignIn.client_id,
|
10
|
+
GoogleSignIn.client_secret,
|
11
|
+
authorize_url: 'https://accounts.google.com/o/oauth2/auth',
|
12
|
+
token_url: 'https://www.googleapis.com/oauth2/v3/token',
|
13
|
+
redirect_uri: callback_url
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_dependency 'google_sign_in/redirect_protector'
|
2
|
+
|
3
|
+
class GoogleSignIn::CallbacksController < GoogleSignIn::BaseController
|
4
|
+
def show
|
5
|
+
if valid_request?
|
6
|
+
redirect_to proceed_to_url, flash: { google_sign_in_token: id_token }
|
7
|
+
else
|
8
|
+
head :unprocessable_entity
|
9
|
+
end
|
10
|
+
rescue GoogleSignIn::RedirectProtector::Violation => error
|
11
|
+
logger.error error.message
|
12
|
+
head :bad_request
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def valid_request?
|
17
|
+
flash[:state].present? && params.require(:state) == flash[:state]
|
18
|
+
end
|
19
|
+
|
20
|
+
def proceed_to_url
|
21
|
+
flash[:proceed_to].tap { |url| GoogleSignIn::RedirectProtector.ensure_same_origin(url, request.url) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def id_token
|
25
|
+
client.auth_code.get_token(params.require(:code))['id_token']
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module GoogleSignIn::ButtonHelper
|
2
|
+
def google_sign_in_button(text = nil, proceed_to:, **options, &block)
|
3
|
+
form_with url: google_sign_in.authorization_path do
|
4
|
+
hidden_field_tag(:proceed_to, proceed_to, id: nil) + button_tag(text, name: nil, **options, &block)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
3
|
+
# installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/blorgh/engine', __dir__)
|
7
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
8
|
+
|
9
|
+
# Set up gems listed in the Gemfile.
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
11
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
12
|
+
|
13
|
+
require 'rails'
|
14
|
+
require 'action_controller/railtie'
|
15
|
+
require 'rails/test_unit/railtie'
|
16
|
+
require 'rails/engine/commands'
|
data/config/routes.rb
ADDED
data/google_sign_in.gemspec
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'google_sign_in'
|
3
|
-
s.version = '0.
|
4
|
-
s.authors = 'David Heinemeier Hansson'
|
5
|
-
s.email = 'david@basecamp.com'
|
3
|
+
s.version = '1.0.0'
|
4
|
+
s.authors = ['David Heinemeier Hansson', 'George Claghorn']
|
5
|
+
s.email = ['david@basecamp.com', 'george@basecamp.com']
|
6
6
|
s.summary = 'Sign in (or up) with Google for Rails applications'
|
7
7
|
s.homepage = 'https://github.com/basecamp/google_sign_in'
|
8
8
|
s.license = 'MIT'
|
9
9
|
|
10
10
|
s.required_ruby_version = '>= 1.9.3'
|
11
11
|
|
12
|
-
s.add_dependency '
|
12
|
+
s.add_dependency 'rails', '>= 5.2.0'
|
13
13
|
s.add_dependency 'google-id-token', '>= 1.4.0'
|
14
|
+
s.add_dependency 'oauth2', '>= 1.4.0'
|
14
15
|
|
15
16
|
s.add_development_dependency 'bundler', '~> 1.15'
|
17
|
+
s.add_development_dependency 'jwt'
|
18
|
+
s.add_development_dependency 'webmock'
|
16
19
|
|
17
|
-
s.files
|
18
|
-
s.test_files
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
19
22
|
end
|
data/lib/google_sign_in.rb
CHANGED
@@ -1,2 +1,10 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/rails'
|
3
|
+
|
4
|
+
module GoogleSignIn
|
5
|
+
mattr_accessor :client_id
|
6
|
+
mattr_accessor :client_secret
|
7
|
+
end
|
8
|
+
|
1
9
|
require 'google_sign_in/identity'
|
2
|
-
require 'google_sign_in/
|
10
|
+
require 'google_sign_in/engine' if defined?(Rails)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rails/engine'
|
2
|
+
|
3
|
+
module GoogleSignIn
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace GoogleSignIn
|
6
|
+
|
7
|
+
config.google_sign_in = ActiveSupport::OrderedOptions.new
|
8
|
+
|
9
|
+
initializer 'google_sign_in.config' do |app|
|
10
|
+
config.after_initialize do
|
11
|
+
GoogleSignIn.client_id = config.google_sign_in.client_id || app.credentials.dig(:google_sign_in, :client_id)
|
12
|
+
GoogleSignIn.client_secret = config.google_sign_in.client_secret || app.credentials.dig(:google_sign_in, :client_secret)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer 'google_sign_in.helpers' do
|
17
|
+
ActiveSupport.on_load :action_controller do
|
18
|
+
ActionController::Base.helper GoogleSignIn::Engine.helpers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
initializer 'google_sign_in.mount' do |app|
|
23
|
+
app.routes.append do
|
24
|
+
mount GoogleSignIn::Engine, at: app.config.google_sign_in.root || 'google_sign_in'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,21 +1,15 @@
|
|
1
1
|
require 'google-id-token'
|
2
|
-
require 'active_support/core_ext/
|
3
|
-
require 'active_support/core_ext/numeric/time'
|
2
|
+
require 'active_support/core_ext/module/delegation'
|
4
3
|
|
5
4
|
module GoogleSignIn
|
6
5
|
class Identity
|
7
|
-
|
6
|
+
class ValidationError < StandardError; end
|
8
7
|
|
9
|
-
class_attribute :
|
10
|
-
self.token_expiry = 5.minutes
|
11
|
-
|
12
|
-
class_attribute :logger
|
13
|
-
self.logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
8
|
+
class_attribute :validator, default: GoogleIDToken::Validator.new
|
14
9
|
|
15
10
|
def initialize(token)
|
16
11
|
ensure_client_id_present
|
17
12
|
set_extracted_payload(token)
|
18
|
-
ensure_proper_audience
|
19
13
|
end
|
20
14
|
|
21
15
|
def user_id
|
@@ -31,7 +25,7 @@ module GoogleSignIn
|
|
31
25
|
end
|
32
26
|
|
33
27
|
def email_verified?
|
34
|
-
@payload["email_verified"] ==
|
28
|
+
@payload["email_verified"] == true
|
35
29
|
end
|
36
30
|
|
37
31
|
def avatar_url
|
@@ -43,6 +37,8 @@ module GoogleSignIn
|
|
43
37
|
end
|
44
38
|
|
45
39
|
private
|
40
|
+
delegate :client_id, to: GoogleSignIn
|
41
|
+
|
46
42
|
def ensure_client_id_present
|
47
43
|
if client_id.blank?
|
48
44
|
raise ArgumentError, "GoogleSignIn.client_id must be set to validate identity"
|
@@ -50,16 +46,9 @@ module GoogleSignIn
|
|
50
46
|
end
|
51
47
|
|
52
48
|
def set_extracted_payload(token)
|
53
|
-
@payload =
|
54
|
-
rescue GoogleIDToken::ValidationError =>
|
55
|
-
|
56
|
-
@payload = {}
|
57
|
-
end
|
58
|
-
|
59
|
-
def ensure_proper_audience
|
60
|
-
unless @payload["aud"].include?(client_id)
|
61
|
-
raise "Failed to locate the client_id #{client_id} in the authorized audience (#{@payload["aud"]})"
|
62
|
-
end
|
49
|
+
@payload = validator.check(token, client_id)
|
50
|
+
rescue GoogleIDToken::ValidationError => error
|
51
|
+
raise ValidationError, error.message
|
63
52
|
end
|
64
53
|
end
|
65
|
-
end
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module GoogleSignIn
|
4
|
+
module RedirectProtector
|
5
|
+
extend self
|
6
|
+
|
7
|
+
class Violation < StandardError; end
|
8
|
+
|
9
|
+
QUALIFIED_URL_PATTERN = /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
|
10
|
+
|
11
|
+
def ensure_same_origin(target, source)
|
12
|
+
if target =~ QUALIFIED_URL_PATTERN && origin_of(target) != origin_of(source)
|
13
|
+
raise Violation, "Redirect target #{target} does not have same origin as request (expected #{origin_of(source)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def origin_of(url)
|
19
|
+
uri = URI(url)
|
20
|
+
"#{uri.scheme}://#{uri.host}:#{uri.port}"
|
21
|
+
rescue ArgumentError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDETCCAfmgAwIBAQIBADANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDDCRnb29n
|
3
|
+
bGUtc2lnbi1pbi1mb3ItcmFpbHMuZXhhbXBsZS5jb20wHhcNMTgwOTAyMjI0MTQw
|
4
|
+
WhcNMjMwOTAyMjI0MTQwWjAvMS0wKwYDVQQDDCRnb29nbGUtc2lnbi1pbi1mb3It
|
5
|
+
cmFpbHMuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
6
|
+
AQCtnO1OcLbdxj4f6I/aUMJkCJfrDNvp0v2ljUJoaq6hqWPmoZgcl92njBvt91np
|
7
|
+
JPfGaCy0ZYLfizUNBRKkfo6u3MXvubktYqk3SVCejy0TUE11PwRx1u/x0c2rCTa6
|
8
|
+
Y7ppAoO9Ur3yoccDmkceP8MpofHWetrdaxyhktlqy6gpM7V+kjj+anySQk4XqDJl
|
9
|
+
F4FXHt82HMNK3xbjXJyEoyMudGUISBDn/rG8b3LxEKawUiLVCI54g3+L/Oi4nZCE
|
10
|
+
XNCd/mvWpVFoPpFQGMoKW3S9KxFowvfkDSxWYwgYnWnsO0ueXS+WYML88KO1Qf7V
|
11
|
+
mEZ91u7w1/EiMVxiuczxycSfAgMBAAGjODA2MAwGA1UdEwEB/wQCMAAwDgYDVR0P
|
12
|
+
AQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUA
|
13
|
+
A4IBAQCVXynTCZhpbINC4j1prGaPfY6mRkCZzcRpQFim4C6hJtYSRn57qjpmYWG3
|
14
|
+
eVc3ElfNUAWgC3trACjN3hDqKv0/hH9TGTY9iFhc747L/VSaKWzH/uWewj1qTwsX
|
15
|
+
dUEFxZILAWvAMBNUT060t8bt+pSFc2h4fHsftOqFLfkFUcCr22QsWyueXzWZyDeZ
|
16
|
+
XWFGtD+WOR5SC4mIY359e75/vZsJymzZIfM+pfcaHnXtXez9SeLM81rvnRdR1b+H
|
17
|
+
/S0LT0dPRkXSvC2HRPwzHxVctNrDoaxON+OIMgd4lHAFs6doVoYmnprzO69+IBUK
|
18
|
+
s0LBENxbn2rd7IEl6EaC91cXCl3y
|
19
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GoogleSignIn::AuthorizationsControllerTest < ActionDispatch::IntegrationTest
|
4
|
+
test "redirecting to Google for authorization" do
|
5
|
+
post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
|
6
|
+
assert_response :redirect
|
7
|
+
assert_match 'https://accounts.google.com/o/oauth2/auth', response.location
|
8
|
+
|
9
|
+
params = extract_query_params_from(response.location)
|
10
|
+
assert_equal FAKE_GOOGLE_CLIENT_ID, params[:client_id]
|
11
|
+
assert_equal 'login', params[:prompt]
|
12
|
+
assert_equal 'code', params[:response_type]
|
13
|
+
assert_equal 'http://www.example.com/google_sign_in/callback', params[:redirect_uri]
|
14
|
+
assert_equal 'openid profile email', params[:scope]
|
15
|
+
assert_match /[A-Za-z0-9+\/]{22}==/, params[:state]
|
16
|
+
|
17
|
+
assert_equal 'http://www.example.com/login', flash[:proceed_to]
|
18
|
+
assert_equal params[:state], flash[:state]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def extract_query_params_from(url)
|
23
|
+
query = URI(url).query
|
24
|
+
Rack::Utils.parse_query(query).symbolize_keys
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GoogleSignIn::CallbacksControllerTest < ActionDispatch::IntegrationTest
|
4
|
+
test "receiving an authorization code" do
|
5
|
+
post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
|
6
|
+
assert_response :redirect
|
7
|
+
|
8
|
+
stub_token_request code: '4/SgCpHSVW5-Cy', access_token: 'ya29.GlwIBo', id_token: 'eyJhbGciOiJSUzI'
|
9
|
+
|
10
|
+
get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state])
|
11
|
+
assert_redirected_to 'http://www.example.com/login'
|
12
|
+
assert_equal 'eyJhbGciOiJSUzI', flash[:google_sign_in_token]
|
13
|
+
end
|
14
|
+
|
15
|
+
test "protecting against CSRF" do
|
16
|
+
get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: 'invalid')
|
17
|
+
assert_response :unprocessable_entity
|
18
|
+
end
|
19
|
+
|
20
|
+
test "protecting against open redirects" do
|
21
|
+
post google_sign_in.authorization_url, params: { proceed_to: 'http://malicious.example.com/login' }
|
22
|
+
assert_response :redirect
|
23
|
+
|
24
|
+
get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state])
|
25
|
+
assert_response :bad_request
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def stub_token_request(code:, **params)
|
30
|
+
stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token').
|
31
|
+
with(body: { grant_type: 'authorization_code', code: code,
|
32
|
+
client_id: FAKE_GOOGLE_CLIENT_ID, client_secret: FAKE_GOOGLE_CLIENT_SECRET,
|
33
|
+
redirect_uri: 'http://www.example.com/google_sign_in/callback' }).
|
34
|
+
to_return(status: 200, headers: { 'Content-Type' => 'application/json' }, body: JSON.generate(params))
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
2.5.0
|
data/test/dummy/Rakefile
ADDED
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require rails-ujs
|
14
|
+
//= require activestorage
|
15
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// Action Cable provides the framework to deal with WebSockets in Rails.
|
2
|
+
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
|
3
|
+
//
|
4
|
+
//= require action_cable
|
5
|
+
//= require_self
|
6
|
+
//= require_tree ./channels
|
7
|
+
|
8
|
+
(function() {
|
9
|
+
this.App || (this.App = {});
|
10
|
+
|
11
|
+
App.cable = ActionCable.createConsumer();
|
12
|
+
|
13
|
+
}).call(this);
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
File without changes
|