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

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.

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.

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())