class Google::Auth::UserAuthorizer

Handles an interactive 3-Legged-OAuth2 (3LO) user consent authorization.

Example usage for a simple command line app:

credentials = authorizer.get_credentials(user_id)
if credentials.nil?
  url = authorizer.get_authorization_url(
    base_url: OOB_URI)
  puts "Open the following URL in the browser and enter the " +
       "resulting code after authorization"
  puts url
  code = gets
  credentials = authorizer.get_and_store_credentials_from_code(
    user_id: user_id, code: code, base_url: OOB_URI)
end
# Credentials ready to use, call APIs
...

Constants

MISMATCHED_CLIENT_ID_ERROR
MISSING_ABSOLUTE_URL_ERROR
NIL_CLIENT_ID_ERROR
NIL_SCOPE_ERROR
NIL_TOKEN_STORE_ERROR
NIL_USER_ID_ERROR

Public Class Methods

new(client_id, scope, token_store, callback_uri = nil) click to toggle source

Initialize the authorizer

@param [Google::Auth::ClientID] client_id

Configured ID & secret for this application

@param [String, Array<String>] scope

Authorization scope to request

@param [Google::Auth::Stores::TokenStore] token_store

Backing storage for persisting user credentials

@param [String] callback_uri

URL (either absolute or relative) of the auth callback.
Defaults to '/oauth2callback'
# File lib/googleauth/user_authorizer.rb, line 75
def initialize(client_id, scope, token_store, callback_uri = nil)
  raise NIL_CLIENT_ID_ERROR if client_id.nil?
  raise NIL_SCOPE_ERROR if scope.nil?

  @client_id = client_id
  @scope = Array(scope)
  @token_store = token_store
  @callback_uri = callback_uri || '/oauth2callback'
end

Public Instance Methods

get_and_store_credentials_from_code(options = {}) click to toggle source

Exchanges an authorization code returned in the oauth callback. Additionally, stores the resulting credentials in the token store if the exchange is successful.

@param [String] user_id

Unique ID of the user for loading/storing credentials.

@param [String] code

The authorization code from the OAuth callback

@param [String, Array<String>] scope

Authorization scope requested. Overrides the instance
scopes if not nil.

@param [String] base_url

Absolute URL to resolve the configured callback uri against.
Required if the configured
callback uri is a relative.

@return [Google::Auth::UserRefreshCredentials]

Credentials if exchange is successful
# File lib/googleauth/user_authorizer.rb, line 199
def get_and_store_credentials_from_code(options = {})
  credentials = get_credentials_from_code(options)
  store_credentials(options[:user_id], credentials)
end
get_authorization_url(options = {}) click to toggle source

Build the URL for requesting authorization.

@param [String] login_hint

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

@param [String] state

Opaque state value to be returned to the oauth callback.

@param [String] base_url

Absolute URL to resolve the configured callback uri against. Required
if the configured callback uri is a relative.

@param [String, Array<String>] scope

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

@return [String]

Authorization url
# File lib/googleauth/user_authorizer.rb, line 100
def get_authorization_url(options = {})
  scope = options[:scope] || @scope
  credentials = UserRefreshCredentials.new(
    client_id: @client_id.id,
    client_secret: @client_id.secret,
    scope: scope
  )
  redirect_uri = redirect_uri_for(options[:base_url])
  url = credentials.authorization_uri(access_type: 'offline',
                                      redirect_uri: redirect_uri,
                                      approval_prompt: 'force',
                                      state: options[:state],
                                      include_granted_scopes: true,
                                      login_hint: options[:login_hint])
  url.to_s
end
get_credentials(user_id, scope = nil) click to toggle source

Fetch stored credentials for the user.

@param [String] user_id

Unique ID of the user for loading/storing credentials.

@param [Array<String>, String] scope

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

@return [Google::Auth::UserRefreshCredentials]

Stored credentials, nil if none present
# File lib/googleauth/user_authorizer.rb, line 126
def get_credentials(user_id, scope = nil)
  saved_token = stored_token(user_id)
  return nil if saved_token.nil?
  data = MultiJson.load(saved_token)

  if data.fetch('client_id', @client_id.id) != @client_id.id
    raise sprintf(MISMATCHED_CLIENT_ID_ERROR,
                  data['client_id'], @client_id.id)
  end

  credentials = UserRefreshCredentials.new(
    client_id: @client_id.id,
    client_secret: @client_id.secret,
    scope: data['scope'] || @scope,
    access_token: data['access_token'],
    refresh_token: data['refresh_token'],
    expires_at: data.fetch('expiration_time_millis', 0) / 1000
  )
  scope ||= @scope
  if credentials.includes_scope?(scope)
    return monitor_credentials(user_id, credentials)
  end
  nil
end
get_credentials_from_code(options = {}) click to toggle source

Exchanges an authorization code returned in the oauth callback

@param [String] user_id

Unique ID of the user for loading/storing credentials.

@param [String] code

The authorization code from the OAuth callback

@param [String, Array<String>] scope

Authorization scope requested. Overrides the instance
scopes if not nil.

@param [String] base_url

Absolute URL to resolve the configured callback uri against.
Required if the configured
callback uri is a relative.

@return [Google::Auth::UserRefreshCredentials]

Credentials if exchange is successful
# File lib/googleauth/user_authorizer.rb, line 166
def get_credentials_from_code(options = {})
  user_id = options[:user_id]
  code = options[:code]
  scope = options[:scope] || @scope
  base_url = options[:base_url]
  credentials = UserRefreshCredentials.new(
    client_id: @client_id.id,
    client_secret: @client_id.secret,
    redirect_uri: redirect_uri_for(base_url),
    scope: scope
  )
  credentials.code = code
  credentials.fetch_access_token!({})
  monitor_credentials(user_id, credentials)
end
revoke_authorization(user_id) click to toggle source

Revokes a user's credentials. This both revokes the actual grant as well as removes the token from the token store.

@param [String] user_id

Unique ID of the user for loading/storing credentials.
# File lib/googleauth/user_authorizer.rb, line 209
def revoke_authorization(user_id)
  credentials = get_credentials(user_id)
  if credentials
    begin
      @token_store.delete(user_id)
    ensure
      credentials.revoke!
    end
  end
  nil
end
store_credentials(user_id, credentials) click to toggle source

Store credentials for a user. Generally not required to be called directly, but may be used to migrate tokens from one store to another.

@param [String] user_id

Unique ID of the user for loading/storing credentials.

@param [Google::Auth::UserRefreshCredentials] credentials

Credentials to store.
# File lib/googleauth/user_authorizer.rb, line 229
def store_credentials(user_id, credentials)
  json = MultiJson.dump(
    client_id: credentials.client_id,
    access_token: credentials.access_token,
    refresh_token: credentials.refresh_token,
    scope: credentials.scope,
    expiration_time_millis: credentials.expires_at.to_i * 1000
  )
  @token_store.store(user_id, json)
  credentials
end

Private Instance Methods

monitor_credentials(user_id, credentials) click to toggle source

Begin watching a credential for refreshes so the access token can be saved.

@param [String] user_id

Unique ID of the user for loading/storing credentials.

@param [Google::Auth::UserRefreshCredentials] credentials

Credentials to store.
# File lib/googleauth/user_authorizer.rb, line 262
def monitor_credentials(user_id, credentials)
  credentials.on_refresh do |cred|
    store_credentials(user_id, cred)
  end
  credentials
end
redirect_uri_for(base_url) click to toggle source

Resolve the redirect uri against a base.

@param [String] base_url

Absolute URL to resolve the callback against if necessary.

@return [String]

Redirect URI
# File lib/googleauth/user_authorizer.rb, line 275
def redirect_uri_for(base_url)
  return @callback_uri unless URI(@callback_uri).scheme.nil?
  if base_url.nil? || URI(base_url).scheme.nil?
    raise sprintf(MISSING_ABSOLUTE_URL_ERROR, @callback_uri)
  end
  URI.join(base_url, @callback_uri).to_s
end
stored_token(user_id) click to toggle source

@private Fetch stored token with given user_id

@param [String] user_id

Unique ID of the user for loading/storing credentials.

@return [String] The saved token from @token_store

# File lib/googleauth/user_authorizer.rb, line 248
def stored_token(user_id)
  raise NIL_USER_ID_ERROR if user_id.nil?
  raise NIL_TOKEN_STORE_ERROR if @token_store.nil?

  @token_store.load(user_id)
end