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

Add Documentation About Using Delegated Credential With Service Account #1699

Closed
melvinkcx opened this issue May 7, 2019 · 12 comments
Closed
Assignees
Labels
type: docs Improvement to the documentation for an API.

Comments

@melvinkcx
Copy link

In this guide, it provides code example to use delegated domain-wide authority in Python.

from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

delegated_credentials = credentials.with_subject('user@example.org')

However, this seems to be non-existent in this Nodejs client lib. I spent some time navigating the source code before I managed to use delegated credential in Nodejs.

Hence, I suggest we supplement this piece of information in the README.

@melvinkcx melvinkcx changed the title Add Documentation For Using Delegated Credential With Service Account Add Documentation About Using Delegated Credential With Service Account May 7, 2019
@bcoe bcoe added type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. type: docs Improvement to the documentation for an API. labels May 7, 2019
@goooseman
Copy link

Oh, I've just spent 4 hours searching for it.

Here is the code sample:

const getAdminApi = async () => {
  const auth = await google.auth.getClient({
    scopes: ["https://www.googleapis.com/auth/admin.directory.user"],
  });
  // Yep, that's weird. But we need it. And yes, there is no such parameter nor in docs nor in typings
  // But we need it, believe me. Spent 4 hours searching for it
  // https://stackoverflow.com/questions/50335892/how-to-impersonate-an-admin-user-when-using-getclient-in-the-google-api-nodejs
  // @ts-ignore
  auth.subject = process.env.GOOGLE_ADMIN_EMAIL;
  return google.admin({
    version: "directory_v1",
    auth,
  });
};

export const createUser = async ({
  email,
  username,
  password,
}: {
  email: string;
  password: string;
  username: UserName;
}) => {
  const adminApi = await getAdminApi();
  const { data: user } = await adminApi.users.insert({
    requestBody: {
      password: md5(password),
      hashFunction: "MD5",
      primaryEmail: email,
      name: username,
    },
  });
  user.password = password;
  return user;
};

Property subject does not exists in authClient regarding the docs or typings. But it exists and it works!

@goooseman
Copy link

Ok, it does exists in typings:
https://github.com/googleapis/google-auth-library-nodejs/blob/37bb8c7cd0a6501103274284d9cddd6816cc881e/src/auth/jwtclient.ts#L36

So I've rewritten my code without using @ts-ignore by manually selecting JWT class for Auth client:

import { JWT } from "google-auth-library";

  const auth = (await google.auth.getClient({
    scopes: ["https://www.googleapis.com/auth/admin.directory.user"],
  })) as JWT;

  auth.subject = process.env.GOOGLE_ADMIN_EMAIL;

But anyway it should be mentioned in the docs, because users trying to access directory API to create or manipulate users in their G Suite SDK will face unrecognized 403 error and in all these guides: 1, 2 is not written about providing subject.

Ok, in one of them it is said, that you need to impersonate your service account with admin account, but it is not possible to find anything by "impersonate" keyword in this repo.

@bcoe
Copy link
Contributor

bcoe commented Jun 4, 2019

@goooseman 👋 do you think it would be sufficient to add another example in samples/ demonstrating the approach you outline? Any interest in making a PR?

@JustinBeckwith JustinBeckwith removed the type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. label Jun 5, 2019
@staadecker
Copy link

I spent 2 days until I found this thread! Definitely would recommend better documenting this and adding subject as a parameter to the GoogleAuth constructor. Having the samples in Node on the doc pages linked by @goooseman would also be incredibly helpful!

@vettloffah
Copy link

Would this also lead to "Precondition check failed" error? Os is that something else?

@etiennebatise
Copy link

Hi all,

Encountered the same problem today. I definitely agree with @melvinkcx: the documentation is not clear enough.

I was trying to access the calendars of GSuite user with a service account credentials. The code samples provided by @goooseman didn’t work in my case.

I ended up on this blog (🙏 @nilsnh) and the following code is doing the delegation right:

// loads credentials from .env file
require("dotenv").config();
import { google } from "googleapis";

function initializeDrive(version = "v3") {
  const client_email = process.env.GOOGLE_CLIENT_EMAIL;
  // add some necessary escaping so to avoid errors when parsing the private key.
  const private_key = process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, "\n");
  // impersonate an account with rights to create team drives
  const emailToImpersonate = "some-user@acme-industries.com";
  const jwtClient = new google.auth.JWT(
    client_email,
    null, // undefined if you use TypeScript
    private_key,
    ["https://www.googleapis.com/auth/drive"],
    emailToImpersonate
  );
  return google.drive({
    version: version,
    auth: jwtClient
  });
}

As explained in the article, GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY are available in the secret keyfile generate for the service account. Hope this helps.

I don’t know if that is the best way of doing but there should be a nodejs sample in the examples of this page https://developers.google.com/identity/protocols/oauth2/service-account#python

@gauravkrp
Copy link

gauravkrp commented Feb 2, 2021

In this guide, it provides code example to use delegated domain-wide authority in Python.

from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

delegated_credentials = credentials.with_subject('user@example.org')

However, this seems to be non-existent in this Nodejs client lib. I spent some time navigating the source code before I managed to use delegated credential in Nodejs.

Hence, I suggest we supplement this piece of information in the README.

Hey @melvinkcx can you show me how you did it in node.js
Same issue and I've been stuck for 2 days.

errors: [
    {
      message: 'Delegation denied for user email',
      domain: 'global',
      reason: 'forbidden'
    }
  ]

BTW, I am trying to read emails metadata for my org gsuite users.

@ArashMotamedi
Copy link

A few hours of research later, the solution ended up being quite simple, though poorly documented.

All I had to do was change this:

const auth = new google.auth.GoogleAuth({
    keyFile: "service-account.json",
    scopes: [ ... ],
});

to this:

const auth = new google.auth.JWT({      // JWT instead of GoogleAuth
    subject: "me@mycompany.com",        // include this property  
    keyFile: "service-account.json",
    scopes: [ ... ],
})

This thread helped home in on the solution. Thanks all.

@antonydevanchi
Copy link

Why issue closed? Docs still no have any mention about that.

@ninapavlich
Copy link

ninapavlich commented Mar 16, 2023

UPDATE: I was able to make this work by making the following change to the auth object:

const auth = new google.auth.JWT(
        myServiceAccountCreds.client_email,
        undefined,
        myServiceAccountCreds.private_key,
        ['https://www.googleapis.com/auth/drive.readonly.metadata'],
        "mysubject@mydomain.com"
      );

Thanks for sharing this info @ArashMotamedi!

One clarification if anyone recalls: When I tried to swap the google.auth.GoogleAuth object with google.auth.JWT, I ran into the following error:
Error: error:1E08010C:DECODER routines::unsupported which I suspect is because my key file is maybe not in the right format.

This is the format of the file I am using:

{
  "type": "service_account",
  "project_id": "1234",
  "private_key_id": "1234",
  "private_key": "-----BEGIN PRIVATE KEY-----asdf\n-----END PRIVATE KEY-----\n",
  "client_email": "adsf@asdf.iam.gserviceaccount.com",
  "client_id": "asdf",
  "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/asdf%40asdf.iam.gserviceaccount.com"
}

Any guidance about what format is required here would be super helpful!

@mhio
Copy link

mhio commented Oct 19, 2023

This example would be really useful to have as a tab on the documentation as the other code API samples are so different.

A few hours of research later, the solution ended up being quite simple, though poorly documented.

All I had to do was change this:

const auth = new google.auth.GoogleAuth({
    keyFile: "service-account.json",
    scopes: [ ... ],
});

to this:

const auth = new google.auth.JWT({      // JWT instead of GoogleAuth
    subject: "me@mycompany.com",        // include this property  
    keyFile: "service-account.json",
    scopes: [ ... ],
})

This thread helped home in on the solution. Thanks all.

@Jpaulsisson
Copy link

You're all so beautiful for figuring this out. I spent 4 days searching for this answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: docs Improvement to the documentation for an API.
Projects
None yet
Development

No branches or pull requests