Search code examples
ruby-on-railsrubygoogle-apigoogle-oauthgoogle-classroom

Google::Apis::ClientError: forbidden: The caller does not have permission when using service account call with ruby google api


I'm trying use a service account with google's api to work with classroom data with the goal of synchronizing our web service for schools with the google classroom data.

I have delegated domain wide authority to the service account and have activated the Google Classroom API. I have downloaded the json Key file used below. I have added https://www.googleapis.com/auth/classroom.courses to the scope of the service account.

My test code in app/models/g_service.rb:

class GService  
  require 'google/apis/classroom_v1'

  def get_course
    authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open('/Users/jose/Downloads/skt1-301603-4a655caa8963.json'),
      scope: [ Google::Apis::ClassroomV1::AUTH_CLASSROOM_COURSES ]
    )
    authorizer.fetch_access_token!
    service = Google::Apis::ClassroomV1::ClassroomService.new
    service.authorization = authorizer

    puts "\n service\n #{service.inspect}"
    response = service.get_course( '99999' )
    puts "\n response \n#{response.inspect}"
  end
end

The results in the console are:

>> GService.new.get_course

 service
 #<Google::Apis::ClassroomV1::ClassroomService:0x007fe1cff98338 @root_url="https://classroom.googleapis.com/", @base_path="", @upload_path="upload/", @batch_path="batch", @client_options=#<struct Google::Apis::ClientOptions application_name="unknown", application_version="0.0.0", proxy_url=nil, open_timeout_sec=nil, read_timeout_sec=nil, send_timeout_sec=nil, log_http_requests=false, transparent_gzip_decompression=true>, @request_options=#<struct Google::Apis::RequestOptions authorization=#<Google::Auth::ServiceAccountCredentials:0x0xxxxxxx @project_id="sssssssss", @authorization_uri=nil, @token_credential_uri=#<Addressable::URI:0x000000000 URI:https://www.googleapis.com/oauth2/v4/token>, @client_id=nil, @client_secret=nil, @code=nil, @expires_at=2021-01-13 20:56:46 -0800, @issued_at=2021-01-13 19:56:47 -0800, @issuer="xxxxxxx.iam.gserviceaccount.com", @password=nil, @principal=nil, @redirect_uri=nil, @scope=["https://www.googleapis.com/auth/classroom.courses"], @state=nil, @username=nil, @access_type=:offline, @expiry=60, @audience="https://www.googleapis.com/oauth2/v4/token", @signing_key=#<OpenSSL::PKey::RSA:0xxxxxxxxx>, @extension_parameters={}, @additional_parameters={}, @connection_info=nil, @grant_type=nil, @refresh_token=nil, @access_token="-------------------------------------------------------------------------------------->, retries=0, header=nil, normalize_unicode=false, skip_serialization=false, skip_deserialization=false, api_format_version=nil, use_opencensus=true>>
Google::Apis::ClientError: forbidden: The caller does not have permission

It appears everything is working fine until the service.get_course('99999') call. I've tested this call using the https://developers.google.com/classroom/reference/rest/v1/courses/get online tool and it works fine.

I've poured over the documentation but have been unable to resolve this. Can anybody please let me know what I am missing?

I'm running rails 3.2 and ruby 2.1


Solution

  • Considering the error you're getting, I think you are not impersonating any account.

    The purpose of domain-wide delegation is that the service account can impersonate a regular account in your domain, but in order to do that, you have to specify which account you want to impersonate. Otherwise, you are calling the service account by itself, and it doesn't matter that you've enabled domain-wide delegation for it.

    In the Ruby library, you can specify that using the :sub parameter, as shown in the section Preparing to make an authorized API call at the library docs:

    authorizer.sub = "<email-address-to-impersonate>"
    

    Note:

    Make sure the account you impersonate has access to this course, otherwise you'll get the same error.

    Related: