Search code examples
rubygoogle-apigoogle-api-ruby-client

Google API Client secrets failing to load issued_at


I'm getting the following error message when I try to do an OAuth2 connection to google.

.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/google-api-client-0.7.1/lib/google/api_client/auth/file_storage.rb:49:in `at': can't convert nil into an exact number (TypeError)

Looking at the source this is trying to read a cached credentials file and is failing to parse an attribute called issued_at.

I initially set up my app with the wrong port in the google developer console. Now I've updated the client_secrets.json but I'm continually getting this error.

My code is trying doing the calendar example from the google site, but converted to use the admin directory API, but it isn't getting beyond the auth step.

Where is this cached value coming from?

require 'rubygems'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/file_storage'
require 'sinatra'
require 'logger'

enable :sessions

CREDENTIAL_STORE_FILE = "client_secrets.json"

def logger; settings.logger end

def api_client; settings.api_client; end

def directory_api; settings.directory; end

def user_credentials
  # Build a per-request oauth credential based on token stored in session
  # which allows us to use a shared API client.
  @authorization ||= (
    auth = api_client.authorization.dup
    auth.redirect_uri = to('/oauth2callback')
    auth.update_token!(session)
    auth
  )
end

configure do
  log_file = File.open('directory.log', 'a+')
  log_file.sync = true
  logger = Logger.new(log_file)
  logger.level = Logger::DEBUG

  client = Google::APIClient.new(
    :application_name => 'Ruby Directory sample',
    :application_version => '1.0.0')

  puts "store file : #{CREDENTIAL_STORE_FILE}"

  file_storage = Google::APIClient::FileStorage.new(CREDENTIAL_STORE_FILE)
  if file_storage.authorization.nil?
    client_secrets = Google::APIClient::ClientSecrets.load
    client.authorization = client_secrets.to_authorization
    client.authorization.scope = 'https://www.googleapis.com/auth/admin.directory.user'
  else
    client.authorization = file_storage.authorization
  end

  # Since we're saving the API definition to the settings, we're only retrieving
  # it once (on server start) and saving it between requests.
  # If this is still an issue, you could serialize the object and load it on
  # subsequent runs.
  directory = client.discovered_api('admin', "directory_v1")

  set :logger, logger
  set :api_client, client
  set :directory, directory
end

before do
  # Ensure user has authorized the app
  unless user_credentials.access_token || request.path_info =~ /\A\/oauth2/
    redirect to('/oauth2authorize')
  end
end

after do
  # Serialize the access/refresh token to the session and credential store.
  session[:access_token] = user_credentials.access_token
  session[:refresh_token] = user_credentials.refresh_token
  session[:expires_in] = user_credentials.expires_in
  session[:issued_at] = user_credentials.issued_at

  file_storage = Google::APIClient::FileStorage.new(CREDENTIAL_STORE_FILE)
  file_storage.write_credentials(user_credentials)
end

get '/oauth2authorize' do
  # Request authorization
  redirect user_credentials.authorization_uri.to_s, 303
end

get '/oauth2callback' do
  # Exchange token
  user_credentials.code = params[:code] if params[:code]
  user_credentials.fetch_access_token!
  redirect to('/')
end

get '/' do

    result = api_client.execute(:api_method => directory.users.list)

  # # Fetch list of events on the user's default calandar
  # result = api_client.execute(:api_method => calendar_api.events.list,
  #                             :parameters => {'calendarId' => 'primary'},
  #                             :authorization => user_credentials)
  [result.status, {'Content-Type' => 'application/json'}, result.data.to_json]
end

Solution

  • Change the line to CREDENTIAL_STORE_FILE = "#{$0}-oauth2.json" and then rename the json you downloaded from the Google dashboard to client_secrets.json per convention. CREDENTIAL_STORE_FILE is where your OAuth tokens get stored and is created by the FileStorage instance.