Skip to content

Commit

Permalink
Pass OAuth 2 errors through to the app as flash[:google_sign_in_error]
Browse files Browse the repository at this point in the history
Rather than responding with 400 Bad Request due to missing `code` param.
  • Loading branch information
jeremy committed Mar 6, 2019
1 parent bd3adcf commit 15dc21c
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 5 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ This gem provides a `google_sign_in_button` helper. It generates a button which
```

The `proceed_to` argument is required. After authenticating with Google, the gem redirects to `proceed_to`, providing
a Google ID token in `flash[:google_sign_in_token]`. Your application decides what to do with it:
a Google ID token in `flash[:google_sign_in_token]` or an [OAuth error code](https://tools.ietf.org/html/rfc6749#section-4.1.2.1)
in `flash[:google_sign_in_error]`. Your application decides what to do with it:

```ruby
# config/routes.rb
Expand Down Expand Up @@ -108,8 +109,11 @@ class LoginsController < ApplicationController
private
def authenticate_with_google
if flash[:google_sign_in_token].present?
User.find_by google_id: GoogleSignIn::Identity.new(flash[:google_sign_in_token]).user_id
if id_token = flash[:google_sign_in_token]
User.find_by google_id: GoogleSignIn::Identity.new(id_token).user_id
elsif error = flash[:google_sign_in_error]
logger.error "Google authentication error: #{error}"
nil
end
end
end
Expand Down
16 changes: 14 additions & 2 deletions app/controllers/google_sign_in/callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class GoogleSignIn::CallbacksController < GoogleSignIn::BaseController
def show
if valid_request?
redirect_to proceed_to_url, flash: { google_sign_in_token: id_token }
redirect_to proceed_to_url, flash: google_sign_in_response
else
head :unprocessable_entity
end
Expand All @@ -21,7 +21,19 @@ def proceed_to_url
flash[:proceed_to].tap { |url| GoogleSignIn::RedirectProtector.ensure_same_origin(url, request.url) }
end

def google_sign_in_response
if params[:code].present?
{ google_sign_in_token: id_token }
else
{ google_sign_in_error: error_message }
end
end

def id_token
client.auth_code.get_token(params.require(:code))['id_token']
client.auth_code.get_token(params[:code])['id_token']
end

def error_message
params[:error].presence_in(GoogleSignIn::OAUTH2_ERRORS) || "invalid_request"
end
end
10 changes: 10 additions & 0 deletions lib/google_sign_in.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
module GoogleSignIn
mattr_accessor :client_id
mattr_accessor :client_secret

# https://tools.ietf.org/html/rfc6749#section-4.1.2.1
OAUTH2_ERRORS = %w[
invalid_request
invalid_client
invalid_grant
unauthorized_client
unsupported_grant_type
invalid_scope
]
end

require 'google_sign_in/identity'
Expand Down
33 changes: 33 additions & 0 deletions test/controllers/callbacks_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@ class GoogleSignIn::CallbacksControllerTest < ActionDispatch::IntegrationTest
get google_sign_in.callback_url(code: '4/SgCpHSVW5-Cy', state: flash[:state])
assert_redirected_to 'http://www.example.com/login'
assert_equal 'eyJhbGciOiJSUzI', flash[:google_sign_in_token]
assert_nil flash[:google_sign_in_error]
end

GoogleSignIn::OAUTH2_ERRORS.each do |error|
test "receiving an authorization error: #{error}" do
post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
assert_response :redirect

get google_sign_in.callback_url(error: error, state: flash[:state])
assert_redirected_to 'http://www.example.com/login'
assert_nil flash[:google_sign_in_token]
assert_equal error, flash[:google_sign_in_error]
end
end

test "receiving an invalid authorization error" do
post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
assert_response :redirect

get google_sign_in.callback_url(error: 'unknown error code', state: flash[:state])
assert_redirected_to 'http://www.example.com/login'
assert_nil flash[:google_sign_in_token]
assert_equal "invalid_request", flash[:google_sign_in_error]
end

test "receiving neither code nor error" do
post google_sign_in.authorization_url, params: { proceed_to: 'http://www.example.com/login' }
assert_response :redirect

get google_sign_in.callback_url(state: flash[:state])
assert_redirected_to 'http://www.example.com/login'
assert_nil flash[:google_sign_in_token]
assert_equal 'invalid_request', flash[:google_sign_in_error]
end

test "protecting against CSRF" do
Expand Down

0 comments on commit 15dc21c

Please sign in to comment.