From fb67a021e7e1bb430af73104807f92cc25adc6fe Mon Sep 17 00:00:00 2001 From: Jeff Posnick Date: Wed, 29 May 2013 15:31:44 -0400 Subject: [PATCH] Added Google::APIClient::FileStorage, to save OAuth 2 credentials to disk This is a (potentially rough) bit of code to persist OAuth 2 credentials to disk, similar to http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client. file.Storage-class.html It can be used in the following manner, which roughly translates to what the Python client library code looks like. file_storage = Google::APIClient::FileStorage.new("#{$0}-oauth2.json") if file_storage.authorization.nil? client_secrets = Google::APIClient::ClientSecrets.load flow = Google::APIClient::InstalledAppFlow.new( :client_id => client_secrets.client_id, :client_secret => client_secrets.client_secret, :scope => [SCOPE1, SCOPE2] ) client.authorization = flow.authorize(file_storage) else client.authorization = file_storage.authorization end --- lib/google/api_client/auth/file_storage.rb | 87 +++++++++++++++++++++ lib/google/api_client/auth/installed_app.rb | 9 ++- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 lib/google/api_client/auth/file_storage.rb diff --git a/lib/google/api_client/auth/file_storage.rb b/lib/google/api_client/auth/file_storage.rb new file mode 100644 index 000000000..049ef965a --- /dev/null +++ b/lib/google/api_client/auth/file_storage.rb @@ -0,0 +1,87 @@ +# Copyright 2013 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'json' +require 'signet/oauth_2/client' + +module Google + class APIClient + ## + # Represents cached OAuth 2 tokens stored on local disk in a + # JSON serialized file. Meant to resemble the serialized format + # http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html + # + class FileStorage + # @return [String] Path to the credentials file. + attr_accessor :path + + # @return [Signet::OAuth2::Client] Path to the credentials file. + attr_reader :authorization + + ## + # Initializes the FileStorage object. + # + # @param [String] path + # Path to the credentials file. + def initialize(path) + @path = path + self.load_credentials + end + + ## + # Attempt to read in credentials from the specified file. + def load_credentials + if File.exist? self.path + File.open(self.path, 'r') do |file| + cached_credentials = JSON.load(file) + @authorization = Signet::OAuth2::Client.new(cached_credentials) + @authorization.issued_at = Time.at(cached_credentials['issued_at']) + if @authorization.expired? + @authorization.fetch_access_token! + self.write_credentials + end + end + end + end + + ## + # Write the credentials to the specified file. + # + # @param [Signet::OAuth2::Client] authorization + # Optional authorization instance. If not provided, the authorization + # already associated with this instance will be written. + def write_credentials(authorization=nil) + @authorization = authorization unless authorization.nil? + + unless @authorization.refresh_token.nil? + hash = {} + %w'access_token + authorization_uri + client_id + client_secret + expires_in + refresh_token + token_credential_uri'.each do |var| + hash[var] = @authorization.instance_variable_get("@#{var}") + end + hash['issued_at'] = @authorization.issued_at.to_i + + File.open(self.path, 'w', 0600) do |file| + file.write(hash.to_json) + end + end + end + end + end +end diff --git a/lib/google/api_client/auth/installed_app.rb b/lib/google/api_client/auth/installed_app.rb index 4f7bf11a4..2edf36332 100644 --- a/lib/google/api_client/auth/installed_app.rb +++ b/lib/google/api_client/auth/installed_app.rb @@ -77,9 +77,13 @@ module Google ## # Request authorization. Opens a browser and waits for response. # + # @param [Google::APIClient::FileStorage] storage + # Optional object that responds to :write_credentials, used to serialize + # the OAuth 2 credentials after completing the flow. + # # @return [Signet::OAuth2::Client] # Authorization instance, nil if user cancelled. - def authorize + def authorize(storage=nil) auth = @authorization server = WEBrick::HTTPServer.new( @@ -103,6 +107,9 @@ module Google Launchy.open(auth.authorization_uri.to_s) server.start if @authorization.access_token + if storage.respond_to?(:write_credentials) + storage.write_credentials(@authorization) + end return @authorization else return nil