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
Generate the code verifier needed to be sent while fetching authorization URL.
# File lib/googleauth/user_authorizer.rb, line 267 def self.generate_code_verifier random_number = rand 32..96 SecureRandom.alphanumeric random_number end
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] legacy_callback_uri
URL (either absolute or relative) of the auth callback. Defaults to '/oauth2callback'. @deprecated This field is deprecated. Instead, use the keyword argument callback_uri.
@param [String] code_verifier
Random string of 43-128 chars used to verify the key exchange using PKCE.
# File lib/googleauth/user_authorizer.rb, line 66 def initialize client_id, scope, token_store, legacy_callback_uri = nil, callback_uri: nil, code_verifier: 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 = legacy_callback_uri || callback_uri || "/oauth2callback" @code_verifier = code_verifier end
Public Instance Methods
The code verifier for PKCE for OAuth 2.0. When set, the authorization URI will contain the Code Challenge and Code Challenge Method querystring parameters, and the token URI will contain the Code Verifier parameter.
@param [String|nil] new_code_erifier
# File lib/googleauth/user_authorizer.rb, line 261 def code_verifier= new_code_verifier @code_verifier = new_code_verifier end
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 213 def get_and_store_credentials_from_code options = {} credentials = get_credentials_from_code options store_credentials options[:user_id], credentials end
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 136 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 format(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 return monitor_credentials user_id, credentials if credentials.includes_scope? scope nil end
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.
@param [Hash] additional_parameters
Additional parameters to be added to the post body of token endpoint request.
@return [Google::Auth::UserRefreshCredentials]
Credentials if exchange is successful
# File lib/googleauth/user_authorizer.rb, line 177 def get_credentials_from_code options = {} user_id = options[:user_id] code = options[:code] scope = options[:scope] || @scope base_url = options[:base_url] options[:additional_parameters] ||= {} options[:additional_parameters].merge!({ code_verifier: @code_verifier }) credentials = UserRefreshCredentials.new( client_id: @client_id.id, client_secret: @client_id.secret, redirect_uri: redirect_uri_for(base_url), scope: scope, additional_parameters: options[:additional_parameters] ) credentials.code = code credentials.fetch_access_token!({}) monitor_credentials user_id, credentials end
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 243 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
# File lib/googleauth/user_authorizer.rb, line 322 def code_challenge_method "S256" end
# File lib/googleauth/user_authorizer.rb, line 317 def generate_code_challenge code_verifier digest = Digest::SHA256.digest code_verifier Base64.urlsafe_encode64 digest, padding: false end
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 293 def monitor_credentials user_id, credentials credentials.on_refresh do |cred| store_credentials user_id, cred end credentials end
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 306 def redirect_uri_for base_url return @callback_uri if uri_is_postmessage?(@callback_uri) || !URI(@callback_uri).scheme.nil? raise format(MISSING_ABSOLUTE_URL_ERROR, @callback_uri) if base_url.nil? || URI(base_url).scheme.nil? URI.join(base_url, @callback_uri).to_s end
@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 279 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
Check if URI is Google’s postmessage flow (not a valid redirect_uri by spec, but allowed)
# File lib/googleauth/user_authorizer.rb, line 313 def uri_is_postmessage? uri uri.to_s.casecmp("postmessage").zero? end