Setting up OAuth2 authentication for Gmail SMTP connection

I need help implementing OAuth2 authentication for Gmail SMTP since Google is removing support for basic authentication methods. I have created a service account and downloaded the JSON credentials file but my authentication attempt is failing.

Here is my current implementation:

import com.google.auth.oauth2.ServiceAccountCredentials;
import com.sun.mail.smtp.SMTPTransport;
import jakarta.mail.Session;

import java.io.InputStream;
import java.util.Base64;
import java.util.Collections;
import java.util.Properties;

public class EmailAuthenticator {

  void authenticate() throws Exception {
    InputStream credentialsFile = this.getClass().getClassLoader().getResourceAsStream("service-account.json");
    String accountEmail = "[email protected]";
    
    GoogleCredentials creds = ServiceAccountCredentials.fromStream(credentialsFile)
        .createScoped(Collections.singletonList("https://mail.google.com/"));
    creds.refreshIfExpired();
    
    String accessToken = creds
        .getAccessToken()
        .getTokenValue();

    Properties mailProps = new Properties();
    mailProps.put("mail.smtp", "true");
    mailProps.put("mail.transport.protocol", "smtp");
    mailProps.put("mail.smtp.auth.mechanisms", "XOAUTH2");
    mailProps.put("mail.smtp.starttls.enable", "true");

    Session mailSession = Session.getInstance(mailProps);
    mailSession.setDebug(true);

    SMTPTransport smtpTransport = (SMTPTransport) mailSession.getTransport("smtp");
    smtpTransport.connect("smtp.gmail.com", 587, accountEmail, null);

    String oauthString = "user=" + accountEmail + "\001auth=Bearer " + accessToken + "\001\001";
    String encodedAuth = Base64.getEncoder().encodeToString(oauthString.getBytes());
    smtpTransport.issueCommand("AUTH XOAUTH2 " + encodedAuth, 235);
  }
}

The connection fails with a 555-5.5.2 syntax error during the authentication step. The server responds with “Syntax error, goodbye” before I even try to send any email. What could be causing this authentication failure?

you’re missing the smtp host in your mail props. add mailProps.put("mail.smtp.host", "smtp.gmail.com"); before creating the session. also check that domain-wide delegation is enabled for your service account if you’re impersonating users.

Your OAuth2 string construction is wrong, and you’re not handling the auth flow correctly. Service accounts don’t work the same way as regular user accounts with Gmail SMTP. You need domain-wide delegation to impersonate a real user email - you can’t use the service account email directly. Don’t manually issue the AUTH command either. Let SMTPTransport handle authentication by setting the password parameter to your access token in the connect call. Change it to smtpTransport.connect("smtp.gmail.com", 587, userEmailToImpersonate, accessToken) where userEmailToImpersonate is an actual Gmail address in your domain that the service account can impersonate.

Your Properties config is the problem. You’ve got mail.smtp set to “true” but it should be mail.smtp.auth. You’re also missing the host property. Here’s what fixed it for me after hitting the same auth errors:

mailProps.put("mail.smtp.host", "smtp.gmail.com");
mailProps.put("mail.smtp.port", "587");
mailProps.put("mail.smtp.auth", "true");
mailProps.put("mail.smtp.auth.mechanisms", "XOAUTH2");
mailProps.put("mail.smtp.starttls.enable", "true");

Ditch the manual AUTH command entirely - JavaMail does this automatically when you pass the token as the password. Double-check that your service account has domain-wide delegation enabled in Google Admin Console and you’re using an actual user email from your domain, not the service account email.