I’m working on a Java web application that needs to send emails through Gmail using JavaMail 1.6.0. Instead of asking users to enable less secure app access, I want to implement proper OAuth2 authentication.
I’ve set up everything on Google Developer Console including creating OAuth2 credentials and enabling Gmail API access. The OAuth2 flow works correctly and I receive valid access and refresh tokens.
Here’s my authorization URL setup:
String authUrl = new GoogleAuthorizationCodeRequestUrl(
appClientId,
callbackUrl,
Arrays.asList(GmailScopes.GMAIL_SEND)
).setAccessType("offline").build();
Token exchange also works fine:
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
new JacksonFactory(),
appClientId,
appClientSecret,
authCode,
callbackUrl
).execute();
However, when I try to use the access token with JavaMail SMTP, I get authentication failures. My SMTP configuration:
The debug output shows that Gmail server supports XOAUTH2 but returns a 400 error when I attempt authentication. I’ve verified the token works with Google’s REST API. What could be causing this authentication issue?
The 400 error occurs because JavaMail’s default OAuth2 setup does not correctly implement Google’s XOAUTH2 protocol. I experienced the same issue when transitioning from app passwords to OAuth2. JavaMail 1.6.0 has an issue with its XOAUTH2 provider that leads to malformed authentication strings for Gmail’s SMTP. To resolve this, disable JavaMail’s built-in XOAUTH2 feature and create a custom OAuth2Authenticator class that extends javax.mail.Authenticator. Remove the “mail.smtp.auth.mechanisms” property and directly pass your custom authenticator to Session.getInstance(). Ensure your authenticator constructs the SASL XOAUTH2 string with proper base64 encoding and that your OAuth2 scope includes “https://mail.google.com/”, as SMTP requires broader permissions than the REST API.
I had the exact same issue with OAuth2 and JavaMail for Gmail. The problem wasn’t my token - it was how I formatted the XOAUTH2 SASL string that JavaMail sends to Gmail’s SMTP server. Your token might work fine for REST API calls, but JavaMail needs specific formatting for SMTP auth. You’ll need a custom authenticator that builds the XOAUTH2 string like this: “user=username\x01auth=Bearer access_token\x01\x01”. Make sure you’re using the email address tied to your OAuth2 token as the username in Session.getInstance(). Also check your token scope - you need the Gmail SMTP scope, not just the API scope. That 400 error usually means your auth data is malformed, not that your token’s invalid.
Check if your refresh token’s working when the access token expires. I got the same 400 errors - turned out my token was dying mid-session. Also don’t mix up oauth scopes - Gmail SMTP needs different permissions than the API. Try regenerating tokens with the right SMTP scope first.