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

Gmail API with service account- Precondition check failed #2322

Closed
vettloffah opened this issue Aug 8, 2020 · 14 comments
Closed

Gmail API with service account- Precondition check failed #2322

vettloffah opened this issue Aug 8, 2020 · 14 comments
Assignees
Labels
type: question Request for information or clarification. Not an issue.

Comments

@vettloffah
Copy link

vettloffah commented Aug 8, 2020

I'm trying to connect with the google API's for the first time, and when I attempt to make a request to the gmail API I'm getting a Precondition check failed error. I am using a service account authorization, not Oauth2 user consent. Things I've tried:

  1. Authorized "domain wide delegation" for the service account.
  2. Ensured the APP is trusted in the G suite account.
  3. Have also tried just returning the projectId from the API - and that works. So it is authorizing and connecting. It's when I try to make a request to gmail specifically that's giving me issue.
  4. Service account role is "owner"

This is adapted from a sample, but the sample did not use service account auth so I wasn't able to use the sample directly.

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

const gmail = google.gmail('v1');

async function runSample() {
  // Obtain user credentials to use for the request
  const auth = new google.auth.GoogleAuth({
    keyFile: path.resolve(__dirname, 'google-key.json'),
    scopes: ['https://www.googleapis.com/auth/gmail.readonly'],
  });
  google.options({auth});

  const res = await gmail.users.messages.list({userId: 'me'}); // have tried with my gsuite email address as well
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

returning Error: Precondition check failed.

@yoshi-automation yoshi-automation added the triage me I really want to be triaged. label Aug 9, 2020
@vettloffah
Copy link
Author

vettloffah commented Aug 9, 2020

I figured out how to solve the issue.

I would like to sbumit a pull request for this issue to add a sample to the gmail sample library. It would look something like this (can work on refactoring to match code style in other samples if you give me the go-ahead on submitting a PR):

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

async getMessageList(userId, qty) {

  const JWT = google.auth.JWT;
  const authClient = new JWT({
    keyFile: path.resolve(__dirname, 'google-key.json'),
    scopes: ['https://www.googleapis.com/auth/gmail.readonly'],
    subject: '[email protected]' // google admin email address to impersonate
  });

  await authClient.authorize(); // once authorized, can do whatever you want

  const gmail = google.gmail({
    auth: authClient,
    version: 'v1'
  });

  const response = await gmail.users.messages.list({
    includeSpamTrash: false,
    maxResults: qty,
    q: "",
    userId: userId
  });

  // the data object includes a "messages" array of message data
  return response.data;

}

@JustinBeckwith JustinBeckwith added the type: question Request for information or clarification. Not an issue. label Aug 10, 2020
@yoshi-automation yoshi-automation removed the triage me I really want to be triaged. label Aug 10, 2020
@cpereyra5199
Copy link

@vettloffah What was the actual fix here? I am running into the same issue and have not been able to come up with a solution.

@vettloffah
Copy link
Author

@cpereyra5199 i posted the solution in my last comment. You have to delegate the service to a specific user using JWT authentication

@klvenky
Copy link

klvenky commented Aug 29, 2020

@vettloffah I would like to know "if you have used a service account for authorization?"

@klvenky
Copy link

klvenky commented Aug 29, 2020

I have actually tried something similar with GoogleAuth. I didn't understand what went wrong here.
I have even tried the approach which @vettloffah has mentioned by replacing the GoogleAuth code.
Can someone point me to what's wrong here?

import { google } from "googleapis";
import dotenv from "dotenv";

import path from "path";
dotenv.config();

(async () => {
  const SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"];

  const fileName = process.env.GOOGLE_APPLICATION_CREDENTIALS!;
  console.log({ fileName });
  const keyFile = path.resolve("./",fileName);
  console.log("calling gauth client");
  const gAuth = new google.auth.GoogleAuth({
    keyFile,
    scopes: SCOPES,
  });

  google.options({ auth: gAuth });
  const token = await gAuth.getAccessToken();
  console.log({ token });
  // token is getting generated here.
  const gmail = await google.gmail({ auth: gAuth, version: "v1" });
  const userId = process.env.SERVICE_ACCOUNT! || "me";
  const messages = await gmail.users.messages.list({ userId, token });
  console.log(messages);
})();
`

@klvenky
Copy link

klvenky commented Aug 29, 2020

I have found another issue with a similar error here
I am using a personal gmail account for this. And, I have created a service account for the same.
@JustinBeckwith Can you let me know if this is possible or not?

@sqrrrl
Copy link
Contributor

sqrrrl commented Sep 15, 2020

The Gmail API isn't intended to be used with service accounts (other than domain-wide delegation use cases.) You need to be acting as a real user -- either using oauth credentials obtained with user consent, or in the case of a Gsuite domain, using a service account delegating/impersonating a real user.

I'll file a bug to make the error message clearer, but it's likely that the failed precondition is that the service account isn't a valid gmail user.

@fhinkel
Copy link
Contributor

fhinkel commented Dec 7, 2020

Greetings, we're closing this. Looks like the issue got resolved. Please let us know if the issue needs to be reopened.

@fhinkel fhinkel closed this as completed Dec 7, 2020
@fhinkel fhinkel assigned fhinkel and unassigned sofisl Dec 8, 2020
@martinschnurer
Copy link

The Gmail API isn't intended to be used with service accounts (other than domain-wide delegation use cases.) You need to be acting as a real user -- either using oauth credentials obtained with user consent, or in the case of a Gsuite domain, using a service account delegating/impersonating a real user.

I'll file a bug to make the error message clearer, but it's likely that the failed precondition is that the service account isn't a valid gmail user.

This is ridiculous.

@Wolverine971
Copy link

This is the way

@Jerther
Copy link

Jerther commented Aug 23, 2022

And here's how to do it in Python

@aixi-mfisher
Copy link

So it has to be able to impersonate any user or none at all? Isn't that a pretty significant security risk?

@sqrrrl
Copy link
Contributor

sqrrrl commented Jan 30, 2023

Impersonation is neither required nor encouraged. Use regular OAuth 2.

Most of the conversation has been about wanting to use the gmail API with a service account where the service account is running as it's own identity. That isn't supported as gmail is inherently a product for human users. Service accounts with delegation is an option for some enterprise use cases, and the scope of delegation is users within that org.

There are handful of operations that actually require delegation, and that's mostly for security reasons. Settings that create forwarding rules are particularly sensitive as those forwarding rules persist even after a user has revoked access to an app. Anything guarded by https://www.googleapis.com/auth/gmail.settings.sharing is in that camp.

But the rest of the API no service accounts and no delegation required.

@nabsabraham
Copy link

@sqrrrl iiuc, if I wanted to auth with the gmail API for my personal inbox, it seems I need to make the oauth client screen and get app approval since I do not have a Google Workspace. Is this correct/suggested approach? Since its just for testing, I wonder if there's a faster way before getting app approval

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question Request for information or clarification. Not an issue.
Projects
None yet
Development

No branches or pull requests