2

I have a Json service file, and a service account that already accesses translate and sheets, but it will not access user lists. The result is either 400 showing its confused or 401 saying its not authorized. Examples are usually about client involved OAuth processes, where I need server to server. I have enabled that "Enable G Suite domain-wide delegation" feature on the service account too.

I read and tried the JWT method, but I get the same error responses. https://developers.google.com/identity/protocols/oauth2/service-account#python_2

My goal is to call either one of these end points https://www.googleapis.com/admin/directory/v1/users https://www.googleapis.com/admin/directory/v1/users.readonly

Any direction would be greatly appreciated.

UPDATE1: This is using the Jwt token approach which yields error 401.

with open(CLIENT_SECRET_FILE, "r+") as fh:
    config = json.load(fh)

iat = time.time()
exp = iat + 3600
payload = {'iss': config['client_email'],
           'sub': config['client_email'],
           'aud': 'https://www.googleapis.com/',
           'iat': iat,
           'exp': exp}
additional_headers = {'kid': config['private_key_id']}
signed_jwt = jwt.encode(payload, config['private_key'], headers=additional_headers,
                        algorithm='RS256')

url = 'https://www.googleapis.com/admin/directory/v1/users'        
headers = {"Authorization": "Bearer " + signed_jwt}
r = requests.get(url, headers=headers)

I have also tried

scopes = ['https://www.googleapis.com/auth/admin.directory.user']
credentials = ServiceAccountCredentials.from_json_keyfile_name(CLIENT_SECRET_FILE, scopes=scopes)
service = build('admin', 'directory_v1', credentials=credentials)
results = service.users().list().execute()

UPDATE2: This link has great information and simple code to review. As much as I tried to avoid impersonation, the AdminSDK requires it. That makes integrations a bit awkward in my view. In addition, the issue I also faced was the Domain-Wide-Delegation screen in the Google Workspace Admin can get messed up. Deleting the entry and recreating it fixed the forever 403 error I kept getting no matter what I had tried. https://gist.github.com/lewisrodgers/fa766ebd37c1397d3a014eb73805edd1

darren
  • 579
  • 8
  • 23
  • You need to show your code and how the service account is configured. The setup and use is fairly complicated (simple once you understand the steps). Most likely you have left out the impersonation step when creating the access token. I have posted other answers on this type of question, search my profile. – John Hanley Jan 27 '21 at 19:51
  • So there is no way to grant a service account rights to act as a service? If the admin leaves, then all the services stop working until someone reconfigures the background jobs? This seems odd to me. I can manage sheets and gdrive as a service without impersonation. – darren Jan 28 '21 at 23:08
  • What do you mean by "act as a service" and how is that related to your question? Your comment "if the admin leaves" - the "company" should own/control all accounts that admins use. If they are using personal accounts, setup your identity and access management under company control. – John Hanley Jan 29 '21 at 00:26
  • I am trying to setup a user sync between Google and another product. It should be independent of a user because it is a system calling a system. My focus on service account was for that purpose. All I really need is readonly access to the users for their name, email, and profile ID. – darren Jan 29 '21 at 16:31
  • Reread my first comment and update your question. – John Hanley Jan 29 '21 at 16:34
  • Thank you John. I have posted code from 2 approaches I have tried – darren Jan 29 '21 at 22:03
  • 1) In Google Cloud `CLIENT_SECRET_FILE` has a specific meaning (OAuth 2 client three-legged authorization). Your code would fail to run if this was wrong however, but it indicates you do not yet understand how authorization works and the types of authentication. 2) Your code is not impersonating a user. It is creating an OAuth Access Token for the service account's identity. I recommend that you use Google's SDK libraries. The link in @ziganotschka has an example in Python that you can use. – John Hanley Jan 29 '21 at 22:33
  • Yeah I get the request token and the access token exchanges, and I have implemented impersonation systems before. Thanks for the help John, I'll figure it out. – darren Jan 30 '21 at 21:13
  • @JohnHanley - as much as I wanted to avoid impersonation, you were correct in that it is required. I also had hit another issue in the Domain-Wide Delegation setup that sometimes gets messed up and needs to be deleted and rebuilt. A few have reported that issue, which was also mine. – darren Feb 26 '21 at 22:21
  • I am glad that you figured this out. Post an answer on your solution to help others. Delegation seems to be a sticking point in some designs. – John Hanley Feb 26 '21 at 23:13

2 Answers2

3

You need to incorporate into your code impersonation, so that the service account acts on behalf of the admin

Because only admins have authroization to access Resource: users.

For impersonation in Python you need to implement the additional line

delegated_credentials = credentials.with_subject('admin@example.org')
ziganotschka
  • 25,866
  • 2
  • 16
  • 33
0

The link below has great information and simple code to review. As much as I tried to avoid impersonation, the AdminSDK requires it. That makes integrations a bit awkward in my view.

In addition, the issue I also faced was the Domain-Wide-Delegation screen in the Google Workspace Admin that messed up. After much digging over weeks, I found a simple solution of deleting and recreating the client entry in that screen. It fixed the never ending 403 error that hit every test I tried that should have worked and did for many others.

This seems to be the only API set by Google that requires impersonation, and is annoying when attempting to create a SaaS solution.

Really basic, trimmed examples, and decent article references. https://gist.github.com/lewisrodgers/fa766ebd37c1397d3a014eb73805edd1

darren
  • 579
  • 8
  • 23