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 CallbackApp instead.

See Also:

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'



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

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.



68
69
70
# File 'lib/googleauth/web_user_authorizer.rb', line 68

def default
  @default
end

Class Method Details

.extract_callback_state(request) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/googleauth/web_user_authorizer.rb', line 194

def self.extract_callback_state request
  state = MultiJson.load(request.params[STATE_PARAM] || "{}")
  redirect_uri = state[CURRENT_URI_KEY]
  callback_state = {
    AUTH_CODE_KEY  => request.params[AUTH_CODE_KEY],
    ERROR_CODE_KEY => request.params[ERROR_CODE_KEY],
    SESSION_ID_KEY => state[SESSION_ID_KEY],
    SCOPE_KEY      => request.params[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



82
83
84
85
86
# File 'lib/googleauth/web_user_authorizer.rb', line 82

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

Raises:

  • (Signet::AuthorizationError)


216
217
218
219
220
221
222
223
224
# File 'lib/googleauth/web_user_authorizer.rb', line 216

def self.validate_callback_state state, request
  raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR if state[AUTH_CODE_KEY].nil?
  if state[ERROR_CODE_KEY]
    raise Signet::AuthorizationError,
          format(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.

  • state (Hash)

    Optional key-values to be returned to the oauth callback.

Returns:

  • (String)

    Authorization url

Raises:



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/googleauth/web_user_authorizer.rb', line 143

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?

  state = options[:state] || {}

  redirect_to = options[:redirect_to] || request.url
  request.session[XSRF_KEY] = SecureRandom.base64
  options[:state] = MultiJson.dump(state.merge(
                                     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 = nil, scope = nil) ⇒ Google::Auth::UserRefreshCredentials

Fetch stored credentials for the user from the given request session.

Parameters:

  • user_id (String)

    Unique ID of the user for loading/storing credentials.

  • request (Rack::Request) (defaults to: nil)

    Current request. Optional. If omitted, this will attempt to fall back on the base class behavior of reading from the token store.

  • 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



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/googleauth/web_user_authorizer.rb', line 176

def get_credentials user_id, request = nil, 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:



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/googleauth/web_user_authorizer.rb', line 112

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