Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directory API: Not Authorized to access this resource/api #1884

Open
nicholasc opened this issue Nov 18, 2019 · 51 comments
Open

Directory API: Not Authorized to access this resource/api #1884

nicholasc opened this issue Nov 18, 2019 · 51 comments
Labels
api: admin external This issue is blocked on a bug with the actual product. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@nicholasc
Copy link

I created a project in the Google Cloud Platform and went ahead and activated the Admin SDK & Gmail APIs. I then created a domain-wide service account in the project, downloaded the JSON key file and gave it authorization to the following scopes in the Admin Console:

https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/admin.directory.group
https://www.googleapis.com/auth/admin.directory.group.member
https://www.googleapis.com/auth/gmail.settings.basic

Using the JSON file, I can create a JWT auth object and access the Gmail API just fine. However, I keep getting 403: Not Authorized to access this resource/api when attempting to use any resource on the Directory API.

In the API & Services Dashboard on the Google Cloud Platform, I can see the requests coming in and being denied but I can't think of any reason why this would not work properly.

Here is the code I am using (of course using a different domain than mydomain.com):

const useDirectory = async keyFile => {
  const auth = new google.auth.GoogleAuth({
    keyFile,
    scopes: [
      'https://www.googleapis.com/auth/admin.directory.user',
      'https://www.googleapis.com/auth/admin.directory.group'
      'https://www.googleapis.com/auth/admin.directory.group.member'
    ]
  });

  return google.admin({
    version: "directory_v1",
    auth: await auth.getClient()
  });
};

const token = path.resolve("./token.json");
if (!fs.existsSync(token)) {
  throw new Error("Could not find token.json for authentication.");
}

const directory = await useDirectory(token);
const users = await directory.groups
  .list({ domain: "mydomain.com" })
  .catch(console.error);

Environment details

  • OS: macOS Mojave 10.14.6
  • Node.js version: 10.16.3
  • npm version: 6.11.3
  • google-auth-library version: 5.2.0

Steps to reproduce

Too many steps based on the description of the issue.

@bcoe bcoe added the type: question Request for information or clarification. Not an issue. label Nov 18, 2019
@bcoe
Copy link
Contributor

bcoe commented Nov 18, 2019

@nicholasc I believe the scope for list might be:

https://www.googleapis.com/auth/admin.directory.user.security

can you give this a shot?

@nicholasc
Copy link
Author

nicholasc commented Nov 18, 2019

It did not work.

The url used by the library is

https://www.googleapis.com/admin/directory/v1/groups

Based on the API Reference the scope for that url is

https://www.googleapis.com/auth/admin.directory.group

@devatclearoutio
Copy link

I too face the same problem when trying to manage the G Suite users using service account credential, but hitting the same problem

Screenshot 2020-02-24 at 6 41 57 PM

@bcoe bcoe added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. and removed type: question Request for information or clarification. Not an issue. labels Feb 26, 2020
@bcoe
Copy link
Contributor

bcoe commented Feb 26, 2020

@nicholasc I work on the GCP client libraries team, which is a few steps away from the Gmail API. I've added the bug label, and we will make an effort to dig into this when possible, but:

It might also be worth following the directions here to get help with the gmail API, just in case someone on stack overflow knows an immediate solution to your problem.

@nicholasc
Copy link
Author

nicholasc commented Feb 26, 2020

Hello @bcoe .

I appreciate that this will be looked into as I am still facing this issue. I've temporarily resorted to a manual list of our users but I don't see this as a viable solution long term.

Thank you for referring me to the Gmail API help section. However, this is not related to the Gmail API, which I have been able to use with a service account, but with the Directory API within the Admin SDK. I went here and looked for similar issue on stack overflow but haven't been able to find much of anything.

Again, thank you.

@nicholasc
Copy link
Author

nicholasc commented Feb 26, 2020

Found relevant issue on the Admin SDK Bug issue tracker:
https://issuetracker.google.com/issues/113755665

No real solution at the moment.

@somejavadev
Copy link

@nicholasc, thanks for pointing out the issue tracker. The solution there was to use a legit admin user's account which is not a viable solution. This is also occurring in the java API at the moment. @bcoe Is there any other possible way to determine which groups a user belong to using service account authentication?

@allaway1
Copy link

Seeing the same issue. Have Domain Wide Delegation enabled and added the scopes for the client in Admin.

@tchimih
Copy link

tchimih commented Mar 11, 2020

I have the same issue, I gave my service accounts all the authorization in GSuite Admin and gave him the right scopes with domain wide delegation and I'm facing the same error. FYI, I use a server to server oauth scheme (JWT).

@SqiSch
Copy link

SqiSch commented Mar 13, 2020

@nicholasc I believe the scope for list might be:

https://www.googleapis.com/auth/admin.directory.user.security

can you give this a shot?

Thanks a lot. This scope fixed my problem.

@bcoe
Copy link
Contributor

bcoe commented Mar 17, 2020

@SqiSch could you share a brief snippet of code, demonstrating the approach you're taking. I wonder why this scope did not solve @nicholasc's issue for them.

@allaway1
Copy link

Didn't solve mine either. I've given up and am using something else. Requiring human interaction via OAuth to access google groups (or not publishing what is needed) is silly. Last thing I need to do is grant full admin rights to Mechanical Turk and pay someone to be an API.

@ghost
Copy link

ghost commented Mar 19, 2020

The same issue. Didn't solve mine either.
I'm trying to create new user with service account credentials.

const { google } = require('googleapis');

const key = require('./sa.json');
const ADMIN_API = google.admin('directory_v1');
const SCOPES = [
    'https://www.googleapis.com/auth/admin.directory.user',
    'https://www.googleapis.com/auth/admin.directory.user.security',
];

const auth = new google.auth.JWT(
    key.client_email,
    null,
    key.private_key,
    SCOPES,
);

ADMIN_API.users
    .insert({
        auth,
        resource: {
            name: {
                familyName: 'John',
                givenName: 'Doe',
            },
            password: '1qaZXsw2',
            changePasswordAtNextLogin: true,
            primaryEmail: 'testuser@example.com',
        },
    })
    .then((data) => {
        console.log(data);
    })
    .catch((error) => {
        console.log(error);
    });

Versions:

  • "googleapis": "48.0.0"

@devinmatte
Copy link

devinmatte commented Mar 20, 2020

I'm running into the same issue
GET https://www.googleapis.com/admin/directory/v1/groups?userKey=email@company.com gets me

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "forbidden",
    "message": "Not Authorized to access this resource/api"
   }
  ],
  "code": 403,
  "message": "Not Authorized to access this resource/api"
 }
}

When using the scopes of:

https://www.googleapis.com/auth/admin.directory.group.readonly

AND with

https://www.googleapis.com/auth/admin.directory.group.member.readonly 
https://www.googleapis.com/auth/admin.directory.group.readonly 
https://www.googleapis.com/auth/admin.directory.user.readonly 
https://www.googleapis.com/auth/admin.directory.user.security 

It's also failing from https://developers.google.com/admin-sdk/directory/v1/reference/groups/list in the API trial sidebar

@justinmchase
Copy link

justinmchase commented Mar 26, 2020

The issue appears to be that the user is not an admin, and thus cannot access the admin sdk:
https://stackoverflow.com/a/26469289/12958

On second thought, this does not make sense to me. If I was able to create an access token with the given scopes this should be all I need to look at my groups at least.

@gustavopergola
Copy link

gustavopergola commented Apr 16, 2020

I had the same problem.
As mentioned on google issue tracker, I impersonated an admin account with a Domain-wide Delegation
service account.

EDIT: Forgot to mention, it solved the problem

@sanderisbestok
Copy link

Is there already a solution for this? I am facing the same problem with indeed a Service account with a Domain-wide Delegation

@LakshminarayanaTh-Kore
Copy link

Is there already a solution for this? I am facing the same problem with indeed a Service account with a Domain-wide Delegation

Yes @sanderisbestok you may refer this https://stackoverflow.com/questions/56636523/instantiate-an-admin-sdk-directory-service-object-with-nodejs. Thing here is, need also to pass 'subject'.

@tchimih
Copy link

tchimih commented May 7, 2020

Hey guys,

I solved my issue by bypassing the google api nodejs client, using my following code:

const jwt       = require('jsonwebtoken');
const keys      = require('./jwt.keys.json');
const fetch     = require("node-fetch");

const createJwt = (projectId, algorithm) => {
    // Create a JWT to authenticate this device. The device will be disconnected
    // after the token expires, and will have to reconnect with a new token. The
    // audience field should always be set to the GCP project id.
    const token = {
      iss: keys.client_email,
      scope: "your scopes",
      iat: parseInt(Date.now() / 1000),
      exp: parseInt(Date.now() / 1000) + 60 * 60, // 20 minutes
      aud:"https://oauth2.googleapis.com/token",
      sub: "account for DWD" // here you need to put the account used for domain delegation
    };
    const privateKey = keys.private_key; // load the private key of your SA 
    return jwt.sign(token, privateKey, {algorithm: algorithm});
  };

function getOauthBearer(token, callback){
     // this method will return the oauth bearer so you can use it when calling Admin API
    let options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token
    }
    
    const BASE_URL = 'https://oauth2.googleapis.com/token'
    fetch(BASE_URL, options)
    .then(response => response.json())
    .then(json=> {
      callback(json.access_token);
    })    
    .catch(err => console.error(err))
}

export function yourFunction(param1, ... , callback) {
  let token = createJwt(keys.project_id, 'RS256');
  getOauthBearer(token, body => {
    const url   = "your Google directory API URL"
    const headers = {
        method: 'GET',
        headers:{
            'Authorization': 'Bearer ' + body
        }
    };

    fetch(url, headers)
    .then(response => response.json())
    .then(json=> {
      callback(json);
    }) 
    .catch((error) => console.error(error))
  });
}

There you go, tell me if it worked for you !

@justinmchase
Copy link

@tchimih How is that bypassing the directory api? It seems like its still calling the same api? I think what we're seeing is that it depends on what "account for DWD" you are using. You need to make the call with the correct account to be able to get results.

@tchimih
Copy link

tchimih commented May 7, 2020

@justinmchase sorry for my mistake. I meant by bypassing the google nodejs client. I think you need to specify when the service account is created the dwd account used. That's what I did and it worked for me.

I used to have the same error.

@bcoe bcoe added the external This issue is blocked on a bug with the actual product. label May 14, 2020
@mathewtrivett
Copy link

I'm having a similar issue. Could someone clarify. Does the account that creates the Service account need to be an Admin on the Domain?

For example:

  • I am a developer my company but do not have admin access.
  • I have created a service account and downloaded the keys etc.
  • Instantiated a node.js auth object like this:
const auth = new google.auth.GoogleAuth({
  keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,
  scopes: ['https://www.googleapis.com/auth/admin.directory.user'],
});
  • I provided the service account email to a domain admin to add to the service accounts in Security > APIs > Service accounts with the scope of https://www.googleapis.com/auth/admin.directory.user

However when I then try to call the API via the node client I get the 403 error.

Does my service account need to be created by a Google Admin?

@LakshminarayanaTh-Kore
Copy link

I'm having a similar issue. Could someone clarify. Does the account that creates the Service account need to be an Admin on the Domain?

For example:

  • I am a developer my company but do not have admin access.
  • I have created a service account and downloaded the keys etc.
  • Instantiated a node.js auth object like this:
const auth = new google.auth.GoogleAuth({
  keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,
  scopes: ['https://www.googleapis.com/auth/admin.directory.user'],
});
  • I provided the service account email to a domain admin to add to the service accounts in Security > APIs > Service accounts with the scope of https://www.googleapis.com/auth/admin.directory.user

However when I then try to call the API via the node client I get the 403 error.

Does my service account need to be created by a Google Admin?

Try this... #1884 (comment)

@arcrose
Copy link

arcrose commented Jun 24, 2020

I am also experiencing this problem using the Python client libraries. I am using the following code to try to suspend an account but am getting the same error that everyone here is reporting.

from google.oauth2 import service_account
from googleapiclient import discovery

SCOPES = [
    'https://www.googleapis.com/auth/admin.directory.user',
    'https://www.googleapis.com/auth/admin.directory.user.security',
]


service_account_json_key = {
  "type": "service_account",
  "project_id": "[REDACTED]",
  "private_key_id": "[REDACTED]",
  "private_key": "[REDACTED]",
  "client_email": "[REDACTED]",
  "client_id": "[REDACTED]",
  "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": "[REDACTED]"
}

credentials = service_account.Credentials.from_service_account_info(
    service_account_json_key, 
    scopes=SCOPES,
)

service = discovery.build('admin', 'directory_v1', credentials=credentials)

resp = service.users().patch(
    userKey='user@company.com',
    body={
        'suspended': True,
    },
).execute()

print(dir(resp))

As you can see, I have tried adding the scope described earlier. I have also enabled domain-wide delegation.

@gustavopergola
Copy link

@arcrose

Try:

...
credentials = service_account.Credentials.from_service_account_info(
    service_account_json_key, 
    scopes=SCOPES,
)
credentials = credentials.with_subject('admin.user@company.com')
....

@arcrose
Copy link

arcrose commented Jun 24, 2020

Thank you for the suggestion @gustavopergola. I've tried adding a subject containing both my own GSuite user email address (which is not an admin) as well as that of an admin who has access to the service account I am using. In both cases I receive the following error:

google.auth.exceptions.RefreshError: ('unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.', '{\n "error": "unauthorized_client",\n "error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested."\n}')

I should also note that, after the two lines you listed above, printing both credentials.expired and credentials.valid print False.

I've also taken note of the fact that domain-wide delegation has been disabled and I am unable to re-enable it.

@mathewtrivett
Copy link

It turns out that my assumption was correct unless a maintainer could clarify otherwise. The only users able to access the Admin SDK must be an Admin / Superuser of the Domain or a registered Google Reseller.

As others have mentioned, you also need to provide the subject for JWT which must be the email of the admin who created the Service Account.

I was able to connect with the following example:

const auth = new google.auth.JWT({
  keyFile: KEYFILE_GENERATED_BY_ADMIN,
  scopes: ['https://www.googleapis.com/auth/admin.directory.user.readonly'],
  subject: ADMIN_EMAIL,
});

But not able to connect with the following example:

const auth = new google.auth.JWT({
  keyFile: KEYFILE_GENERATED_BY_THIRD_PARTY,
  scopes: ['https://www.googleapis.com/auth/admin.directory.user.readonly'],
  subject: THIRD_PARTY_EMAIL,
});

I don't know if others are trying to connect as effectively a third party developer and running into this issue. The docs could be a lot more explicit if this the expected behaviour?

Hope it helps others.

@arcrose
Copy link

arcrose commented Jun 24, 2020

@mathewtrivett's suggestion actually worked perfectly on my side.

@staadecker
Copy link

staadecker commented Jul 28, 2020

For those who want to use the GoogleAuth constructor instead of JWT to use Application default credentials, you can use:

  const auth = new google.auth.GoogleAuth({
    scopes: ["https://www.googleapis.com/auth/admin.directory.group"],
  });

  const authClient = await auth.getClient();
  authClient.subject = ADMIN_EMAIL;
  return google.admin({ version: "directory_v1", auth: authClient });

See issue #1699 .

⚠️ EDIT: This did not work on Google Cloud Function because GoogleAuth() uses Compute() when running in the cloud instead of JWT(). See the note here. I've had to revert to @mathewtrivett method from above which is too bad because application default credentials make everything easier. My only guess for why this is would be that Compute() doesn't handle the subject setting the same way as JWT(). It maybe be possible to still use application default credentials by forcing the type as show here however I haven't tried.

@staadecker
Copy link

It turns out that my assumption was correct unless a maintainer could clarify otherwise. The only users able to access the Admin SDK must be an Admin / Superuser of the Domain or a registered Google Reseller.

The note at the bottom of this section of the documentation seems to confirm this.

@jacquelineborg
Copy link

Hello. I am also having the same issue. I am trying to gather a list of users from gsuite to google sheets but when trying to gather the list of users, i get the following error:

GoogleJsonResponseException: API call to directory.groups.list failed with error: Not Authorized to access this resource/api (line 28, file "1_listAllGroupMembers")

I have checked with the admin of gsuite and he did add me as an admin but on admin.google.com I can see the users but cannot add new ones for example, could it be that I am not admin in reality?

Appreciate your help with this :)

@thongly
Copy link

thongly commented Jan 17, 2021

@arcrose

Try:

...
credentials = service_account.Credentials.from_service_account_info(
    service_account_json_key, 
    scopes=SCOPES,
)
credentials = credentials.with_subject('admin.user@company.com')
....

@gustavopergola - this did it for me.

@jeanomobono
Copy link

It turns out that my assumption was correct unless a maintainer could clarify otherwise. The only users able to access the Admin SDK must be an Admin / Superuser of the Domain or a registered Google Reseller.

The note at the bottom of this section of the documentation seems to confirm this.

It doesn't seem to work either even adding the non-admin user to a role with the Admin API access privileges

@darrinthomascecil
Copy link

I flailed at this for a couple days, and have a somewhat workable solution. I created a service account with domain-wide delegation and Organization Admin/Service Usage Admin/Role Admin/Owner roles. The roles where a complete shot in the dark because nothing seemed to work. With the .NET SDK and ApplicationDefaultCredentials I could set an IAM policy, and create a project, but not create a group. I'm not totally ready to give up, but I have settled so far on the following
gcloud auth activate-service-account --key-file=/path-to-above-sa.json
gcloud identity groups create thegroup@my.domain --organization my.domain --with-initial-owner=WITH_INITIAL_OWNER --display-name="display name" --description="blah" --labels="cloudidentity.googleapis.com/groups.discussion_forum"

This does work, confirmed in GCP console and in the Admin console. However, if I do something like
curl -H "Authorization: Bearer $(gcloud auth print-access-token)" https://the-url.... I get the same 403 that I get from the SDK. This is far from ideal, because I'll have to build up a file to run with strings on what groups I want to create, but it's the only way with .NET so far I've had any luck.

@canavci2016
Copy link

Hey guys,

I solved my issue by bypassing the google api nodejs client, using my following code:

const jwt       = require('jsonwebtoken');
const keys      = require('./jwt.keys.json');
const fetch     = require("node-fetch");

const createJwt = (projectId, algorithm) => {
    // Create a JWT to authenticate this device. The device will be disconnected
    // after the token expires, and will have to reconnect with a new token. The
    // audience field should always be set to the GCP project id.
    const token = {
      iss: keys.client_email,
      scope: "your scopes",
      iat: parseInt(Date.now() / 1000),
      exp: parseInt(Date.now() / 1000) + 60 * 60, // 20 minutes
      aud:"https://oauth2.googleapis.com/token",
      sub: "account for DWD" // here you need to put the account used for domain delegation
    };
    const privateKey = keys.private_key; // load the private key of your SA 
    return jwt.sign(token, privateKey, {algorithm: algorithm});
  };

function getOauthBearer(token, callback){
     // this method will return the oauth bearer so you can use it when calling Admin API
    let options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token
    }
    
    const BASE_URL = 'https://oauth2.googleapis.com/token'
    fetch(BASE_URL, options)
    .then(response => response.json())
    .then(json=> {
      callback(json.access_token);
    })    
    .catch(err => console.error(err))
}

export function yourFunction(param1, ... , callback) {
  let token = createJwt(keys.project_id, 'RS256');
  getOauthBearer(token, body => {
    const url   = "your Google directory API URL"
    const headers = {
        method: 'GET',
        headers:{
            'Authorization': 'Bearer ' + body
        }
    };

    fetch(url, headers)
    .then(response => response.json())
    .then(json=> {
      callback(json);
    }) 
    .catch((error) => console.error(error))
  });
}

There you go, tell me if it worked for you !

this is worked for me. whenever i get this error. i add the "sub" field indicating the email address of admin in gsuite

@elishaterada
Copy link

elishaterada commented Apr 15, 2021

I also struggled to get past the 403 errors using googleapis library no matter what I also wanted to avoid using the user-tied OAuth approach, and instead opted in to work with a service account.

TLDR: It worked when I switched to google-auth-library, which is a dependency included in the googleapis. My naive assumption is that the added abstraction layer was the issue.

Here are steps I've taken to make it work:

  1. Create a service account in your GCP project. Give it the "domain-wide delegation" privilege, and add or ask your admin to add the account in the G Suite Admin page with the same set of scope you need the service to have access to, for example:
"https://www.googleapis.com/auth/admin.directory.user.readonly"

For more, follow the Perform Google Workspace Domain-Wide Delegation of Authority

  1. Generate and download the service account JSON that contains the private key you need to load up in the app.

  2. Load the file up and use google-auth-library to construct your function call:

const fsPromise = require("fs").promises;
const { JWT } = require("google-auth-library");

async function createClient() {
  const data = await fsPromise.readFile(PATH_TO_SERVICE_ACCOUT_JSON);
  const keys = JSON.parse(data);

  return new JWT({
    email: keys.client_email,
    key: keys.private_key,
    subject: "your_gsuite_admin@yourdomain.com",
    scopes: ["https://www.googleapis.com/auth/admin.directory.user.readonly"],
  });
}


async function listUsers() {
  const client = await createClient();

  try {
    const res = await client.request({
      url: "https://www.googleapis.com/admin/directory/v1/users",
      params: {
        domain: "yourdomain.com",
      },
    });

    console.log(res.data);
  } catch (err) {
    console.log(err);
  }
}

@JustinBeckwith
Copy link
Contributor

@elishaterada it really is the same dependency, with 0 layers of abstraction in between.

@AlonGluz
Copy link

What did the trick for me is that I had to impersonate a different user:
https://developers.google.com/admin-sdk/directory/v1/guides/delegation#go

@egyptianbman
Copy link

I ran into this issue as well. In my case, I was trying to use this api in a script, when a sheet is updated. What I ended up having to do to resolve the issue is make sure the trigger is one that's created by an admin. In my case, I had created the trigger some time ago and downgraded the account I used at the time. The way I resolved is by deleting the trigger I created previously and creating a new trigger. This prompted me to select an account to authenticate the trigger. Selecting an account with current admin privileges solved my issue.

@mgrsskls
Copy link

It turns out that my assumption was correct unless a maintainer could clarify otherwise. The only users able to access the Admin SDK must be an Admin / Superuser of the Domain or a registered Google Reseller.

As others have mentioned, you also need to provide the subject for JWT which must be the email of the admin who created the Service Account.

I was able to connect with the following example:

const auth = new google.auth.JWT({
  keyFile: KEYFILE_GENERATED_BY_ADMIN,
  scopes: ['https://www.googleapis.com/auth/admin.directory.user.readonly'],
  subject: ADMIN_EMAIL,
});

But not able to connect with the following example:

const auth = new google.auth.JWT({
  keyFile: KEYFILE_GENERATED_BY_THIRD_PARTY,
  scopes: ['https://www.googleapis.com/auth/admin.directory.user.readonly'],
  subject: THIRD_PARTY_EMAIL,
});

For me it worked like this:

const auth = new google.auth.JWT({
   keyFile: KEYFILE_GENERATED_BY_THIRD_PARTY,
   scopes: ['https://www.googleapis.com/auth/admin.directory.user.readonly'],
   subject: ADMIN_EMAIL,
});

I am not an admin, but had created the key file. Afterwards the admin activated Domain Wide Delegation and I used that admin email address for subject. Worked!

@fazith27
Copy link

@arcrose

Try:

...
credentials = service_account.Credentials.from_service_account_info(
    service_account_json_key, 
    scopes=SCOPES,
)
credentials = credentials.with_subject('admin.user@company.com')
....

It worked for me. But what is really happening here? Can you please explain? Is this impersonating an admin account?

@chickenzz
Copy link

What worked for me was including the clientOptions object where I specified the subject for domain wide delegation.

const auth = new google.auth.GoogleAuth({
        scopes : [
            'https://www.googleapis.com/auth/admin.directory.user.readonly'
            ],
        clientOptions : {
            subject : 'adminuser@example.com'
        }
    });

@sfc-gh-mbhardwaj
Copy link

I am still having this issue? Is there a solution for this? It keeps throwing 403 and am generating these calls from an application

@FULLBL00M
Copy link

I am still having this issue.
I have been working on this for over two weeks now :(

I have not had success with ruby, python, or postman.

I have successfully generated tokens with postman ONLY.

I am currently dealing with
"message": "Not Authorized to access this resource/api"

All I need to do is return a list of devices from the directory api and it is one of the hardest things I have ever tried to do as a developer and that is crazy!

I am really fighting burnout on this one and any advice would be greatly appreciated.

@lyricnz
Copy link

lyricnz commented Jun 4, 2022

@FULLBL00M I don't know about nodejs, but for me in python, here's what I just did to get it working:

Screen Shot 2022-06-04 at 3 08 11 pm

SCOPES = [
    'https://www.googleapis.com/auth/admin.directory.group.readonly',
    'https://www.googleapis.com/auth/admin.directory.domain.readonly',
    'https://www.googleapis.com/auth/admin.directory.user.readonly',
]

    creds, project_id = google.auth.load_credentials_from_file(filename='blah/Downloads/dummyproject1-blah.json', scopes=SCOPES)
    creds = creds.with_subject('myfirst.mylast@mydomain.com')

@bgavas
Copy link

bgavas commented Aug 10, 2022

In my case, I solved by giving the view_type parameter like the following

    const users = await this.adminClient.users.list({
      customer: 'my_customer',
      maxResults: 10,
      orderBy: 'email',
      viewType: 'domain_public',   <<<------------ This
    });

@zetaminer
Copy link

zetaminer commented Sep 12, 2022

Solved my issue of getting authenticated using a service account with domain-wide delegation in python by adding
credentials = credentials.create_delegated(user_email) as shown in link that @AlonGluz linked above.

Full code:

from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
KEY_FILE_LOCATION = 'serviceAccountCreds.json'
CUSTOMER_ID = 'your_customer_id'
DELEGATED_ADMIN_EMAIL = 'admin@your_domain'

def create_directory_service(user_email):
    """Build and returns an Admin SDK Directory service object authorized with the service accounts
    that act on behalf of the given user.

    Args:
      user_email: The email of the user. Needs permissions to access the Admin APIs.
    Returns:
      Admin SDK directory service object.
    """
    credentials = ServiceAccountCredentials.from_json_keyfile_name(KEY_FILE_LOCATION, SCOPES)
    credentials = credentials.create_delegated(user_email)

    return build('admin', 'directory_v1', credentials=credentials)


service = create_directory_service(DELEGATED_ADMIN_EMAIL)

print('Getting the first 10 users in the domain')
results = service.users().list(customer= CUSTOMER_ID, maxResults=10, orderBy='email').execute()
print('getting results')
users = results.get('users', [])

if not users:
    print('No users in the domain.')
else:
    print('Users:')
    for user in users:
        print(u'{0} ({1})'.format(user['primaryEmail'],user['name']['fullName']))

@tyteen4a03
Copy link

tyteen4a03 commented Sep 19, 2022

If you are coming from Google, this solution is the one: #1884 (comment)

@julestruong
Copy link

https://stackoverflow.com/a/26469289/12958

Just enhancing this reply . this solved my problem.

@laurensdacosta
Copy link

I know this is a Node topic, but the same thing goes for the PHP library.
You can use $client->setSubject('EMAIL_OF_SERVICE_ACCOUNT_CREATOR');
Thank you for the intel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: admin external This issue is blocked on a bug with the actual product. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

No branches or pull requests