0

I have been attempting to work on a request from my boss this week that requires using the google admin directory api.

At this point I am questioning if what I am trying to do is even possible.

  • Can I retrieve data from the scope "https://www.googleapis.com/auth/admin.directory.device.mobile.readonly" with a service account? Is it even possible?

The errors I have seen in the past hour are below... Many of them sound the same and I have no idea what is going on or why this is such a difficult journey for such basic information.

  1. PERMISSION_DENIED: Request had insufficient authentication scopes. (Google::Apis::ClientError)

  2. `check_status': Unauthorized (Google::Apis::AuthorizationError)

  3. Authorization failed. Server message: (Signet::AuthorizationError) { "error": "unauthorized_client", "error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested." }

  4. `check_status': permission_denied: request had insufficient authentication scopes

  5. `check_status': badRequest: Bad Request

My current test script is below...

require "google/apis/admin_directory_v1"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"

APPLICATION_NAME = "Directory API Ruby Quickstart".freeze
CREDENTIALS_PATH = "credentials.json".freeze
CUSTOMER_ID = "thasgunnabeanopefrommedawg".freeze

SCOPE = ["https://www.googleapis.com/auth/admin.directory.device.mobile.readonly"].freeze

authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: 
File.open('credentials.json'),
  scope: SCOPE)
authorizer.update!(sub: "fullbl00m@citadelny.com")
authorizer.fetch_access_token!

# puts authorize

# Initialize the API
service = Google::Apis::AdminDirectoryV1::DirectoryService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = Google::Auth.get_application_default(SCOPE)

response = service.list_mobile_devices(customer_id: CUSTOMER_ID)
puts response.to_json

EDITS BELOW *** [27th, MAY, 2022]

I have been trying with ruby, python, and postman for two weeks at this point :/

Last night I took the ruby snippet that was posted by user:Daimto below.

I was able to return a token with the following modified version of the ruby snippet provided in the answer below.

require 'googleauth'
require 'google/apis/admin_directory_v1'

creds = {
  "type": "service_account",
  "project_id": "MYPROJECTNAME",
  "private_key_id": "MYPRIVATEKEYID",
  "private_key": "-----BEGIN PRIVATE KEY-----\n-MY PRIVATE KEY 
WILL BE HERE BUT REMOVED FOR SECURITY-----END PRIVATE KEY-----\n",
"client_email": "emailfromserviceaccount-compute@developer.gserviceaccount.com",
  "client_id": "MYCLIENTIDISACTUALLYHERE",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/compute%40developer.gserviceaccount.com"
}

creds_json = creds.to_json
creds_json_io = StringIO.new(creds_json)
auth = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: creds_json_io, 
scope["https://www.googleapis.com/auth/admin.directory.device.mobile.readonly","https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly","https://www.googleapis.com/auth/admin.directory.device.mobile"]
)
    auth.sub = "emailfrommyserviceaccount- 
   compute@developer.gserviceaccount.com" 

puts auth.fetch_access_token

Please excuse the formatting.

I took the service account out of the env variable for now to make sure I can get it to work without adding extra layers of abstraction at this time.

When trying to add the additional code from the Directory Api Quickstart to the above snip I STILL RETURN THE ERROR

/var/lib/gems/2.7.0/gems/google-apis-core-0.5.0/lib/google/apis/core/http_command.rb:224:in `check_status': Unauthorized (Google::Apis::AuthorizationError)

The additional code added is below... The last line of the previous snip gets changed to the first line of the snip that comes after this. This is to properly pass the token to the example after modifying user:Daimto's response.

authorize = auth.fetch_access_token

# Initialize the API
service = Google::Apis::AdminDirectoryV1::DirectoryService.new
service.client_options.application_name = "my-application-name"
service.authorization = authorize
# List the first 10 users in the domain
response = service.list_users(customer:    "my_customer",
                              max_results: 10,
                              order_by:    "email")
puts "Users:"
puts "No users found" if response.users.empty?
response.users.each { |user| puts "- #{user.primary_email} (#{user.name.full_name})" }
fullbl00m
  • 41
  • 5

1 Answers1

1

The method Method: mobiledevices.list requires one of the following scopes.

enter image description here

So to answer your first question yes you can use the https://www.googleapis.com/auth/admin.directory.device.mobile.readonly scope.

Error number 1

PERMISSION_DENIED: Request had insufficient authentication scopes.

You were probably getting this error when you had supplied a different scope.

Error 3;

 Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.

There are three types of clients you can create on google cloud console.

  • web client
  • native client
  • service account

The json file you get from creating these clients is all different. The code that uses them is also different. The error is telling you that you have a client.json file that you are using which does not match the type of code you are using.

How to create service account credetinals

The code for a service account would be like this Not tested you may need to fix the scope. Remember that the service account needs to be configured properly on your workspace domain for the sub to work.

require 'googleauth'
require 'google/apis/admin_v1'

creds = ENV['GOOGLE_SERVICE_ACCOUNT'] # JSON downloaded from cloud console
                                      # is saved in this ENV variable
creds_json = JSON.parse(creds)
creds_json_io = StringIO.new(creds_json.to_json)
auth = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: creds_json_io,
  scope: [Google::Apis::ADMINV1::ADMIN_DIRECTORY_MOBILE_READONLY]
)
auth.sub = 'admin@yourdomain.com' 
auth.fetch_access_token

Tip: You have a lot of errors there, I feel that you have been struggling with this for a while. Advice step back, have a look at the sample on the readme for the Google-api-ruby-client. Start over. Just get your auth to work. Once you get the code right and the client right all the pieces will fit into place.

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • I have struggled with it for a long time. I have read the entire readme many times. I have made a collection of tons of other people having the same problems and have found tons of times where the information has completely changed over time as the API has evolved. All I was even trying to do was get the Auth to work lol – fullbl00m May 25 '22 at 01:46
  • I completely said I have had enough with Ruby and moved to Python. It is the same experience there and the docs look like they were all generated instead of actually written and that explains why its such a trainwreck to make sense of. My newest troubles are at this link >> https://stackoverflow.com/questions/72371138/python-google-api-quickstart-examples-are-poorly-documented – fullbl00m May 25 '22 at 01:50
  • Did none of what i told you above help? – Linda Lawton - DaImTo May 25 '22 at 07:24
  • It has yet to help me yes. I have been trying to return data with postman using the following tutorial because the python and ruby things were so convoluted I gave up hope of being able to wade through it alone. https://medium.com/kinandcartacreated/google-authentication-with-postman-12943b63e76a I have ONLY been able to generate an auth token using this method and a OAuth2.0 Credential that is of type 'Web Application'. In all attempts I have found no clear documentation about configuring a service account to work in this process. – fullbl00m May 27 '22 at 02:18
  • Everything you find states it similarly to yourself, 'Remember that the service account needs to be configured properly on your workspace domain for the sub to work.' BUT NOTHING MAKES IT CLEAR OF HOW TO DO THIS LOL – fullbl00m May 27 '22 at 02:20
  • I am testing the ruby now cus I am at a total loss still, I will let you know how it goes. I keep getting a link generated by my python scripts that looks like it will work, but then it just tries to forward to localhost and doesnt work? The other issues I still am hitting are error 400 URI mismatch and "Client secrets must be for a web or installed app." – fullbl00m May 27 '22 at 05:24
  • Dont try to get service account working with postman its not designed for that authorization type. Follow [domain wide deligation](https://developers.google.com/admin-sdk/directory/v1/guides/delegation) to understand how to delignate to a service account just change the scopes. Your code must be the code for a service account the credentials you downloaded must also be a service account. – Linda Lawton - DaImTo May 27 '22 at 16:09
  • The only useful walkthrough that I have found that has actually returned a real access token is the one that uses postman. The only reason I attempted using a service account with it is because his tutorial was the closest thing to a working example I can find, and it does not work with a service account anyway lol – fullbl00m May 27 '22 at 22:52
  • The code above shows that the credentials I have downloaded from my service account have already been used in the example I posted on [27th, MAY, 2022] – fullbl00m May 27 '22 at 22:53
  • I have been trying to understand domain wide delegation for 3 days lol I watched your 17 minute video on Understanding google Oauth on youtube. – fullbl00m May 27 '22 at 22:53
  • I have a video in the works on how to set up delegation. I just need to finish editing it. – Linda Lawton - DaImTo May 29 '22 at 17:55
  • I give up, I can not take Google API's seriously for one more minute. I am not going to build on something so chaotic with generated and maliciously deceptive documentation. I am just using desktop automation and a database of my own and scraping it off because modern problems require modern solutions and I can't take it anymore. GOOD RIDDANCE LOL – fullbl00m Jun 01 '22 at 15:41
  • If you change your mind check my profile there are ways to contact me out side of stack. – Linda Lawton - DaImTo Jun 01 '22 at 20:46