Trouble with Google Apps API and Service Accounts

2019-02-19 04:22发布

问题:

I'm having some trouble getting the sample code for instantiating a Drive Service Account working. I've set up the service account in the API console as directed and included the scope for the 'https://www.googleapis.com/auth/drive', but running this generates the following error: "Authorization failed. Server message: (Signet::AuthorizationError)".

Oddly, if I omit the user_email address it doesn't generate an error.

My objective is to be able to do an audit on all the files stored on the organization's Drive, and it's my understanding that using a service account would be the way to get a listing of all the files stored.

Have I missed some special setting on the server side for this?

require 'google/api_client'

## Email of the Service Account #
SERVICE_ACCOUNT_EMAIL = '<service account email>@developer.gserviceaccount.com'

## Path to the Service Account's Private Key file #
SERVICE_ACCOUNT_PKCS12_FILE_PATH = '<private key file>-privatekey.p12'

def build_client(user_email)
    key = Google::APIClient::PKCS12.load_key(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'notasecret')
    asserter = Google::APIClient::JWTAsserter.new(SERVICE_ACCOUNT_EMAIL, 'https://www.googleapis.com/auth/drive', key)
    client = Google::APIClient.new

    client.authorization = asserter.authorize(user_email)
    return client
end

client = build_client("<users email address>")

回答1:

This looks to me like you are using an older example. I think that's how you used to do it about a year ago. Back in late 2012 that method of setting up the app was deprecated because Signet was updated to handle all aspects of the OAuth2 setup.

Here is the code I generally use to create a service account. You can tweak it to fit into your method.

client.authorization = Signet::OAuth2::Client.new(
 :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
 :audience => 'https://accounts.google.com/o/oauth2/token',
 :scope => "https://www.googleapis.com/auth/drive",
 :issuer => "<service account email>@developer.gserviceaccount.com",
 :signing_key => Google::APIClient::KeyUtils.load_from_pkcs12("<private key file>-privatekey.p12", "notasecret"),
 :person => "<users email address>")
client.authorization.fetch_access_token!

If you are still having issues let me know and I'll see if I can help.



回答2:

I have worked with service account+Drive+file permissions using Java. In order to use permissions for a particular user, I had to allow certain scope. The only thing I can guess about your issue is that you might have missed the Delegation part



回答3:

Using version 0.9.13 of google-api-client, I succeeded in using the following slight adaptation of Woodward's answer (note the absence of the person parameter):

def service_account_authorization(credentials_file, scope)
  credentials = JSON.parse(File.open(credentials_file, 'rb').read)
  authorization = Signet::OAuth2::Client.new(
    :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
    :audience => 'https://accounts.google.com/o/oauth2/token',
    :scope => scope,
    :issuer => credentials['client_id'],
    :signing_key => OpenSSL::PKey::RSA.new(credentials['private_key'], nil),
  )
  authorization.fetch_access_token!
  authorization
end

This snippet takes a file as it was downloaded from Google Cloud Console for a service account and returns an auth object that can be fed to Google::Apis::*Service.authorization.

Thanks James!