Class: Google::Auth::WebUserAuthorizer

Inherits:
UserAuthorizer show all
Defined in:
lib/googleauth/web_user_authorizer.rb

Overview

Note:

Requires sessions are enabled

Varation on UserAuthorizer adapted for Rack based web applications.

Example usage:

 get('/') do
   user_id = request.session['user_email']
   credentials = authorizer.get_credentials(user_id, request)
   if credentials.nil?
     redirect authorizer.get_authorization_url(user_id: user_id,
                                               request: request)
   end
   # Credentials are valid, can call APIs
   ...
end

get('/oauth2callback') do
  url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
    request)
  redirect url
end

Instead of implementing the callback directly, applications are encouraged to use Google::Auth::Web::AuthCallbackApp instead.

For rails apps, see ControllerHelpers

See Also:

  • {Google{Google::Auth{Google::Auth::AuthCallbackApp}
  • {Google{Google::Auth{Google::Auth::ControllerHelpers}

Defined Under Namespace

Classes: CallbackApp

Constant Summary collapse

STATE_PARAM =
'state'.freeze
AUTH_CODE_KEY =
'code'.freeze
ERROR_CODE_KEY =
'error'.freeze
SESSION_ID_KEY =
'session_id'.freeze
CALLBACK_STATE_KEY =
'g-auth-callback'.freeze
CURRENT_URI_KEY =
'current_uri'.freeze
XSRF_KEY =
'g-xsrf-token'.freeze
SCOPE_KEY =
'scope'.freeze
NIL_REQUEST_ERROR =
'Request is required.'.freeze
NIL_SESSION_ERROR =
'Sessions must be enabled'.freeze
MISSING_AUTH_CODE_ERROR =
'Missing authorization code in request'.freeze
AUTHORIZATION_ERROR =
'Authorization error: %s'.freeze
INVALID_STATE_TOKEN_ERROR =
'State token does not match expected value'.freeze

Constants inherited from UserAuthorizer

UserAuthorizer::MISMATCHED_CLIENT_ID_ERROR, UserAuthorizer::MISSING_ABSOLUTE_URL_ERROR, UserAuthorizer::NIL_CLIENT_ID_ERROR, UserAuthorizer::NIL_SCOPE_ERROR, UserAuthorizer::NIL_TOKEN_STORE_ERROR, UserAuthorizer::NIL_USER_ID_ERROR

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from UserAuthorizer

#get_and_store_credentials_from_code, #get_credentials_from_code, #revoke_authorization, #store_credentials

Constructor Details

#initialize(client_id, scope, token_store, callback_uri = nil) ⇒ WebUserAuthorizer

Initialize the authorizer

Parameters:

  • client_id (Google::Auth::ClientID)

    Configured ID & secret for this application

  • scope (String, Array<String>)

    Authorization scope to request

  • token_store (Google::Auth::Stores::TokenStore)

    Backing storage for persisting user credentials

  • callback_uri (String) (defaults to: nil)

    URL (either absolute or relative) of the auth callback. Defaults to '/oauth2callback'


117
118
119
# File 'lib/googleauth/web_user_authorizer.rb', line 117

def initialize(client_id, scope, token_store, callback_uri = nil)
  super(client_id, scope, token_store, callback_uri)
end

Class Attribute Details

.defaultObject

Returns the value of attribute default


86
87
88
# File 'lib/googleauth/web_user_authorizer.rb', line 86

def default
  @default
end

Class Method Details

.extract_callback_state(request) ⇒ Object


207
208
209
210
211
212
213
214
215
216
217
# File 'lib/googleauth/web_user_authorizer.rb', line 207

def self.extract_callback_state(request)
  state = MultiJson.load(request[STATE_PARAM] || '{}')
  redirect_uri = state[CURRENT_URI_KEY]
  callback_state = {
    AUTH_CODE_KEY => request[AUTH_CODE_KEY],
    ERROR_CODE_KEY => request[ERROR_CODE_KEY],
    SESSION_ID_KEY => state[SESSION_ID_KEY],
    SCOPE_KEY => request[SCOPE_KEY]
  }
  [callback_state, redirect_uri]
end

.handle_auth_callback_deferred(request) ⇒ Object

Handle the result of the oauth callback. This version defers the exchange of the code by temporarily stashing the results in the user's session. This allows apps to use the generic CallbackApp handler for the callback without any additional customization.

Apps that wish to handle the callback directly should use #handle_auth_callback instead.

Parameters:

  • request (Rack::Request)

    Current request


100
101
102
103
104
# File 'lib/googleauth/web_user_authorizer.rb', line 100

def self.handle_auth_callback_deferred(request)
  callback_state, redirect_uri = extract_callback_state(request)
  request.session[CALLBACK_STATE_KEY] = MultiJson.dump(callback_state)
  redirect_uri
end

.validate_callback_state(state, request) ⇒ Object

Verifies the results of an authorization callback

Parameters:

  • state (Hash)

    Callback state

  • request (Rack::Request)

    Current request

Options Hash (state):

  • AUTH_CODE_KEY (String)

    The authorization code

  • ERROR_CODE_KEY (String)

    Error message if failed


229
230
231
232
233
234
235
236
237
238
# File 'lib/googleauth/web_user_authorizer.rb', line 229

def self.validate_callback_state(state, request)
  if state[AUTH_CODE_KEY].nil?
    raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR
  elsif state[ERROR_CODE_KEY]
    raise Signet::AuthorizationError,
          sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
  elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
    raise Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
  end
end

Instance Method Details

#get_authorization_url(options = {}) ⇒ String

Build the URL for requesting authorization.

Parameters:

  • login_hint (String)

    Login hint if need to authorize a specific account. Should be a user's email address or unique profile ID.

  • request (Rack::Request)

    Current request

  • redirect_to (String)

    Optional URL to proceed to after authorization complete. Defaults to the current URL.

  • scope (String, Array<String>)

    Authorization scope to request. Overrides the instance scopes if not nil.

Returns:

  • (String)

    Authorization url

Raises:


159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/googleauth/web_user_authorizer.rb', line 159

def get_authorization_url(options = {})
  options = options.dup
  request = options[:request]
  raise NIL_REQUEST_ERROR if request.nil?
  raise NIL_SESSION_ERROR if request.session.nil?

  redirect_to = options[:redirect_to] || request.url
  request.session[XSRF_KEY] = SecureRandom.base64
  options[:state] = MultiJson.dump(
    SESSION_ID_KEY => request.session[XSRF_KEY],
    CURRENT_URI_KEY => redirect_to
  )
  options[:base_url] = request.url
  super(options)
end

#get_credentials(user_id, request, scope = nil) ⇒ Google::Auth::UserRefreshCredentials

Fetch stored credentials for the user.

Parameters:

  • user_id (String)

    Unique ID of the user for loading/storing credentials.

  • request (Rack::Request)

    Current request

  • scope (Array<String>, String) (defaults to: nil)

    If specified, only returns credentials that have all the \ requested scopes

Returns:

Raises:

  • (Signet::AuthorizationError)

    May raise an error if an authorization code is present in the session and exchange of the code fails


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/googleauth/web_user_authorizer.rb', line 189

def get_credentials(user_id, request, scope = nil)
  if request.session.key?(CALLBACK_STATE_KEY)    # Note - in theory, no need to check required scope as this is
    # expected to be called immediately after a return from authorization

    state_json = request.session.delete(CALLBACK_STATE_KEY)
    callback_state = MultiJson.load(state_json)
    WebUserAuthorizer.validate_callback_state(callback_state, request)
    get_and_store_credentials_from_code(
      user_id: user_id,
      code: callback_state[AUTH_CODE_KEY],
      scope: callback_state[SCOPE_KEY],
      base_url: request.url
    )
  else
    super(user_id, scope)
  end
end

#handle_auth_callback(user_id, request) ⇒ Google::Auth::UserRefreshCredentials, String

Handle the result of the oauth callback. Exchanges the authorization code from the request and persists to storage.

Parameters:

  • user_id (String)

    Unique ID of the user for loading/storing credentials.

  • request (Rack::Request)

    Current request

Returns:


130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/googleauth/web_user_authorizer.rb', line 130

def handle_auth_callback(user_id, request)
  callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state(
    request
  )
  WebUserAuthorizer.validate_callback_state(callback_state, request)
  credentials = get_and_store_credentials_from_code(
    user_id: user_id,
    code: callback_state[AUTH_CODE_KEY],
    scope: callback_state[SCOPE_KEY],
    base_url: request.url
  )
  [credentials, redirect_uri]
end