added ruby 2.3 compatability

also:

* updated rubocop and applied styling
* made small test refactorings (using the expect block notation)
* made a small refactor to remove an unnecessary ``return credentials``
* bumped the version
This commit is contained in:
nicholas shook 2016-07-13 15:44:16 -07:00
parent 72fb8df541
commit 82a4f8520c
25 changed files with 199 additions and 160 deletions

View File

@ -1,6 +1,7 @@
sudo: false sudo: false
language: ruby language: ruby
rvm: rvm:
- 2.3
- 2.2 - 2.2
- 2.0.0 - 2.0.0
- 2.1 - 2.1

View File

@ -42,7 +42,7 @@ module Google
# Module Auth provides classes that provide Google-specific authorization # Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs. # used to access Google APIs.
module Auth module Auth
NOT_FOUND_ERROR = <<END NOT_FOUND_ERROR = <<END.freeze
Could not load the default credentials. Browse to Could not load the default credentials. Browse to
https://developers.google.com/accounts/docs/application-default-credentials https://developers.google.com/accounts/docs/application-default-credentials
for more information for more information
@ -70,14 +70,14 @@ END
def self.read_creds def self.read_creds
env_var = CredentialsLoader::ACCOUNT_TYPE_VAR env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
type = ENV[env_var] type = ENV[env_var]
fail "#{env_var} is undefined in env" unless type raise "#{env_var} is undefined in env" unless type
case type case type
when 'service_account' when 'service_account'
ServiceAccountCredentials ServiceAccountCredentials
when 'authorized_user' when 'authorized_user'
UserRefreshCredentials UserRefreshCredentials
else else
fail "credentials type '#{type}' is not supported" raise "credentials type '#{type}' is not supported"
end end
end end
@ -85,7 +85,7 @@ END
def self.determine_creds_class(json_key_io) def self.determine_creds_class(json_key_io)
json_key = MultiJson.load(json_key_io.read) json_key = MultiJson.load(json_key_io.read)
key = 'type' key = 'type'
fail "the json is missing the '#{key}' field" unless json_key.key?(key) raise "the json is missing the '#{key}' field" unless json_key.key?(key)
type = json_key[key] type = json_key[key]
case type case type
when 'service_account' when 'service_account'
@ -93,7 +93,7 @@ END
when 'authorized_user' when 'authorized_user'
[json_key, UserRefreshCredentials] [json_key, UserRefreshCredentials]
else else
fail "credentials type '#{type}' is not supported" raise "credentials type '#{type}' is not supported"
end end
end end
end end
@ -116,7 +116,7 @@ END
DefaultCredentials.from_well_known_path(scope) || DefaultCredentials.from_well_known_path(scope) ||
DefaultCredentials.from_system_default_path(scope) DefaultCredentials.from_system_default_path(scope)
return creds unless creds.nil? return creds unless creds.nil?
fail NOT_FOUND_ERROR unless GCECredentials.on_gce?(options) raise NOT_FOUND_ERROR unless GCECredentials.on_gce?(options)
GCECredentials.new GCECredentials.new
end end

View File

@ -34,12 +34,12 @@ module Google
# Representation of an application's identity for user authorization # Representation of an application's identity for user authorization
# flows. # flows.
class ClientId class ClientId
INSTALLED_APP = 'installed' INSTALLED_APP = 'installed'.freeze
WEB_APP = 'web' WEB_APP = 'web'.freeze
CLIENT_ID = 'client_id' CLIENT_ID = 'client_id'.freeze
CLIENT_SECRET = 'client_secret' CLIENT_SECRET = 'client_secret'.freeze
MISSING_TOP_LEVEL_ELEMENT_ERROR = MISSING_TOP_LEVEL_ELEMENT_ERROR =
"Expected top level property 'installed' or 'web' to be present." "Expected top level property 'installed' or 'web' to be present.".freeze
# Text identifier of the client ID # Text identifier of the client ID
# @return [String] # @return [String]
@ -63,8 +63,8 @@ module Google
# & secrets in source. See {#from_file} to load from # & secrets in source. See {#from_file} to load from
# `client_secrets.json` files. # `client_secrets.json` files.
def initialize(id, secret) def initialize(id, secret)
fail 'Client id can not be nil' if id.nil? raise 'Client id can not be nil' if id.nil?
fail 'Client secret can not be nil' if secret.nil? raise 'Client secret can not be nil' if secret.nil?
@id = id @id = id
@secret = secret @secret = secret
end end
@ -76,7 +76,7 @@ module Google
# Path of file to read from # Path of file to read from
# @return [Google::Auth::ClientID] # @return [Google::Auth::ClientID]
def self.from_file(file) def self.from_file(file)
fail 'File can not be nil.' if file.nil? raise 'File can not be nil.' if file.nil?
File.open(file.to_s) do |f| File.open(file.to_s) do |f|
json = f.read json = f.read
config = MultiJson.load(json) config = MultiJson.load(json)
@ -92,9 +92,9 @@ module Google
# Parsed contents of the JSON file # Parsed contents of the JSON file
# @return [Google::Auth::ClientID] # @return [Google::Auth::ClientID]
def self.from_hash(config) def self.from_hash(config)
fail 'Hash can not be nil.' if config.nil? raise 'Hash can not be nil.' if config.nil?
raw_detail = config[INSTALLED_APP] || config[WEB_APP] raw_detail = config[INSTALLED_APP] || config[WEB_APP]
fail MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil? raise MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
ClientId.new(raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET]) ClientId.new(raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET])
end end
end end

View File

@ -35,13 +35,13 @@ module Google
# Module Auth provides classes that provide Google-specific authorization # Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs. # used to access Google APIs.
module Auth module Auth
NO_METADATA_SERVER_ERROR = <<END NO_METADATA_SERVER_ERROR = <<END.freeze
Error code 404 trying to get security access token Error code 404 trying to get security access token
from Compute Engine metadata for the default service account. This from Compute Engine metadata for the default service account. This
may be because the virtual machine instance does not have permission may be because the virtual machine instance does not have permission
scopes specified. scopes specified.
END END
UNEXPECTED_ERROR_SUFFIX = <<END UNEXPECTED_ERROR_SUFFIX = <<END.freeze
trying to get security access token from Compute Engine metadata for trying to get security access token from Compute Engine metadata for
the default service account the default service account
END END
@ -52,8 +52,8 @@ END
# The IP Address is used in the URIs to speed up failures on non-GCE # The IP Address is used in the URIs to speed up failures on non-GCE
# systems. # systems.
COMPUTE_AUTH_TOKEN_URI = 'http://169.254.169.254/computeMetadata/v1/'\ COMPUTE_AUTH_TOKEN_URI = 'http://169.254.169.254/computeMetadata/v1/'\
'instance/service-accounts/default/token' 'instance/service-accounts/default/token'.freeze
COMPUTE_CHECK_URI = 'http://169.254.169.254' COMPUTE_CHECK_URI = 'http://169.254.169.254'.freeze
class << self class << self
extend Memoist extend Memoist
@ -94,10 +94,10 @@ END
Signet::OAuth2.parse_credentials(resp.body, Signet::OAuth2.parse_credentials(resp.body,
resp.headers['content-type']) resp.headers['content-type'])
when 404 when 404
fail(Signet::AuthorizationError, NO_METADATA_SERVER_ERROR) raise(Signet::AuthorizationError, NO_METADATA_SERVER_ERROR)
else else
msg = "Unexpected error code #{resp.status}" + UNEXPECTED_ERROR_SUFFIX msg = "Unexpected error code #{resp.status}" + UNEXPECTED_ERROR_SUFFIX
fail(Signet::AuthorizationError, msg) raise(Signet::AuthorizationError, msg)
end end
end end
end end

View File

@ -39,22 +39,23 @@ module Google
# credentials files on the file system. # credentials files on the file system.
module CredentialsLoader module CredentialsLoader
extend Memoist extend Memoist
ENV_VAR = 'GOOGLE_APPLICATION_CREDENTIALS' ENV_VAR = 'GOOGLE_APPLICATION_CREDENTIALS'.freeze
PRIVATE_KEY_VAR = 'GOOGLE_PRIVATE_KEY' PRIVATE_KEY_VAR = 'GOOGLE_PRIVATE_KEY'.freeze
CLIENT_EMAIL_VAR = 'GOOGLE_CLIENT_EMAIL' CLIENT_EMAIL_VAR = 'GOOGLE_CLIENT_EMAIL'.freeze
CLIENT_ID_VAR = 'GOOGLE_CLIENT_ID' CLIENT_ID_VAR = 'GOOGLE_CLIENT_ID'.freeze
CLIENT_SECRET_VAR = 'GOOGLE_CLIENT_SECRET' CLIENT_SECRET_VAR = 'GOOGLE_CLIENT_SECRET'.freeze
REFRESH_TOKEN_VAR = 'GOOGLE_REFRESH_TOKEN' REFRESH_TOKEN_VAR = 'GOOGLE_REFRESH_TOKEN'.freeze
ACCOUNT_TYPE_VAR = 'GOOGLE_ACCOUNT_TYPE' ACCOUNT_TYPE_VAR = 'GOOGLE_ACCOUNT_TYPE'.freeze
CREDENTIALS_FILE_NAME = 'application_default_credentials.json' CREDENTIALS_FILE_NAME = 'application_default_credentials.json'.freeze
NOT_FOUND_ERROR = NOT_FOUND_ERROR =
"Unable to read the credential file specified by #{ENV_VAR}" "Unable to read the credential file specified by #{ENV_VAR}".freeze
WELL_KNOWN_PATH = "gcloud/#{CREDENTIALS_FILE_NAME}" WELL_KNOWN_PATH = "gcloud/#{CREDENTIALS_FILE_NAME}".freeze
WELL_KNOWN_ERROR = 'Unable to read the default credential file' WELL_KNOWN_ERROR = 'Unable to read the default credential file'.freeze
SYSTEM_DEFAULT_ERROR = 'Unable to read the system default credential file' SYSTEM_DEFAULT_ERROR =
'Unable to read the system default credential file'.freeze
# make_creds proxies the construction of a credentials instance # make_creds proxies the construction of a credentials instance
# #
@ -71,7 +72,7 @@ module Google
def from_env(scope = nil) def from_env(scope = nil)
if ENV.key?(ENV_VAR) if ENV.key?(ENV_VAR)
path = ENV[ENV_VAR] path = ENV[ENV_VAR]
fail "file #{path} does not exist" unless File.exist?(path) raise "file #{path} does not exist" unless File.exist?(path)
File.open(path) do |f| File.open(path) do |f|
return make_creds(json_key_io: f, scope: scope) return make_creds(json_key_io: f, scope: scope)
end end

View File

@ -37,16 +37,16 @@ module Google
module Auth module Auth
# Authenticates requests using IAM credentials. # Authenticates requests using IAM credentials.
class IAMCredentials class IAMCredentials
SELECTOR_KEY = 'x-goog-iam-authority-selector' SELECTOR_KEY = 'x-goog-iam-authority-selector'.freeze
TOKEN_KEY = 'x-goog-iam-authorization-token' TOKEN_KEY = 'x-goog-iam-authorization-token'.freeze
# Initializes an IAMCredentials. # Initializes an IAMCredentials.
# #
# @param selector the IAM selector. # @param selector the IAM selector.
# @param token the IAM token. # @param token the IAM token.
def initialize(selector, token) def initialize(selector, token)
fail TypeError unless selector.is_a? String raise TypeError unless selector.is_a? String
fail TypeError unless token.is_a? String raise TypeError unless token.is_a? String
@selector = selector @selector = selector
@token = token @token = token
end end

View File

@ -39,7 +39,7 @@ module Google
'email' => 'https://www.googleapis.com/auth/userinfo.email', 'email' => 'https://www.googleapis.com/auth/userinfo.email',
'profile' => 'https://www.googleapis.com/auth/userinfo.profile', 'profile' => 'https://www.googleapis.com/auth/userinfo.profile',
'openid' => 'https://www.googleapis.com/auth/plus.me' 'openid' => 'https://www.googleapis.com/auth/plus.me'
} }.freeze
def self.normalize(scope) def self.normalize(scope)
list = as_array(scope) list = as_array(scope)
@ -53,7 +53,7 @@ module Google
when String when String
scope.split(' ') scope.split(' ')
else else
fail 'Invalid scope value. Must be string or array' raise 'Invalid scope value. Must be string or array'
end end
end end
end end

View File

@ -46,7 +46,7 @@ module Google
# #
# cf [Application Default Credentials](http://goo.gl/mkAHpZ) # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
class ServiceAccountCredentials < Signet::OAuth2::Client class ServiceAccountCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token' TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'.freeze
extend CredentialsLoader extend CredentialsLoader
# Creates a ServiceAccountCredentials. # Creates a ServiceAccountCredentials.
@ -73,8 +73,8 @@ module Google
# JSON key. # JSON key.
def self.read_json_key(json_key_io) def self.read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read) json_key = MultiJson.load(json_key_io.read)
fail 'missing client_email' unless json_key.key?('client_email') raise 'missing client_email' unless json_key.key?('client_email')
fail 'missing private_key' unless json_key.key?('private_key') raise 'missing private_key' unless json_key.key?('private_key')
[json_key['private_key'], json_key['client_email']] [json_key['private_key'], json_key['client_email']]
end end
@ -119,8 +119,8 @@ module Google
class ServiceAccountJwtHeaderCredentials class ServiceAccountJwtHeaderCredentials
JWT_AUD_URI_KEY = :jwt_aud_uri JWT_AUD_URI_KEY = :jwt_aud_uri
AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token' TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'.freeze
SIGNING_ALGORITHM = 'RS256' SIGNING_ALGORITHM = 'RS256'.freeze
EXPIRY = 60 EXPIRY = 60
extend CredentialsLoader extend CredentialsLoader
@ -139,8 +139,8 @@ module Google
# JSON key. # JSON key.
def self.read_json_key(json_key_io) def self.read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read) json_key = MultiJson.load(json_key_io.read)
fail 'missing client_email' unless json_key.key?('client_email') raise 'missing client_email' unless json_key.key?('client_email')
fail 'missing private_key' unless json_key.key?('private_key') raise 'missing private_key' unless json_key.key?('private_key')
[json_key['private_key'], json_key['client_email']] [json_key['private_key'], json_key['client_email']]
end end

View File

@ -64,7 +64,7 @@ module Signet
@refresh_listeners << block @refresh_listeners << block
end end
alias_method :orig_fetch_access_token!, :fetch_access_token! alias orig_fetch_access_token! fetch_access_token!
def fetch_access_token!(options = {}) def fetch_access_token!(options = {})
info = orig_fetch_access_token!(options) info = orig_fetch_access_token!(options)
notify_refresh_listeners notify_refresh_listeners

View File

@ -37,7 +37,7 @@ module Google
# are stored as JSON using the supplied key, prefixed with # are stored as JSON using the supplied key, prefixed with
# `g-user-token:` # `g-user-token:`
class RedisTokenStore < Google::Auth::TokenStore class RedisTokenStore < Google::Auth::TokenStore
DEFAULT_KEY_PREFIX = 'g-user-token:' DEFAULT_KEY_PREFIX = 'g-user-token:'.freeze
# Create a new store with the supplied redis client. # Create a new store with the supplied redis client.
# #
@ -51,11 +51,11 @@ module Google
def initialize(options = {}) def initialize(options = {})
redis = options.delete(:redis) redis = options.delete(:redis)
prefix = options.delete(:prefix) prefix = options.delete(:prefix)
case redis @redis = case redis
when Redis when Redis
@redis = redis redis
else else
@redis = Redis.new(options) Redis.new(options)
end end
@prefix = prefix || DEFAULT_KEY_PREFIX @prefix = prefix || DEFAULT_KEY_PREFIX
end end

View File

@ -44,7 +44,7 @@ module Google
# @return [String] # @return [String]
# The loaded token data. # The loaded token data.
def load(_id) def load(_id)
fail 'Not implemented' raise 'Not implemented'
end end
# Put the token data into storage for the given ID. # Put the token data into storage for the given ID.
@ -54,7 +54,7 @@ module Google
# @param [String] token # @param [String] token
# The token data to store. # The token data to store.
def store(_id, _token) def store(_id, _token)
fail 'Not implemented' raise 'Not implemented'
end end
# Remove the token data from storage for the given ID. # Remove the token data from storage for the given ID.
@ -62,7 +62,7 @@ module Google
# @param [String] id # @param [String] id
# ID of the token data to delete # ID of the token data to delete
def delete(_id) def delete(_id)
fail 'Not implemented' raise 'Not implemented'
end end
end end
end end

View File

@ -32,6 +32,7 @@ require 'multi_json'
require 'googleauth/signet' require 'googleauth/signet'
require 'googleauth/user_refresh' require 'googleauth/user_refresh'
# rubocop:disable ClassLength
module Google module Google
module Auth module Auth
# Handles an interactive 3-Legged-OAuth2 (3LO) user consent authorization. # Handles an interactive 3-Legged-OAuth2 (3LO) user consent authorization.
@ -53,13 +54,13 @@ module Google
# ... # ...
class UserAuthorizer class UserAuthorizer
MISMATCHED_CLIENT_ID_ERROR = MISMATCHED_CLIENT_ID_ERROR =
'Token client ID of %s does not match configured client id %s' 'Token client ID of %s does not match configured client id %s'.freeze
NIL_CLIENT_ID_ERROR = 'Client id can not be nil.' NIL_CLIENT_ID_ERROR = 'Client id can not be nil.'.freeze
NIL_SCOPE_ERROR = 'Scope can not be nil.' NIL_SCOPE_ERROR = 'Scope can not be nil.'.freeze
NIL_USER_ID_ERROR = 'User ID can not be nil.' NIL_USER_ID_ERROR = 'User ID can not be nil.'.freeze
NIL_TOKEN_STORE_ERROR = 'Can not call method if token store is nil' NIL_TOKEN_STORE_ERROR = 'Can not call method if token store is nil'.freeze
MISSING_ABSOLUTE_URL_ERROR = MISSING_ABSOLUTE_URL_ERROR =
'Absolute base url required for relative callback url "%s"' 'Absolute base url required for relative callback url "%s"'.freeze
# Initialize the authorizer # Initialize the authorizer
# #
@ -73,8 +74,8 @@ module Google
# URL (either absolute or relative) of the auth callback. # URL (either absolute or relative) of the auth callback.
# Defaults to '/oauth2callback' # Defaults to '/oauth2callback'
def initialize(client_id, scope, token_store, callback_uri = nil) def initialize(client_id, scope, token_store, callback_uri = nil)
fail NIL_CLIENT_ID_ERROR if client_id.nil? raise NIL_CLIENT_ID_ERROR if client_id.nil?
fail NIL_SCOPE_ERROR if scope.nil? raise NIL_SCOPE_ERROR if scope.nil?
@client_id = client_id @client_id = client_id
@scope = Array(scope) @scope = Array(scope)
@ -102,7 +103,8 @@ module Google
credentials = UserRefreshCredentials.new( credentials = UserRefreshCredentials.new(
client_id: @client_id.id, client_id: @client_id.id,
client_secret: @client_id.secret, client_secret: @client_id.secret,
scope: scope) scope: scope
)
redirect_uri = redirect_uri_for(options[:base_url]) redirect_uri = redirect_uri_for(options[:base_url])
url = credentials.authorization_uri(access_type: 'offline', url = credentials.authorization_uri(access_type: 'offline',
redirect_uri: redirect_uri, redirect_uri: redirect_uri,
@ -123,8 +125,8 @@ module Google
# @return [Google::Auth::UserRefreshCredentials] # @return [Google::Auth::UserRefreshCredentials]
# Stored credentials, nil if none present # Stored credentials, nil if none present
def get_credentials(user_id, scope = nil) def get_credentials(user_id, scope = nil)
fail NIL_USER_ID_ERROR if user_id.nil? raise NIL_USER_ID_ERROR if user_id.nil?
fail NIL_TOKEN_STORE_ERROR if @token_store.nil? raise NIL_TOKEN_STORE_ERROR if @token_store.nil?
scope ||= @scope scope ||= @scope
saved_token = @token_store.load(user_id) saved_token = @token_store.load(user_id)
@ -132,7 +134,7 @@ module Google
data = MultiJson.load(saved_token) data = MultiJson.load(saved_token)
if data.fetch('client_id', @client_id.id) != @client_id.id if data.fetch('client_id', @client_id.id) != @client_id.id
fail sprintf(MISMATCHED_CLIENT_ID_ERROR, raise sprintf(MISMATCHED_CLIENT_ID_ERROR,
data['client_id'], @client_id.id) data['client_id'], @client_id.id)
end end
@ -142,10 +144,10 @@ module Google
scope: data['scope'] || @scope, scope: data['scope'] || @scope,
access_token: data['access_token'], access_token: data['access_token'],
refresh_token: data['refresh_token'], refresh_token: data['refresh_token'],
expires_at: data.fetch('expiration_time_millis', 0) / 1000) expires_at: data.fetch('expiration_time_millis', 0) / 1000
)
if credentials.includes_scope?(scope) if credentials.includes_scope?(scope)
monitor_credentials(user_id, credentials) return monitor_credentials(user_id, credentials)
return credentials
end end
nil nil
end end
@ -174,7 +176,8 @@ module Google
client_id: @client_id.id, client_id: @client_id.id,
client_secret: @client_id.secret, client_secret: @client_id.secret,
redirect_uri: redirect_uri_for(base_url), redirect_uri: redirect_uri_for(base_url),
scope: scope) scope: scope
)
credentials.code = code credentials.code = code
credentials.fetch_access_token!({}) credentials.fetch_access_token!({})
monitor_credentials(user_id, credentials) monitor_credentials(user_id, credentials)
@ -234,7 +237,8 @@ module Google
access_token: credentials.access_token, access_token: credentials.access_token,
refresh_token: credentials.refresh_token, refresh_token: credentials.refresh_token,
scope: credentials.scope, scope: credentials.scope,
expiration_time_millis: (credentials.expires_at.to_i) * 1000) expiration_time_millis: credentials.expires_at.to_i * 1000
)
@token_store.store(user_id, json) @token_store.store(user_id, json)
credentials credentials
end end
@ -263,11 +267,13 @@ module Google
# Redirect URI # Redirect URI
def redirect_uri_for(base_url) def redirect_uri_for(base_url)
return @callback_uri unless URI(@callback_uri).scheme.nil? return @callback_uri unless URI(@callback_uri).scheme.nil?
fail sprintf( raise sprintf(
MISSING_ABSOLUTE_URL_ERROR, MISSING_ABSOLUTE_URL_ERROR,
@callback_uri) if base_url.nil? || URI(base_url).scheme.nil? @callback_uri
) if base_url.nil? || URI(base_url).scheme.nil?
URI.join(base_url, @callback_uri).to_s URI.join(base_url, @callback_uri).to_s
end end
end end
end end
end end
# rubocop:enable ClassLength

View File

@ -46,9 +46,9 @@ module Google
# #
# cf [Application Default Credentials](http://goo.gl/mkAHpZ) # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
class UserRefreshCredentials < Signet::OAuth2::Client class UserRefreshCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token' TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'.freeze
AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth' AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth'.freeze
REVOKE_TOKEN_URI = 'https://accounts.google.com/o/oauth2/revoke' REVOKE_TOKEN_URI = 'https://accounts.google.com/o/oauth2/revoke'.freeze
extend CredentialsLoader extend CredentialsLoader
# Create a UserRefreshCredentials. # Create a UserRefreshCredentials.
@ -77,7 +77,7 @@ module Google
json_key = MultiJson.load(json_key_io.read) json_key = MultiJson.load(json_key_io.read)
wanted = %w(client_id client_secret refresh_token) wanted = %w(client_id client_secret refresh_token)
wanted.each do |key| wanted.each do |key|
fail "the json is missing the #{key} field" unless json_key.key?(key) raise "the json is missing the #{key} field" unless json_key.key?(key)
end end
json_key json_key
end end
@ -99,7 +99,7 @@ module Google
self.refresh_token = nil self.refresh_token = nil
self.expires_at = 0 self.expires_at = 0
else else
fail(Signet::AuthorizationError, raise(Signet::AuthorizationError,
"Unexpected error code #{resp.status}") "Unexpected error code #{resp.status}")
end end
end end

View File

@ -31,6 +31,6 @@ module Google
# Module Auth provides classes that provide Google-specific authorization # Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs. # used to access Google APIs.
module Auth module Auth
VERSION = '0.5.1' VERSION = '0.5.2'.freeze
end end
end end

View File

@ -66,20 +66,21 @@ module Google
# @see {Google::Auth::ControllerHelpers} # @see {Google::Auth::ControllerHelpers}
# @note Requires sessions are enabled # @note Requires sessions are enabled
class WebUserAuthorizer < Google::Auth::UserAuthorizer class WebUserAuthorizer < Google::Auth::UserAuthorizer
STATE_PARAM = 'state' STATE_PARAM = 'state'.freeze
AUTH_CODE_KEY = 'code' AUTH_CODE_KEY = 'code'.freeze
ERROR_CODE_KEY = 'error' ERROR_CODE_KEY = 'error'.freeze
SESSION_ID_KEY = 'session_id' SESSION_ID_KEY = 'session_id'.freeze
CALLBACK_STATE_KEY = 'g-auth-callback' CALLBACK_STATE_KEY = 'g-auth-callback'.freeze
CURRENT_URI_KEY = 'current_uri' CURRENT_URI_KEY = 'current_uri'.freeze
XSRF_KEY = 'g-xsrf-token' XSRF_KEY = 'g-xsrf-token'.freeze
SCOPE_KEY = 'scope' SCOPE_KEY = 'scope'.freeze
NIL_REQUEST_ERROR = 'Request is required.' NIL_REQUEST_ERROR = 'Request is required.'.freeze
NIL_SESSION_ERROR = 'Sessions must be enabled' NIL_SESSION_ERROR = 'Sessions must be enabled'.freeze
MISSING_AUTH_CODE_ERROR = 'Missing authorization code in request' MISSING_AUTH_CODE_ERROR = 'Missing authorization code in request'.freeze
AUTHORIZATION_ERROR = 'Authorization error: %s' AUTHORIZATION_ERROR = 'Authorization error: %s'.freeze
INVALID_STATE_TOKEN_ERROR = 'State token does not match expected value' INVALID_STATE_TOKEN_ERROR =
'State token does not match expected value'.freeze
class << self class << self
attr_accessor :default attr_accessor :default
@ -128,13 +129,15 @@ module Google
# credentials & next URL to redirect to # credentials & next URL to redirect to
def handle_auth_callback(user_id, request) def handle_auth_callback(user_id, request)
callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state( callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state(
request) request
)
WebUserAuthorizer.validate_callback_state(callback_state, request) WebUserAuthorizer.validate_callback_state(callback_state, request)
credentials = get_and_store_credentials_from_code( credentials = get_and_store_credentials_from_code(
user_id: user_id, user_id: user_id,
code: callback_state[AUTH_CODE_KEY], code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY], scope: callback_state[SCOPE_KEY],
base_url: request.url) base_url: request.url
)
[credentials, redirect_uri] [credentials, redirect_uri]
end end
@ -156,14 +159,15 @@ module Google
def get_authorization_url(options = {}) def get_authorization_url(options = {})
options = options.dup options = options.dup
request = options[:request] request = options[:request]
fail NIL_REQUEST_ERROR if request.nil? raise NIL_REQUEST_ERROR if request.nil?
fail NIL_SESSION_ERROR if request.session.nil? raise NIL_SESSION_ERROR if request.session.nil?
redirect_to = options[:redirect_to] || request.url redirect_to = options[:redirect_to] || request.url
request.session[XSRF_KEY] = SecureRandom.base64 request.session[XSRF_KEY] = SecureRandom.base64
options[:state] = MultiJson.dump( options[:state] = MultiJson.dump(
SESSION_ID_KEY => request.session[XSRF_KEY], SESSION_ID_KEY => request.session[XSRF_KEY],
CURRENT_URI_KEY => redirect_to) CURRENT_URI_KEY => redirect_to
)
options[:base_url] = request.url options[:base_url] = request.url
super(options) super(options)
end end
@ -193,7 +197,8 @@ module Google
user_id: user_id, user_id: user_id,
code: callback_state[AUTH_CODE_KEY], code: callback_state[AUTH_CODE_KEY],
scope: callback_state[SCOPE_KEY], scope: callback_state[SCOPE_KEY],
base_url: request.url) base_url: request.url
)
else else
super(user_id, scope) super(user_id, scope)
end end
@ -223,12 +228,12 @@ module Google
# Current request # Current request
def self.validate_callback_state(state, request) def self.validate_callback_state(state, request)
if state[AUTH_CODE_KEY].nil? if state[AUTH_CODE_KEY].nil?
fail Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR
elsif state[ERROR_CODE_KEY] elsif state[ERROR_CODE_KEY]
fail Signet::AuthorizationError, raise Signet::AuthorizationError,
sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY]) sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY] elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
fail Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR raise Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
end end
end end
@ -254,7 +259,7 @@ module Google
# #
# @see {Google::Auth::WebUserAuthorizer} # @see {Google::Auth::WebUserAuthorizer}
class CallbackApp class CallbackApp
LOCATION_HEADER = 'Location' LOCATION_HEADER = 'Location'.freeze
REDIR_STATUS = 302 REDIR_STATUS = 302
ERROR_STATUS = 500 ERROR_STATUS = 500

View File

@ -62,7 +62,8 @@ shared_examples 'apply/apply! are OK' do
@client.on_refresh(&b) @client.on_refresh(&b)
@client.fetch_access_token! @client.fetch_access_token!
end.to yield_with_args(have_attributes( end.to yield_with_args(have_attributes(
access_token: '1/abcdef1234567890')) access_token: '1/abcdef1234567890'
))
expect(stub).to have_been_requested expect(stub).to have_been_requested
end end
end end

View File

@ -104,7 +104,8 @@ describe Google::Auth::ClientId do
it 'should raise error' do it 'should raise error' do
expect { Google::Auth::ClientId.from_hash(config) }.to raise_error( expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
/Expected top level property/) /Expected top level property/
)
end end
end end
@ -119,7 +120,8 @@ describe Google::Auth::ClientId do
it 'should raise error' do it 'should raise error' do
expect { Google::Auth::ClientId.from_hash(config) }.to raise_error( expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
/Client id can not be nil/) /Client id can not be nil/
)
end end
end end
@ -134,7 +136,8 @@ describe Google::Auth::ClientId do
it 'should raise error' do it 'should raise error' do
expect { Google::Auth::ClientId.from_hash(config) }.to raise_error( expect { Google::Auth::ClientId.from_hash(config) }.to raise_error(
/Client secret can not be nil/) /Client secret can not be nil/
)
end end
end end
end end

View File

@ -37,7 +37,7 @@ require 'googleauth/compute_engine'
require 'spec_helper' require 'spec_helper'
describe Google::Auth::GCECredentials do describe Google::Auth::GCECredentials do
MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token' MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'.freeze
GCECredentials = Google::Auth::GCECredentials GCECredentials = Google::Auth::GCECredentials
before(:example) do before(:example) do
@ -64,8 +64,8 @@ describe Google::Auth::GCECredentials do
stub = stub_request(:get, MD_URI) stub = stub_request(:get, MD_URI)
.to_return(status: 404, .to_return(status: 404,
headers: { 'Metadata-Flavor' => 'Google' }) headers: { 'Metadata-Flavor' => 'Google' })
blk = proc { @client.fetch_access_token! } expect { @client.fetch_access_token! }
expect(&blk).to raise_error Signet::AuthorizationError .to raise_error Signet::AuthorizationError
expect(stub).to have_been_requested expect(stub).to have_been_requested
end end
@ -73,8 +73,8 @@ describe Google::Auth::GCECredentials do
stub = stub_request(:get, MD_URI) stub = stub_request(:get, MD_URI)
.to_return(status: 503, .to_return(status: 503,
headers: { 'Metadata-Flavor' => 'Google' }) headers: { 'Metadata-Flavor' => 'Google' })
blk = proc { @client.fetch_access_token! } expect { @client.fetch_access_token! }
expect(&blk).to raise_error Signet::AuthorizationError .to raise_error Signet::AuthorizationError
expect(stub).to have_been_requested expect(stub).to have_been_requested
end end
end end

View File

@ -45,7 +45,8 @@ describe '#get_application_default' do
@var_name = ENV_VAR @var_name = ENV_VAR
@credential_vars = [ @credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, CLIENT_ID_VAR, ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, CLIENT_ID_VAR,
CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR, ACCOUNT_TYPE_VAR] CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR, ACCOUNT_TYPE_VAR
]
@original_env_vals = {} @original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] } @credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
@home = ENV['HOME'] @home = ENV['HOME']
@ -74,10 +75,9 @@ describe '#get_application_default' do
Dir.mktmpdir do |dir| Dir.mktmpdir do |dir|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV['HOME'] = dir # no config present in this tmp dir ENV['HOME'] = dir # no config present in this tmp dir
blk = proc do expect do
Google::Auth.get_application_default(@scope, options) Google::Auth.get_application_default(@scope, options)
end end.to raise_error RuntimeError
expect(&blk).to raise_error RuntimeError
end end
expect(stub).to have_been_requested expect(stub).to have_been_requested
end end
@ -215,10 +215,9 @@ describe '#get_application_default' do
FileUtils.mkdir_p(File.dirname(key_path)) FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text) File.write(key_path, cred_json_text)
ENV[@var_name] = key_path ENV[@var_name] = key_path
blk = proc do expect do
Google::Auth.get_application_default(@scope, options) Google::Auth.get_application_default(@scope, options)
end end.to raise_error RuntimeError
expect(&blk).to raise_error RuntimeError
end end
end end
@ -229,20 +228,18 @@ describe '#get_application_default' do
FileUtils.mkdir_p(File.dirname(key_path)) FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text) File.write(key_path, cred_json_text)
ENV['HOME'] = dir ENV['HOME'] = dir
blk = proc do expect do
Google::Auth.get_application_default(@scope, options) Google::Auth.get_application_default(@scope, options)
end end.to raise_error RuntimeError
expect(&blk).to raise_error RuntimeError
end end
end end
it 'fails if env vars are set' do it 'fails if env vars are set' do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key] ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email] ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
blk = proc do expect do
Google::Auth.get_application_default(@scope, options) Google::Auth.get_application_default(@scope, options)
end end.to raise_error RuntimeError
expect(&blk).to raise_error RuntimeError
end end
end end
end end

View File

@ -39,13 +39,15 @@ describe Google::Auth::ScopeUtil do
it 'normalizes the email scope' do it 'normalizes the email scope' do
expect(normalized).to include( expect(normalized).to include(
'https://www.googleapis.com/auth/userinfo.email') 'https://www.googleapis.com/auth/userinfo.email'
)
expect(normalized).to_not include 'email' expect(normalized).to_not include 'email'
end end
it 'normalizes the profile scope' do it 'normalizes the profile scope' do
expect(normalized).to include( expect(normalized).to include(
'https://www.googleapis.com/auth/userinfo.profile') 'https://www.googleapis.com/auth/userinfo.profile'
)
expect(normalized).to_not include 'profile' expect(normalized).to_not include 'profile'
end end

View File

@ -139,7 +139,8 @@ describe Google::Auth::ServiceAccountCredentials do
end end
stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token') stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
.with(body: hash_including( .with(body: hash_including(
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'), 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'
),
&blk) &blk)
.to_return(body: body, .to_return(body: body,
status: 200, status: 200,
@ -164,7 +165,8 @@ describe Google::Auth::ServiceAccountCredentials do
before(:example) do before(:example) do
@var_name = ENV_VAR @var_name = ENV_VAR
@credential_vars = [ @credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR] ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
]
@original_env_vals = {} @original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] } @credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type] ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
@ -294,7 +296,8 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
before(:example) do before(:example) do
@var_name = ENV_VAR @var_name = ENV_VAR
@credential_vars = [ @credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR] ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
]
@original_env_vals = {} @original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] } @credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type] ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]

View File

@ -45,7 +45,8 @@ describe Signet::OAuth2::Client do
scope: 'https://www.googleapis.com/auth/userinfo.profile', scope: 'https://www.googleapis.com/auth/userinfo.profile',
issuer: 'app@example.com', issuer: 'app@example.com',
audience: 'https://accounts.google.com/o/oauth2/token', audience: 'https://accounts.google.com/o/oauth2/token',
signing_key: @key) signing_key: @key
)
end end
def make_auth_stubs(opts) def make_auth_stubs(opts)
@ -60,8 +61,8 @@ describe Signet::OAuth2::Client do
end end
stub_request(:post, 'https://accounts.google.com/o/oauth2/token') stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
.with(body: hash_including( .with(body: hash_including(
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'), 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'
&blk) ), &blk)
.to_return(body: body, .to_return(body: body,
status: 200, status: 200,
headers: { 'Content-Type' => 'application/json' }) headers: { 'Content-Type' => 'application/json' })

View File

@ -82,7 +82,8 @@ describe Google::Auth::UserAuthorizer do
it 'should include the callback uri' do it 'should include the callback uri' do
expect(URI(uri).query).to match( expect(URI(uri).query).to match(
%r{redirect_uri=https://www.example.com/oauth/callback}) %r{redirect_uri=https://www.example.com/oauth/callback}
)
end end
it 'should include the scope' do it 'should include the scope' do
@ -139,7 +140,8 @@ describe Google::Auth::UserAuthorizer do
MultiJson.dump( MultiJson.dump(
access_token: 'accesstoken', access_token: 'accesstoken',
refresh_token: 'refreshtoken', refresh_token: 'refreshtoken',
expiration_time_millis: 1_441_234_742_000) expiration_time_millis: 1_441_234_742_000
)
end end
context 'with a valid user id' do context 'with a valid user id' do
@ -150,7 +152,8 @@ describe Google::Auth::UserAuthorizer do
it 'should return an instance of UserRefreshCredentials' do it 'should return an instance of UserRefreshCredentials' do
expect(credentials).to be_instance_of( expect(credentials).to be_instance_of(
Google::Auth::UserRefreshCredentials) Google::Auth::UserRefreshCredentials
)
end end
it 'should return credentials with a valid refresh token' do it 'should return credentials with a valid refresh token' do
@ -226,7 +229,8 @@ describe Google::Auth::UserAuthorizer do
it 'should persist the expiry as milliseconds' do it 'should persist the expiry as milliseconds' do
expected_expiry = expiry * 1000 expected_expiry = expiry * 1000
expect(MultiJson.load(token_json)['expiration_time_millis']).to eql( expect(MultiJson.load(token_json)['expiration_time_millis']).to eql(
expected_expiry) expected_expiry
)
end end
end end
@ -240,12 +244,14 @@ describe Google::Auth::UserAuthorizer do
before(:example) do before(:example) do
stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token') stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
.to_return(body: token_json, status: 200, headers: { .to_return(body: token_json, status: 200, headers: {
'Content-Type' => 'application/json' }) 'Content-Type' => 'application/json'
})
end end
it 'should exchange a code for credentials' do it 'should exchange a code for credentials' do
credentials = authorizer.get_credentials_from_code( credentials = authorizer.get_credentials_from_code(
user_id: 'user1', code: 'code') user_id: 'user1', code: 'code'
)
expect(credentials.access_token).to eq '1/abc123' expect(credentials.access_token).to eq '1/abc123'
end end
@ -256,7 +262,8 @@ describe Google::Auth::UserAuthorizer do
it 'should store credentials when requested' do it 'should store credentials when requested' do
authorizer.get_and_store_credentials_from_code( authorizer.get_and_store_credentials_from_code(
user_id: 'user1', code: 'code') user_id: 'user1', code: 'code'
)
expect(token_store.load('user1')).to_not be_nil expect(token_store.load('user1')).to_not be_nil
end end
end end
@ -286,20 +293,23 @@ describe Google::Auth::UserAuthorizer do
MultiJson.dump( MultiJson.dump(
access_token: 'accesstoken', access_token: 'accesstoken',
refresh_token: 'refreshtoken', refresh_token: 'refreshtoken',
expiration_time_millis: 1_441_234_742_000) expiration_time_millis: 1_441_234_742_000
)
end end
before(:example) do before(:example) do
token_store.store('user1', token_json) token_store.store('user1', token_json)
stub_request( stub_request(
:get, 'https://accounts.google.com/o/oauth2/revoke?token=refreshtoken') :get, 'https://accounts.google.com/o/oauth2/revoke?token=refreshtoken'
)
.to_return(status: 200) .to_return(status: 200)
end end
it 'should revoke the grant' do it 'should revoke the grant' do
authorizer.revoke_authorization('user1') authorizer.revoke_authorization('user1')
expect(a_request( expect(a_request(
:get, 'https://accounts.google.com/o/oauth2/revoke?token=refreshtoken')) :get, 'https://accounts.google.com/o/oauth2/revoke?token=refreshtoken'
))
.to have_been_made .to have_been_made
end end

View File

@ -87,7 +87,8 @@ describe Google::Auth::UserRefreshCredentials do
@var_name = ENV_VAR @var_name = ENV_VAR
@credential_vars = [ @credential_vars = [
ENV_VAR, CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR, ENV_VAR, CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR,
ACCOUNT_TYPE_VAR] ACCOUNT_TYPE_VAR
]
@original_env_vals = {} @original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] } @credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
@scope = 'https://www.googleapis.com/auth/userinfo.profile' @scope = 'https://www.googleapis.com/auth/userinfo.profile'
@ -288,7 +289,8 @@ describe Google::Auth::UserRefreshCredentials do
it 'raises an authorization error' do it 'raises an authorization error' do
stub stub
expect { @client.revoke! }.to raise_error( expect { @client.revoke! }.to raise_error(
Signet::AuthorizationError) Signet::AuthorizationError
)
end end
end end
end end

View File

@ -52,13 +52,15 @@ describe Google::Auth::WebUserAuthorizer do
let(:env) do let(:env) do
Rack::MockRequest.env_for( Rack::MockRequest.env_for(
'http://example.com:8080/test', 'http://example.com:8080/test',
'REMOTE_ADDR' => '10.10.10.10') 'REMOTE_ADDR' => '10.10.10.10'
)
end end
let(:request) { Rack::Request.new(env) } let(:request) { Rack::Request.new(env) }
it 'should include current url in state' do it 'should include current url in state' do
url = authorizer.get_authorization_url(request: request) url = authorizer.get_authorization_url(request: request)
expect(url).to match( expect(url).to match(
%r{%22current_uri%22:%22http://example.com:8080/test%22}) %r{%22current_uri%22:%22http://example.com:8080/test%22}
)
end end
it 'should include request forgery token in state' do it 'should include request forgery token in state' do
@ -76,20 +78,23 @@ describe Google::Auth::WebUserAuthorizer do
it 'should resolve callback against base URL' do it 'should resolve callback against base URL' do
url = authorizer.get_authorization_url(request: request) url = authorizer.get_authorization_url(request: request)
expect(url).to match( expect(url).to match(
%r{redirect_uri=http://example.com:8080/oauth2callback}) %r{redirect_uri=http://example.com:8080/oauth2callback}
)
end end
it 'should allow overriding the current URL' do it 'should allow overriding the current URL' do
url = authorizer.get_authorization_url( url = authorizer.get_authorization_url(
request: request, request: request,
redirect_to: '/foo') redirect_to: '/foo'
)
expect(url).to match %r{%22current_uri%22:%22/foo%22} expect(url).to match %r{%22current_uri%22:%22/foo%22}
end end
it 'should pass through login hint' do it 'should pass through login hint' do
url = authorizer.get_authorization_url( url = authorizer.get_authorization_url(
request: request, request: request,
login_hint: 'user@example.com') login_hint: 'user@example.com'
)
expect(url).to match(/login_hint=user@example.com/) expect(url).to match(/login_hint=user@example.com/)
end end
end end
@ -113,7 +118,8 @@ describe Google::Auth::WebUserAuthorizer do
'http://example.com:8080/oauth2callback?code=authcode&'\ 'http://example.com:8080/oauth2callback?code=authcode&'\
'state=%7B%22current_uri%22%3A%22%2Ffoo%22%2C%22'\ 'state=%7B%22current_uri%22%3A%22%2Ffoo%22%2C%22'\
'session_id%22%3A%22abc%22%7D', 'session_id%22%3A%22abc%22%7D',
'REMOTE_ADDR' => '10.10.10.10') 'REMOTE_ADDR' => '10.10.10.10'
)
end end
let(:request) { Rack::Request.new(env) } let(:request) { Rack::Request.new(env) }
@ -123,7 +129,8 @@ describe Google::Auth::WebUserAuthorizer do
it 'should return credentials when valid code present' do it 'should return credentials when valid code present' do
expect(credentials).to be_instance_of( expect(credentials).to be_instance_of(
Google::Auth::UserRefreshCredentials) Google::Auth::UserRefreshCredentials
)
end end
it 'should return next URL to redirect to' do it 'should return next URL to redirect to' do