Gmail APIs
Trying to send a mail using a "Service Account" & getting following error.
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
{
"code": 400,
"errors": [
{
"domain": "global",
"message": "Precondition check failed.",
"reason": "failedPrecondition"
}
],
"message": "Precondition check failed.",
"status": "FAILED_PRECONDITION"
}
I am using following sample.
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package com.gmail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.commons.codec.binary.Base64;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.Message;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
/* Class to demonstrate the use of Gmail Send Message API */
public class App {
/**
* Send an email from the user's mailbox to its recipient.
*
* fromEmailAddress - Email address to appear in the from: header
* toEmailAddress - Email address of the recipient
* @return the sent message, {@code null} otherwise.
* @throws MessagingException - if a wrongly formatted address is encountered.
* @throws IOException - if service account credentials file not found.
*/
public static Message sendEmail(final String fromEmailAddress, final String toEmailAddress)
throws MessagingException, IOException {
/*
* Load pre-authorized user credentials from the environment. TODO(developer) -
* See https://developers.google.com/identity for guides on implementing OAuth2
* for your application.
*/
final GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()
.createScoped(GmailScopes.GMAIL_SEND);
final HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
// Create the gmail API client
final Gmail service = new Gmail.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(),
requestInitializer).setApplicationName("Gmail samples").build();
// Create the email content
final String messageSubject = "Test message";
final String bodyText = "lorem ipsum.";
// Encode as MIME message
final Properties props = new Properties();
final Session session = Session.getDefaultInstance(props, null);
final MimeMessage email = new MimeMessage(session);
email.setFrom(new InternetAddress(fromEmailAddress));
email.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(toEmailAddress));
email.setSubject(messageSubject);
email.setText(bodyText);
// Encode and wrap the MIME message into a gmail message
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
email.writeTo(buffer);
final byte[] rawMessageBytes = buffer.toByteArray();
final String encodedEmail = Base64.encodeBase64URLSafeString(rawMessageBytes);
Message message = new Message();
message.setRaw(encodedEmail);
try {
// Create send message
message = service.users().messages().send("me", message).execute();
System.out.println("Message id: " + message.getId());
System.out.println(message.toPrettyString());
return message;
} catch (final GoogleJsonResponseException e) {
// TODO(developer) - handle error appropriately
final GoogleJsonError error = e.getDetails();
if (error.getCode() == 403) {
System.err.println("Unable to send message: " + e.getDetails());
} else {
throw e;
}
}
return null;
}
public static void main(final String[] a) throws MessagingException, IOException {
sendEmail("<PII removed by staff>", "<<PII removed by staff>>");
}
}
You have to specify the user you are looking to impersonate when using domain wide delegation. It should be something like this:
final GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()
.createScoped(GmailScopes.GMAIL_SEND).CreateWithUser("user@example.com")
The library should auto-populate the "CreateWithUser" or
.createDelegated("user@example.com");
com.google.auth.oauth2.GoogleAuthException: Error getting access token for service account: 401 Unauthorized
I am getting above error now with the following change.
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()
.createScoped(GmailScopes.GMAIL_SEND).createDelegated("<PII removed by staff>")
What could be missing? I did search on the internet & even the docs are easier for setup with service account.
Use-case:
I need to use Gmail api to access all mailboxes under my domain. So that my app can send emails based on application needs. Currently, I am doing it by enabling Less secure access (password based) which is not a recommended way.
Is using service account the right choice or should I use individual users Oauth authentication?
I tried creating a VM with service account, but then I get following error.
{"code":403,"details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"ACCESS_TOKEN_SCOPE_INSUFFICIENT"}],"errors":[{"domain":"global","message":"Insufficient Permission","reason":"insufficientPermissions"}],"message":"Request had insufficient authentication scopes.","status":"PERMISSION_DENIED"}
Finally, following worked.
I have some questions:
gcloud auth application-default login