Added Google::APIClient::FileStorage, to save OAuth 2 credentials to disk
authorJeff Posnick <jeffy@google.com>
Wed, 29 May 2013 19:31:44 +0000 (15:31 -0400)
committerJeff Posnick <jeffy@google.com>
Wed, 29 May 2013 19:31:44 +0000 (15:31 -0400)
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 [new file with mode: 0644]
lib/google/api_client/auth/installed_app.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 (file)
index 0000000..049ef96
--- /dev/null
@@ -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
index 4f7bf11a447a2fb7406b2f73984f0392bc22542f..2edf36332bae4dc89cb2b6af5b795ec19998f38f 100644 (file)
@@ -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