Peter S
Peter S

Reputation: 351

How do I call the gmail api in Spring Boot 2.x using a service account?

I have a GSuite service account configured to access corporate user gmail accounts, I have provided it with all of the privileges in the G Suite Admin console including Domain Wide access. I create a service account and now want to use the credentials to send emails on their behalf.

Here is my code so far:

  public void gmailTest(){
  log.info("Gmail test");

  List<String> SCOPES = new ArrayList<String>(GmailScopes.all());
  // List<String> SCOPES = GmailScopes.all();
  InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("Program Name-12345678.json");

  try {
  if(resourceAsStream != null) {
    NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

    log.info("Reading credential file");
    GoogleCredential credential = GoogleCredential.fromStream(resourceAsStream);
    log.info("Creating scopes");
    credential = credential.createScoped(SCOPES);

    log.info("building gmail api service");
    Gmail gmailService = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName("ept-mailer").build();
    String user = "[email protected]";

    log.info("calling gmail api");
    ListLabelsResponse listResponse = gmailService.users().labels().list(user).execute();
    log.info("call did not error");
    List<Label> labels = listResponse.getLabels();
    if (labels.isEmpty()) {
      System.out.println("No labels found.");
    } else {
      System.out.println("Labels:");
      for (Label label : labels) {
        System.out.printf("- %s\n", label.getName());
      }
    }
  }
  }
  catch (IOException | GeneralSecurityException ex) {
  log.error(ex.getMessage());
  }

Here are my privileges:

 Email (Manage labels)  https://www.googleapis.com/auth/gmail.labels 
 https://www.googleapis.com/auth/gmail.metadata 
 Email (Read/Write)  https://www.googleapis.com/auth/gmail.modify 

Here is the error that I get:

 400 Bad Request
 {
   "code" : 400,
   "errors" : [ {
     "domain" : "global",
     "message" : "Bad Request",
     "reason" : "failedPrecondition"
   } ],
   "message" : "Bad Request"
 }

Upvotes: 2

Views: 2672

Answers (2)

i.karayel
i.karayel

Reputation: 4875

I had the same problem and finally, I solved it. I hope that solution helps others.

Create a service account and delegate the right to GSuite.

  1. Go to google cloud console go-to service account
  2. Enable G Suite domain-wide Delegation
  3. Copy Client Id
  4. Login to your GSuite account. Go to Security -> API Controls -> Domain-wide Delegation
  5. Add your Client ID and scopes.
  6. and the final sample java code:

Goolge Cloud Console

GSuite Console

@Value("${gcp.credentials.file}")
private String CREDENTIALS_FILE_PATH;


private static Credentials getCredentials(String CREDENTIALS_FILE_PATH) throws IOException {
    InputStream in = FileLoaderImpl.getStream(CREDENTIALS_FILE_PATH);
    if (in == null) {
        throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
    }
    GoogleCredentials credentials = GoogleCredentials
            .fromStream(in)
            .createDelegated(EMAIL_FROM)
            .createScoped(List.of(GmailScopes.GMAIL_SEND, GmailScopes.GMAIL_LABELS));
    return credentials;
}

private Gmail getGmailService() throws IOException, GeneralSecurityException {
    NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
    return new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY,
            new HttpCredentialsAdapter(getCredentials(CREDENTIALS_FILE_PATH)))
            .setApplicationName(APPLICATION_NAME)
            .build();
}

@Override
public Message sendMailWithTemplate(StarMail starMail, boolean isHtml) {
    try {
        MimeMessage mimeMessage = EmailUtils.createEmailMessage(starMail, isHtml);
        // MimeMessage mm = EmailUtils.createEmail(toEmail, USER_ID, subject, bodyText);
        Gmail service = getGmailService();
        Message message = EmailUtils.sendMessage(service, USER_ID, mimeMessage);
        LOGGER.info("Message id: {} ", message.getId());
        LOGGER.info(message.toPrettyString());
        return message;
    } catch (IOException | MessagingException | GeneralSecurityException e) {
        LOGGER.error("sending email failed {}", e);
        throw new RuntimeException("sending email failed " + e.getMessage());
    }
}

Upvotes: 3

Peter S
Peter S

Reputation: 351

Ok the answer was that you MUST specify a user when creating the credential. In the newer version of the API this call has been changed to: .createDelegated(). Just put the users email that you wish to impersonate there.

 GoogleCredential credential = GoogleCredential.fromStream(resourceAsStream).createDelegated("[email protected]");

Upvotes: 2

Related Questions