Gmail with Oauth rejects my token/password
I am trying to send mail from Django. I've tried two different ways, both of which fail in different ways.
When I tried using an app password, I had no errors, but the email just never arrived. And the sender's outbox is empty, too.
But that was after I tried the modern method, OAuth. After a process that I would never have finished without ChatGPT (because nobody else has created a guide that I can find), I set up something that almost works. I have a file with a refresh token in it, and a custom backend that uses it.
But when it's time to send an email, I'm told "Username and Password not accepted."
Here's what the token file is like:
{
"token": "[secret]",
"refresh_token": "[secret]",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "[secret]",
"client_secret": "[secret]",
"scopes": ["https://www.googleapis.com/auth/gmail.send"],
"universe_domain": "googleapis.com",
"account": "",
"expiry": "2025-01-05T07:39:06.980351Z"
}
And this is the backend:
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from django.core.mail.backends.smtp import EmailBackend
from pathlib import Path
import os
class GmailOAuth2Backend(EmailBackend):
def _get_access_token(self):
dir = Path(__file__).resolve().parent
file = os.path.join(dir, 'oauth2tokenfile.secret.json')
creds = Credentials.from_authorized_user_file(file)
if creds.expired and creds.refresh_token:
creds.refresh(Request())
return creds.token
def open(self):
self.password = self._get_access_token()
super().open()
And finally, my settings
EMAIL_BACKEND = "[secret].GmailOAuth2Backend"
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = '[my email]@gmail.com'
According to the debugger, creds.valid = True (after refresh).
He is not allowed not to eat this came. I have a MFE with a react.
The "Username and Password not accepted" error typically occurs when gmail's smtp server doesn't recognize or accept the credentials being used.
Debug the access token which is being passed as a password to gmail's smtp server this may help
def _get_access_token(self):
dir = Path(__file__).resolve().parent
file = os.path.join(dir, 'oauth2tokenfile.secret.json')
try:
creds = Credentials.from_authorized_user_file(file)
if creds.expired and creds.refresh_token:
creds.refresh(Request())
print(f"Access Token: {creds.token}") # debugging
return creds.token
except Exception as e:
raise Exception(f"Error loading or refreshing credentials: {e}")
One more thing you can try which is to isolate the issue and test the SMTP authentication manually using a library like smtplib
import smtplib
# Ensure the EMAIL_HOST_USER in your Django settings matches the account associated with the oauth2 token (email field)
EMAIL = 'your_email@gmail.com'
ACCESS_TOKEN = 'your_access_token'
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(EMAIL, ACCESS_TOKEN)
print("login successful!")
except smtplib.SMTPAuthenticationError as e:
print("authentication failed:", e)
If this test fails with the same error, the issue is with your token or Gmail account settings.