Search code examples
gmail-apigoogle-oauthgoogle-oauth-java-client

Gmail (for business) API doesn't allow to send email from Alias?


I want to email my customers using different "roles" (e.g. info@ , customer-support@, tech-support@, no-reply@).

I've tried 2 approaches:

  1. Multiple "users"/accounts in my Gmail for business application.
  2. Single gmail account with multiple aliases.

I started by setting up a Service Account with global delegation for my Gmail for Business application.

To test that it works, I've set up 2 users: [email protected] and [email protected]. Indeed, I can successfully send email both from lev@ and root@.

However, when I tried adding 5 distinct user accounts for my application, Gmail got paranoid of bots/abuse and asked me to prove that all the accounts are "human" including setting up passwords, signing in and SMS-text validation via phone. Moreover, they require different phones for different accounts to prove it's a different person. So the setup of the accounts becomes a major issue.

I also want to avoid creating multiple accounts since I'm paying for each one, and since semantically, all the roles are just a single account. So aliases seem like a better idea.

The problem is that when I'm trying to send email and set the "from" field to the alias (e.g. from:[email protected]), I'm getting the following exception:

Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
  "code" : 403,
  "errors" : [ {
    "domain" : "global",
    "message" : "Delegation denied for [email protected]",
    "reason" : "forbidden"
  } ],
  "message" : "Delegation denied for [email protected]"
}

Anyone faced and solved this issue?

The authentication/credential code is as follows:

/*
 * Set up a hashmap HashMap<String, Gmail> gmailServiceByAccount where
 * gmailServiceByAccount.get(emailAccount) contains an authorized Gmail service
 */
private void prepareService(String emailAccount) throws Exception {
    if (gmailServiceByAccount.containsKey(emailAccount)) {
        return;
    }
    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();

    GoogleCredential credential = new GoogleCredential.Builder()
    .setTransport(httpTransport)
    .setJsonFactory(jsonFactory)
    .setServiceAccountId(Config.getInstance().getProperty(Config.gmail_service_account))
    .setServiceAccountPrivateKeyFromP12File(new File(Config.getInstance().getPathToGmailCredential()))
    .setServiceAccountScopes(Arrays.asList(GmailScopes.GMAIL_COMPOSE))
    .setServiceAccountUser(emailAccount)
    .build();        


    gmailServiceByAccount.put(
        emailAccount,
        new Gmail.Builder(httpTransport, jsonFactory, credential)
            .setApplicationName(Config.getInstance().getProperty(Config.google_client_api_application_name))
            .build());
}

And the code which sends the email is as follows:

/**
 * Send an email using the parameters provided.
 *
 * @param fromPersonalName : the free text description of the "from" address (e.g. "Customer Suppport" or "No Reply").
 * @param fromAddress : the email address of the sender, the mailbox account (e.g. [email protected]).
 * @param to : the email address of the recepient.
 * @param subject : Subject of the email.
 * @param htmlContent : (may be null) The HTML-styled body text of the email.
 * @param plainTextContent : (may be null)  The plain text body of the email (e.g if the customer email client does not support or disables html email).
 */
public void sendMail(String fromPersonalName, String fromAddress, String to, String subject, String htmlContent, String plainTextContent) 
        throws Exception {
    prepareService(fromAddress);
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);

    MimeMessage email = new MimeMessage(session);
    InternetAddress tAddress = new InternetAddress(to);
    InternetAddress fAddress = new InternetAddress(fromAddress);
    fAddress.setPersonal(fromPersonalName);
    email.setFrom(fAddress);
    email.addRecipient(javax.mail.Message.RecipientType.TO, tAddress);
    email.setSubject(subject);

    Multipart multiPart = new MimeMultipart("alternative");
    if (!StringValidation.isEmpty(plainTextContent)) {
        MimeBodyPart textPart = new MimeBodyPart();
        textPart.setContent(plainTextContent, "text/plain");
        textPart.setHeader("Content-Type", "text/plain; charset=\"UTF-8\"");
        multiPart.addBodyPart(textPart); 
    }

    if (!StringValidation.isEmpty(htmlContent)) {
        MimeBodyPart htmlPart = new MimeBodyPart();
        htmlPart.setContent(htmlContent, "text/html; charset=\"UTF-8\"");
        multiPart.addBodyPart(htmlPart);
    }        
    email.setContent(multiPart);


    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    email.writeTo(bytes);
    String encodedEmail = Base64.encodeBase64URLSafeString(bytes.toByteArray());
    Message message = new Message();
    message.setRaw(encodedEmail);
    gmailServiceByAccount.get(fromAddress).users().messages().send(fromAddress, message).execute();

}

Solution

  • After additional research, it looks like the only option is to have multiple users.

    The code I've posted indeed works for multiple users, but not for anything else.

    I've tried multiple options including aliases and group email accounts. I'd either get "delegation denied" or "invalid grant" errors.

    I've tried contacting Google For Business customer and tech support, but they don't support the API.

    There's a great workaround to creating several users without having to go through phone validation. Just specify these users as "existing users" when you're signing into Google For Business initially, and activate them before you even transfer the domain.

    For the account I've created without pre-existing users, I had to ask my friend's phones for phone validation.