google_sign_in 1.1.2 → 1.2.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.
Potentially problematic release.
This version of google_sign_in might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/Gemfile.lock +5 -5
- data/README.md +21 -3
- data/app/controllers/google_sign_in/authorizations_controller.rb +2 -0
- data/app/controllers/google_sign_in/base_controller.rb +1 -1
- data/app/controllers/google_sign_in/callbacks_controller.rb +20 -10
- data/bin/rails +1 -1
- data/google_sign_in.gemspec +1 -1
- data/lib/google_sign_in.rb +25 -0
- data/lib/google_sign_in/identity.rb +8 -0
- data/lib/google_sign_in/redirect_protector.rb +2 -2
- data/test/controllers/callbacks_controller_test.rb +107 -10
- data/test/models/identity_test.rb +8 -0
- data/test/models/redirect_protector_test.rb +6 -0
- metadata +3 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 5831caee2b3640fd7ea360cffe55c7941066ace8c22e324ed389215c0cd7c65a
         | 
| 4 | 
            +
              data.tar.gz: 548b00fa1ad0739b039bed3ee80adfa2ee938a841737b5daef5d8eb5a5e11258
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 5f4d62b9a5b2a3ea7d56b643f53ce4f4ee14cf621c4ca54c68d8da546dd2a38f5844ba8516f63e33fab4d69e716791863b4075adedd2dbfcbd7e638c58faf26c
         | 
| 7 | 
            +
              data.tar.gz: d9e6e95a67eaf3ac07fc06361ede928f9e046e04f37806e0f63d937b5ff21db2380597aa9bf438dc426053586aa51cef15e2f904b97ee0639753bcb1c818e389
         | 
    
        data/.travis.yml
    CHANGED
    
    | @@ -2,14 +2,14 @@ language: ruby | |
| 2 2 | 
             
            sudo: false
         | 
| 3 3 | 
             
            cache: bundler
         | 
| 4 4 |  | 
| 5 | 
            -
            # Bundler/RubyGems incompat on Ruby 2.5.0
         | 
| 6 | 
            -
            before_install: gem install bundler
         | 
| 5 | 
            +
            # Bundler/RubyGems incompat on Ruby 2.5.0 and 2.6.1
         | 
| 6 | 
            +
            before_install: gem update --system && gem install bundler -v 1.17.3
         | 
| 7 7 |  | 
| 8 8 | 
             
            rvm:
         | 
| 9 | 
            -
              - 2.2
         | 
| 10 9 | 
             
              - 2.3
         | 
| 11 10 | 
             
              - 2.4
         | 
| 12 11 | 
             
              - 2.5
         | 
| 12 | 
            +
              - 2.6
         | 
| 13 13 | 
             
              - ruby-head
         | 
| 14 14 |  | 
| 15 15 | 
             
            matrix:
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -80,14 +80,14 @@ GEM | |
| 80 80 | 
             
                method_source (0.9.0)
         | 
| 81 81 | 
             
                mimemagic (0.3.2)
         | 
| 82 82 | 
             
                mini_mime (1.0.1)
         | 
| 83 | 
            -
                mini_portile2 (2. | 
| 83 | 
            +
                mini_portile2 (2.4.0)
         | 
| 84 84 | 
             
                minitest (5.11.3)
         | 
| 85 85 | 
             
                multi_json (1.13.1)
         | 
| 86 86 | 
             
                multi_xml (0.6.0)
         | 
| 87 87 | 
             
                multipart-post (2.0.0)
         | 
| 88 88 | 
             
                nio4r (2.3.1)
         | 
| 89 | 
            -
                nokogiri (1.8 | 
| 90 | 
            -
                  mini_portile2 (~> 2. | 
| 89 | 
            +
                nokogiri (1.10.8)
         | 
| 90 | 
            +
                  mini_portile2 (~> 2.4.0)
         | 
| 91 91 | 
             
                oauth2 (1.4.1)
         | 
| 92 92 | 
             
                  faraday (>= 0.8, < 0.16.0)
         | 
| 93 93 | 
             
                  jwt (>= 1.0, < 3.0)
         | 
| @@ -122,7 +122,7 @@ GEM | |
| 122 122 | 
             
                  method_source
         | 
| 123 123 | 
             
                  rake (>= 0.8.7)
         | 
| 124 124 | 
             
                  thor (>= 0.19.0, < 2.0)
         | 
| 125 | 
            -
                rake (12. | 
| 125 | 
            +
                rake (12.3.3)
         | 
| 126 126 | 
             
                safe_yaml (1.0.4)
         | 
| 127 127 | 
             
                sprockets (3.7.2)
         | 
| 128 128 | 
             
                  concurrent-ruby (~> 1.0)
         | 
| @@ -155,4 +155,4 @@ DEPENDENCIES | |
| 155 155 | 
             
              webmock (>= 3.4.2)
         | 
| 156 156 |  | 
| 157 157 | 
             
            BUNDLED WITH
         | 
| 158 | 
            -
               1. | 
| 158 | 
            +
               1.17.2
         | 
    
        data/README.md
    CHANGED
    
    | @@ -64,6 +64,16 @@ end | |
| 64 64 |  | 
| 65 65 | 
             
            **⚠️ Important:** Take care to protect your client secret from disclosure to third parties.
         | 
| 66 66 |  | 
| 67 | 
            +
            9. (Optional) The callback route can be configured using:
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            ```ruby
         | 
| 70 | 
            +
            # config/initializers/google_sign_in.rb
         | 
| 71 | 
            +
            Rails.application.configure do
         | 
| 72 | 
            +
              config.google_sign_in.root = "my_own/google_sign_in_route"
         | 
| 73 | 
            +
            end
         | 
| 74 | 
            +
            ```
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            Which would make the callback `/my_own/google_sign_in_route/callback`.
         | 
| 67 77 |  | 
| 68 78 | 
             
            ## Usage
         | 
| 69 79 |  | 
| @@ -80,7 +90,8 @@ This gem provides a `google_sign_in_button` helper. It generates a button which | |
| 80 90 | 
             
            ```
         | 
| 81 91 |  | 
| 82 92 | 
             
            The `proceed_to` argument is required. After authenticating with Google, the gem redirects to `proceed_to`, providing
         | 
| 83 | 
            -
            a Google ID token in `flash[: | 
| 93 | 
            +
            a Google ID token in `flash[:google_sign_in][:id_token]` or an [OAuth authorizaton code grant error](https://tools.ietf.org/html/rfc6749#section-4.1.2.1)
         | 
| 94 | 
            +
            in `flash[:google_sign_in][:error]`. Your application decides what to do with it:
         | 
| 84 95 |  | 
| 85 96 | 
             
            ```ruby
         | 
| 86 97 | 
             
            # config/routes.rb
         | 
| @@ -108,8 +119,11 @@ class LoginsController < ApplicationController | |
| 108 119 |  | 
| 109 120 | 
             
              private
         | 
| 110 121 | 
             
                def authenticate_with_google
         | 
| 111 | 
            -
                  if flash[: | 
| 112 | 
            -
                    User.find_by google_id: GoogleSignIn::Identity.new( | 
| 122 | 
            +
                  if id_token = flash[:google_sign_in][:id_token]
         | 
| 123 | 
            +
                    User.find_by google_id: GoogleSignIn::Identity.new(id_token).user_id
         | 
| 124 | 
            +
                  elsif error = flash[:google_sign_in][:error]
         | 
| 125 | 
            +
                    logger.error "Google authentication error: #{error}"
         | 
| 126 | 
            +
                    nil
         | 
| 113 127 | 
             
                  end
         | 
| 114 128 | 
             
                end
         | 
| 115 129 | 
             
            end
         | 
| @@ -143,6 +157,10 @@ information contained in the token via the following instance methods: | |
| 143 157 |  | 
| 144 158 | 
             
            * `hosted_domain`: The user’s hosted G Suite domain, provided only if they belong to a G Suite.
         | 
| 145 159 |  | 
| 160 | 
            +
            * `given_name`: The user's given name.
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            * `last_name`: The user's last name.
         | 
| 163 | 
            +
             | 
| 146 164 |  | 
| 147 165 | 
             
            ## Security
         | 
| 148 166 |  | 
| @@ -1,6 +1,8 @@ | |
| 1 1 | 
             
            require 'securerandom'
         | 
| 2 2 |  | 
| 3 3 | 
             
            class GoogleSignIn::AuthorizationsController < GoogleSignIn::BaseController
         | 
| 4 | 
            +
              skip_forgery_protection only: :create
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
              def create
         | 
| 5 7 | 
             
                redirect_to login_url(scope: 'openid profile email', state: state),
         | 
| 6 8 | 
             
                  flash: { proceed_to: params.require(:proceed_to), state: state }
         | 
| @@ -9,7 +9,7 @@ class GoogleSignIn::BaseController < ActionController::Base | |
| 9 9 | 
             
                    GoogleSignIn.client_id,
         | 
| 10 10 | 
             
                    GoogleSignIn.client_secret,
         | 
| 11 11 | 
             
                    authorize_url: 'https://accounts.google.com/o/oauth2/auth',
         | 
| 12 | 
            -
                    token_url: 'https:// | 
| 12 | 
            +
                    token_url: 'https://oauth2.googleapis.com/token',
         | 
| 13 13 | 
             
                    redirect_uri: callback_url
         | 
| 14 14 | 
             
                end
         | 
| 15 15 | 
             
            end
         | 
| @@ -2,26 +2,36 @@ require_dependency 'google_sign_in/redirect_protector' | |
| 2 2 |  | 
| 3 3 | 
             
            class GoogleSignIn::CallbacksController < GoogleSignIn::BaseController
         | 
| 4 4 | 
             
              def show
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  redirect_to proceed_to_url, flash: { google_sign_in_token: id_token }
         | 
| 7 | 
            -
                else
         | 
| 8 | 
            -
                  head :unprocessable_entity
         | 
| 9 | 
            -
                end
         | 
| 5 | 
            +
                redirect_to proceed_to_url, flash: { google_sign_in: google_sign_in_response }
         | 
| 10 6 | 
             
              rescue GoogleSignIn::RedirectProtector::Violation => error
         | 
| 11 7 | 
             
                logger.error error.message
         | 
| 12 8 | 
             
                head :bad_request
         | 
| 13 9 | 
             
              end
         | 
| 14 10 |  | 
| 15 11 | 
             
              private
         | 
| 16 | 
            -
                def valid_request?
         | 
| 17 | 
            -
                  flash[:state].present? && params.require(:state) == flash[:state]
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
             | 
| 20 12 | 
             
                def proceed_to_url
         | 
| 21 13 | 
             
                  flash[:proceed_to].tap { |url| GoogleSignIn::RedirectProtector.ensure_same_origin(url, request.url) }
         | 
| 22 14 | 
             
                end
         | 
| 23 15 |  | 
| 16 | 
            +
                def google_sign_in_response
         | 
| 17 | 
            +
                  if valid_request? && params[:code].present?
         | 
| 18 | 
            +
                    { id_token: id_token }
         | 
| 19 | 
            +
                  else
         | 
| 20 | 
            +
                    { error: error_message_for(params[:error]) }
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                rescue OAuth2::Error => error
         | 
| 23 | 
            +
                  { error: error_message_for(error.code) }
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def valid_request?
         | 
| 27 | 
            +
                  flash[:state].present? && params[:state] == flash[:state]
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 24 30 | 
             
                def id_token
         | 
| 25 | 
            -
                  client.auth_code.get_token(params | 
| 31 | 
            +
                  client.auth_code.get_token(params[:code])['id_token']
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def error_message_for(error_code)
         | 
| 35 | 
            +
                  error_code.presence_in(GoogleSignIn::OAUTH2_ERRORS) || "invalid_request"
         | 
| 26 36 | 
             
                end
         | 
| 27 37 | 
             
            end
         | 
    
        data/bin/rails
    CHANGED
    
    | @@ -3,7 +3,7 @@ | |
| 3 3 | 
             
            # installed from the root of your application.
         | 
| 4 4 |  | 
| 5 5 | 
             
            ENGINE_ROOT = File.expand_path('..', __dir__)
         | 
| 6 | 
            -
            ENGINE_PATH = File.expand_path('../lib/ | 
| 6 | 
            +
            ENGINE_PATH = File.expand_path('../lib/google_sign_in/engine', __dir__)
         | 
| 7 7 | 
             
            APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
         | 
| 8 8 |  | 
| 9 9 | 
             
            # Set up gems listed in the Gemfile.
         | 
    
        data/google_sign_in.gemspec
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Gem::Specification.new do |s|
         | 
| 2 2 | 
             
              s.name     = 'google_sign_in'
         | 
| 3 | 
            -
              s.version  = '1. | 
| 3 | 
            +
              s.version  = '1.2.0'
         | 
| 4 4 | 
             
              s.authors  = ['David Heinemeier Hansson', 'George Claghorn']
         | 
| 5 5 | 
             
              s.email    = ['david@basecamp.com', 'george@basecamp.com']
         | 
| 6 6 | 
             
              s.summary  = 'Sign in (or up) with Google for Rails applications'
         | 
    
        data/lib/google_sign_in.rb
    CHANGED
    
    | @@ -4,6 +4,31 @@ require 'active_support/rails' | |
| 4 4 | 
             
            module GoogleSignIn
         | 
| 5 5 | 
             
              mattr_accessor :client_id
         | 
| 6 6 | 
             
              mattr_accessor :client_secret
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              # https://tools.ietf.org/html/rfc6749#section-4.1.2.1
         | 
| 9 | 
            +
              authorization_request_errors = %w[
         | 
| 10 | 
            +
                invalid_request
         | 
| 11 | 
            +
                unauthorized_client
         | 
| 12 | 
            +
                access_denied
         | 
| 13 | 
            +
                unsupported_response_type
         | 
| 14 | 
            +
                invalid_scope
         | 
| 15 | 
            +
                server_error
         | 
| 16 | 
            +
                temporarily_unavailable
         | 
| 17 | 
            +
              ]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              # https://tools.ietf.org/html/rfc6749#section-5.2
         | 
| 20 | 
            +
              access_token_request_errors = %w[
         | 
| 21 | 
            +
                invalid_request
         | 
| 22 | 
            +
                invalid_client
         | 
| 23 | 
            +
                invalid_grant
         | 
| 24 | 
            +
                unauthorized_client
         | 
| 25 | 
            +
                unsupported_grant_type
         | 
| 26 | 
            +
                invalid_scope
         | 
| 27 | 
            +
              ]
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              # Authorization Code Grant errors from both authorization requests
         | 
| 30 | 
            +
              # and access token requests.
         | 
| 31 | 
            +
              OAUTH2_ERRORS = authorization_request_errors | access_token_request_errors
         | 
| 7 32 | 
             
            end
         | 
| 8 33 |  | 
| 9 34 | 
             
            require 'google_sign_in/identity'
         | 
| @@ -9,8 +9,8 @@ module GoogleSignIn | |
| 9 9 | 
             
                QUALIFIED_URL_PATTERN = /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
         | 
| 10 10 |  | 
| 11 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)})"
         | 
| 12 | 
            +
                  if target.blank? || (target =~ QUALIFIED_URL_PATTERN && origin_of(target) != origin_of(source))
         | 
| 13 | 
            +
                    raise Violation, "Redirect target #{target.inspect} does not have same origin as request (expected #{origin_of(source)})"
         | 
| 14 14 | 
             
                  end
         | 
| 15 15 | 
             
                end
         | 
| 16 16 |  | 
| @@ -5,16 +5,92 @@ class GoogleSignIn::CallbacksControllerTest < ActionDispatch::IntegrationTest | |
| 5 5 | 
             
                post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 6 6 | 
             
                assert_response :redirect
         | 
| 7 7 |  | 
| 8 | 
            -
                 | 
| 8 | 
            +
                stub_token_for '4/SgCpHSVW5-Cy', access_token: 'ya29.GlwIBo', id_token: 'eyJhbGciOiJSUzI'
         | 
| 9 9 |  | 
| 10 10 | 
             
                get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state])
         | 
| 11 11 | 
             
                assert_redirected_to 'http://www.example.com/login'
         | 
| 12 | 
            -
                assert_equal 'eyJhbGciOiJSUzI', flash[: | 
| 12 | 
            +
                assert_equal 'eyJhbGciOiJSUzI', flash[:google_sign_in][:id_token]
         | 
| 13 | 
            +
                assert_nil flash[:google_sign_in][:error]
         | 
| 13 14 | 
             
              end
         | 
| 14 15 |  | 
| 15 | 
            -
               | 
| 16 | 
            +
              # Authorization request errors: https://tools.ietf.org/html/rfc6749#section-4.1.2.1
         | 
| 17 | 
            +
              %w[ invalid_request unauthorized_client access_denied unsupported_response_type invalid_scope server_error temporarily_unavailable ].each do |error|
         | 
| 18 | 
            +
                test "receiving an authorization code grant error: #{error}" do
         | 
| 19 | 
            +
                  post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 20 | 
            +
                  assert_response :redirect
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  get google_sign_in.callback_url(error: error, state: flash[:state])
         | 
| 23 | 
            +
                  assert_redirected_to 'http://www.example.com/login'
         | 
| 24 | 
            +
                  assert_nil flash[:google_sign_in][:id_token]
         | 
| 25 | 
            +
                  assert_equal error, flash[:google_sign_in][:error]
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              test "receiving an invalid authorization error" do
         | 
| 30 | 
            +
                post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 31 | 
            +
                assert_response :redirect
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                get google_sign_in.callback_url(error: 'unknown error code', state: flash[:state])
         | 
| 34 | 
            +
                assert_redirected_to 'http://www.example.com/login'
         | 
| 35 | 
            +
                assert_nil flash[:google_sign_in][:id_token]
         | 
| 36 | 
            +
                assert_equal "invalid_request", flash[:google_sign_in][:error]
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              test "receiving neither code nor error" do
         | 
| 40 | 
            +
                post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 41 | 
            +
                assert_response :redirect
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                get google_sign_in.callback_url(state: flash[:state])
         | 
| 44 | 
            +
                assert_redirected_to 'http://www.example.com/login'
         | 
| 45 | 
            +
                assert_nil flash[:google_sign_in][:id_token]
         | 
| 46 | 
            +
                assert_equal 'invalid_request', flash[:google_sign_in][:error]
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              # Access token request errors: https://tools.ietf.org/html/rfc6749#section-5.2
         | 
| 50 | 
            +
              %w[ invalid_request invalid_client invalid_grant unauthorized_client unsupported_grant_type invalid_scope ].each do |error|
         | 
| 51 | 
            +
                test "receiving an access token request error: #{error}" do
         | 
| 52 | 
            +
                  post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 53 | 
            +
                  assert_response :redirect
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  stub_token_error_for '4/SgCpHSVW5-Cy', error: error
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state])
         | 
| 58 | 
            +
                  assert_redirected_to 'http://www.example.com/login'
         | 
| 59 | 
            +
                  assert_nil flash[:google_sign_in][:id_token]
         | 
| 60 | 
            +
                  assert_equal error, flash[:google_sign_in][:error]
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              test "protecting against CSRF without flash state" do
         | 
| 65 | 
            +
                post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 66 | 
            +
                assert_response :redirect
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: 'invalid')
         | 
| 69 | 
            +
                assert_redirected_to 'http://www.example.com/login'
         | 
| 70 | 
            +
                assert_nil flash[:google_sign_in][:id_token]
         | 
| 71 | 
            +
                assert_equal 'invalid_request', flash[:google_sign_in][:error]
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              test "protecting against CSRF with invalid state" do
         | 
| 75 | 
            +
                post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 76 | 
            +
                assert_response :redirect
         | 
| 77 | 
            +
                assert_not_nil flash[:state]
         | 
| 78 | 
            +
             | 
| 16 79 | 
             
                get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: 'invalid')
         | 
| 17 | 
            -
                 | 
| 80 | 
            +
                assert_redirected_to 'http://www.example.com/login'
         | 
| 81 | 
            +
                assert_nil flash[:google_sign_in][:id_token]
         | 
| 82 | 
            +
                assert_equal 'invalid_request', flash[:google_sign_in][:error]
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              test "protecting against CSRF with missing state" do
         | 
| 86 | 
            +
                post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
         | 
| 87 | 
            +
                assert_response :redirect
         | 
| 88 | 
            +
                assert_not_nil flash[:state]
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy')
         | 
| 91 | 
            +
                assert_redirected_to 'http://www.example.com/login'
         | 
| 92 | 
            +
                assert_nil flash[:google_sign_in][:id_token]
         | 
| 93 | 
            +
                assert_equal 'invalid_request', flash[:google_sign_in][:error]
         | 
| 18 94 | 
             
              end
         | 
| 19 95 |  | 
| 20 96 | 
             
              test "protecting against open redirects" do
         | 
| @@ -25,12 +101,33 @@ class GoogleSignIn::CallbacksControllerTest < ActionDispatch::IntegrationTest | |
| 25 101 | 
             
                assert_response :bad_request
         | 
| 26 102 | 
             
              end
         | 
| 27 103 |  | 
| 104 | 
            +
              test "receiving no proceed_to URL" do
         | 
| 105 | 
            +
                get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: 'invalid')
         | 
| 106 | 
            +
                assert_response :bad_request
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 28 109 | 
             
              private
         | 
| 29 | 
            -
                def  | 
| 30 | 
            -
                   | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 110 | 
            +
                def stub_token_for(code, **response_body)
         | 
| 111 | 
            +
                  stub_token_request(code, status: 200, response: response_body)
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                def stub_token_error_for(code, error:)
         | 
| 115 | 
            +
                  stub_token_request(code, status: 418, response: { error: error })
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def stub_token_request(code, status:, response:)
         | 
| 119 | 
            +
                  stub_request(:post, 'https://oauth2.googleapis.com/token').with(
         | 
| 120 | 
            +
                    body: {
         | 
| 121 | 
            +
                      grant_type: 'authorization_code',
         | 
| 122 | 
            +
                      code: code,
         | 
| 123 | 
            +
                      client_id: FAKE_GOOGLE_CLIENT_ID,
         | 
| 124 | 
            +
                      client_secret: FAKE_GOOGLE_CLIENT_SECRET,
         | 
| 125 | 
            +
                      redirect_uri: 'http://www.example.com/google_sign_in/callback'
         | 
| 126 | 
            +
                    }
         | 
| 127 | 
            +
                  ).to_return(
         | 
| 128 | 
            +
                    status: status,
         | 
| 129 | 
            +
                    headers: { 'Content-Type' => 'application/json' },
         | 
| 130 | 
            +
                    body: JSON.generate(response)
         | 
| 131 | 
            +
                  )
         | 
| 35 132 | 
             
                end
         | 
| 36 133 | 
             
            end
         | 
| @@ -65,6 +65,14 @@ class GoogleSignIn::IdentityTest < ActiveSupport::TestCase | |
| 65 65 | 
             
                assert_equal "basecamp.com", GoogleSignIn::Identity.new(token_with(hd: "basecamp.com")).hosted_domain
         | 
| 66 66 | 
             
              end
         | 
| 67 67 |  | 
| 68 | 
            +
              test "extracting given name" do
         | 
| 69 | 
            +
                assert_equal "George", GoogleSignIn::Identity.new(token_with(given_name: "George")).given_name
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              test "extracting family name" do
         | 
| 73 | 
            +
                assert_equal "Claghorn", GoogleSignIn::Identity.new(token_with(family_name: "Claghorn")).family_name
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 68 76 | 
             
              private
         | 
| 69 77 | 
             
                def switch_client_id_to(value)
         | 
| 70 78 | 
             
                  previous_value = GoogleSignIn.client_id
         | 
| @@ -20,6 +20,12 @@ class GoogleSignIn::RedirectProtectorTest < ActiveSupport::TestCase | |
| 20 20 | 
             
                end
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 | 
            +
              test "disallows empty URL target" do
         | 
| 24 | 
            +
                assert_raises GoogleSignIn::RedirectProtector::Violation do
         | 
| 25 | 
            +
                  GoogleSignIn::RedirectProtector.ensure_same_origin nil, 'https://basecamp.com'
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 23 29 | 
             
              test "allows URL target with same origin as source" do
         | 
| 24 30 | 
             
                assert_nothing_raised do
         | 
| 25 31 | 
             
                  GoogleSignIn::RedirectProtector.ensure_same_origin 'https://basecamp.com', 'https://basecamp.com'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: google_sign_in
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - David Heinemeier Hansson
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 12 | 
            +
            date: 2020-05-23 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: rails
         | 
| @@ -212,8 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 212 212 | 
             
                - !ruby/object:Gem::Version
         | 
| 213 213 | 
             
                  version: '0'
         | 
| 214 214 | 
             
            requirements: []
         | 
| 215 | 
            -
             | 
| 216 | 
            -
            rubygems_version: 2.7.6
         | 
| 215 | 
            +
            rubygems_version: 3.1.2
         | 
| 217 216 | 
             
            signing_key: 
         | 
| 218 217 | 
             
            specification_version: 4
         | 
| 219 218 | 
             
            summary: Sign in (or up) with Google for Rails applications
         |