.. _oauth: OAuth2 Authentication (XOAUTH2) =============================== aiosmtplib supports OAuth2 authentication via the XOAUTH2 mechanism, which is used by Gmail, Outlook.com, and other providers that have deprecated traditional password authentication. The SMTP server must advertise ``XOAUTH2`` as a supported authentication method. To use OAuth2, pass an async callable that returns a valid access token to the ``oauth_token_generator`` parameter. .. note:: The ``oauth_token_generator`` parameter is mutually exclusive with ``password``. You cannot use both at the same time. Basic Structure --------------- .. code-block:: python import aiosmtplib async def get_access_token() -> str: # Your token refresh logic here return "your_access_token" await aiosmtplib.send( message, hostname="smtp.gmail.com", port=465, use_tls=True, username="your.email@gmail.com", oauth_token_generator=get_access_token, ) Token Expiry and Refresh ------------------------ OAuth2 access tokens are short-lived (typically 1 hour). The callable passed as ``oauth_token_generator`` is responsible for returning a valid, non-expired token each time it is called. It will be called immediately before the ``XOAUTH2`` authentication command is sent to the server. **Your generator must handle expiry** aiosmtplib does not track token expiry times or automatically retry on authentication failure. Your callable should check if the token is expired and refresh it before returning. Some libraries (e.g. ``google-auth``, documented below) will handle this. Gmail Example ------------- Gmail allows OAuth2 for SMTP access. You'll need to create a project in the `Google Cloud Console `_, and obtain OAuth2 credentials. The ``google-auth`` library provides credential management with automatic token refresh. .. code-block:: python import asyncio from email.message import EmailMessage import aiosmtplib from google.oauth2.credentials import Credentials from google.auth.transport.requests import Request # Load your credentials (obtained via OAuth flow) credentials = Credentials( token="ya29.access_token", refresh_token="1//refresh_token", token_uri="https://oauth2.googleapis.com/token", client_id="your_client_id.apps.googleusercontent.com", client_secret="your_client_secret", ) async def get_gmail_token() -> str: """Return a valid access token, refreshing if needed.""" if not credentials.valid: # google-auth is synchronous, so run in a thread await asyncio.to_thread(credentials.refresh, Request()) return credentials.token async def send_email(): message = EmailMessage() message["From"] = "your.email@gmail.com" message["To"] = "recipient@example.com" message["Subject"] = "Hello from aiosmtplib!" message.set_content("Sent using OAuth2 authentication.") await aiosmtplib.send( message, hostname="smtp.gmail.com", port=465, use_tls=True, username="your.email@gmail.com", oauth_token_generator=get_gmail_token, ) asyncio.run(send_email()) Outlook / Microsoft 365 Example ------------------------------- Microsoft requires OAuth2 for SMTP authentication. See the `official Microsoft documentation `_ for setup instructions. The ``msal`` library provides credential management with automatic token refresh. .. code-block:: python import asyncio from email.message import EmailMessage import aiosmtplib import msal # Your Azure AD app registration details CLIENT_ID = "your_client_id" TENANT_ID = "your_tenant_id" # Or "common" for multi-tenant apps # Scope for SMTP sending SCOPES = ["https://outlook.office.com/SMTP.Send"] # Create MSAL public client app (for user authentication) app = msal.PublicClientApplication( CLIENT_ID, authority=f"https://login.microsoftonline.com/{TENANT_ID}", ) async def get_outlook_token() -> str: """Return a valid access token, refreshing silently if possible.""" accounts = app.get_accounts() if accounts: # Try to get token silently (uses cached/refresh token) result = await asyncio.to_thread( app.acquire_token_silent, SCOPES, account=accounts[0] ) if result and "access_token" in result: return result["access_token"] raise ValueError("No valid token available. Re-authentication required.") async def send_email(): message = EmailMessage() message["From"] = "your.email@outlook.com" message["To"] = "recipient@example.com" message["Subject"] = "Hello from aiosmtplib!" message.set_content("Sent using OAuth2 authentication.") await aiosmtplib.send( message, hostname="smtp.office365.com", port=587, start_tls=True, username="your.email@outlook.com", oauth_token_generator=get_outlook_token, ) asyncio.run(send_email())