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

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’



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

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.



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

def default
  @default
end

Class Method Details

.extract_callback_state(request) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
# File 'lib/googleauth/web_user_authorizer.rb', line 202

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



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

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



224
225
226
227
228
229
230
231
232
233
# File 'lib/googleauth/web_user_authorizer.rb', line 224

def self.validate_callback_state(state, request)
  if state[AUTH_CODE_KEY].nil?
    fail Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR
  elsif state[ERROR_CODE_KEY]
    fail Signet::AuthorizationError,
         sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
  elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
    fail 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



156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/googleauth/web_user_authorizer.rb', line 156

def get_authorization_url(options = {})
  options = options.dup
  request = options[:request]
  fail NIL_REQUEST_ERROR if request.nil?
  fail 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



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/googleauth/web_user_authorizer.rb', line 185

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:



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

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