Gmail IMAP OAuth2 authentication keeps giving invalid credentials error

I’m trying to build an email client that connects to Gmail using IMAP with OAuth2 authentication but I keep getting authentication failures.

I can get the access token just fine, but when I try to connect to Gmail’s IMAP server, it always says “Invalid credentials”. Here’s what I did:

  1. Set up OAuth2 credentials in Google Console
  2. Got authorization code through browser
  3. Exchanged code for access and refresh tokens
  4. Used access token in my IMAP connection

My account has 2FA enabled and I created an app-specific password too.

public class GmailSaslFactory implements SaslClientFactory {
    private static final Logger log = Logger.getLogger(GmailSaslFactory.class.getName());
    
    public static final String TOKEN_PROPERTY = "mail.imaps.sasl.mechanisms.oauth2.token";
    
    public SaslClient createSaslClient(String[] mechanisms, String authId, 
                                      String protocol, String server,
                                      Map<String, ?> properties, CallbackHandler handler) {
        boolean found = false;
        for (String mech : mechanisms) {
            if ("XOAUTH2".equalsIgnoreCase(mech)) {
                found = true;
                break;
            }
        }
        if (!found) {
            log.info("No matching mechanisms found");
            return null;
        }
        return new GmailSaslClient((String) properties.get(TOKEN_PROPERTY), handler);
    }
    
    public String[] getMechanismNames(Map<String, ?> properties) {
        return new String[] {"XOAUTH2"};
    }
}

class GmailSaslClient implements SaslClient {
    private final String accessToken;
    private final CallbackHandler handler;
    private boolean finished = false;
    
    public GmailSaslClient(String token, CallbackHandler cbHandler) {
        this.accessToken = token;
        this.handler = cbHandler;
    }
    
    public byte[] evaluateChallenge(byte[] challenge) throws SaslException {
        if (finished) {
            return new byte[] {};
        }
        
        NameCallback nameCallback = new NameCallback("username");
        PasswordCallback passCallback = new PasswordCallback("pass", false);
        
        try {
            handler.handle(new Callback[] { nameCallback, passCallback });
        } catch (Exception e) {
            throw new SaslException("Callback failed: " + e);
        }
        
        String username = nameCallback.getName();
        String authString = String.format("user=%s\1auth=Bearer %s\1\1", 
                                         username, accessToken);
        finished = true;
        return authString.getBytes();
    }
    
    public boolean isComplete() {
        return finished;
    }
}

public void connectToGmail(String[] arguments) throws Exception {
    String userEmail = arguments[0];
    String token = arguments[1];
    
    Security.addProvider(new OAuth2Provider());
    
    Properties config = new Properties();
    config.put("mail.imaps.sasl.enable", "true");
    config.put("mail.debug.auth", "true");
    config.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
    config.put(GmailSaslFactory.TOKEN_PROPERTY, token);
    
    Session mailSession = Session.getInstance(config);
    mailSession.setDebug(true);
    
    IMAPSSLStore imapStore = new IMAPSSLStore(mailSession, null);
    imapStore.connect("imap.gmail.com", 993, userEmail, "");
}

The debug output shows:

DEBUG IMAPS: SASL client XOAUTH2
B1 AUTHENTICATE XOAUTH2
B1 NO [AUTHENTICATIONFAILED] Invalid credentials (Failure)

What am I doing wrong here? The access token works fine when I test it with Google’s APIs directly.

Had the same issue a few months ago with OAuth2 and our mail system. You need to enable “Less secure app access” in Google settings even with OAuth2 - sounds backwards but IMAP requires it. Make sure your OAuth2 scope is ‘https://mail.google.com/’ not just basic Gmail API scopes. Access tokens die after an hour, so check you’re refreshing them right. Test your token with a curl command to Gmail’s API first before trying IMAP.

Check if IMAP is enabled in your Gmail settings - OAuth2 won’t work without it. Also make sure your OAuth2 app’s redirect URI matches exactly what you used during auth. Your authentication string looks right, but double-check for extra spaces or encoding problems in the token. I had similar issues when my OAuth2 app wasn’t verified by Google - unverified apps get hit with tighter restrictions. Try a different Gmail account to see if it’s account-specific. Since your token works with other Google APIs, it’s probably an IMAP configuration problem.

your token’s probably expired or you’re using the wrong type - Gmail IMAP needs a specific OAuth2 flow. Regenerate your access token and double-check you’ve got the right client ID/secret combo. also make sure your redirect URI is configured correctly - even tiny typos will kill authentication. I’ve seen this tons of times when people mix up different OAuth2 setups.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.