CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-oauth2

A Python OAuth 1.0 library providing comprehensive authentication, signing, and client capabilities.

Pending
Overview
Eval results
Files

client-extensions.mddocs/

Client Extensions

Protocol-specific OAuth clients for SMTP and IMAP that provide XOAUTH authentication support. These extensions allow OAuth-authenticated access to email services that support the XOAUTH SASL mechanism.

Capabilities

IMAP OAuth Client

OAuth-enabled IMAP client that extends Python's standard imaplib.IMAP4_SSL to support XOAUTH authentication for accessing IMAP email servers with OAuth credentials.

from oauth2.clients.imap import IMAP4_SSL

class IMAP4_SSL:
    def authenticate(self, url: str, consumer, token):
        """
        Authenticate IMAP connection using XOAUTH.
        
        Args:
            url (str): IMAP service URL for XOAUTH string generation
            consumer: OAuth consumer credentials
            token: OAuth token credentials
            
        Raises:
            ValueError: If consumer or token is invalid
        """

SMTP OAuth Client

OAuth-enabled SMTP client that extends Python's standard smtplib.SMTP to support XOAUTH authentication for sending email through OAuth-protected SMTP servers.

from oauth2.clients.smtp import SMTP

class SMTP:
    def authenticate(self, url: str, consumer, token):
        """
        Authenticate SMTP connection using XOAUTH.
        
        Args:
            url (str): SMTP service URL for XOAUTH string generation
            consumer: OAuth consumer credentials  
            token: OAuth token credentials
            
        Raises:
            ValueError: If consumer or token is invalid
        """

Usage Examples

Gmail IMAP Access

import oauth2
from oauth2.clients.imap import IMAP4_SSL

# OAuth credentials (obtain through Google OAuth flow)
consumer = oauth2.Consumer('your_consumer_key', 'your_consumer_secret')
token = oauth2.Token('user_access_token', 'user_access_token_secret')

# Connect to Gmail IMAP
imap_client = IMAP4_SSL('imap.gmail.com', 993)

# Authenticate using XOAUTH
gmail_url = 'https://mail.google.com/mail/b/user@gmail.com/imap/'
imap_client.authenticate(gmail_url, consumer, token)

# Use standard IMAP commands
imap_client.select('INBOX')
typ, data = imap_client.search(None, 'ALL')

# Get message IDs
message_ids = data[0].split()
print(f"Found {len(message_ids)} messages")

# Fetch first message
if message_ids:
    typ, msg_data = imap_client.fetch(message_ids[0], '(RFC822)')
    email_body = msg_data[0][1]
    print(f"First message: {email_body[:200]}...")

# Close connection
imap_client.close()
imap_client.logout()

Yahoo Mail IMAP Access

import oauth2
from oauth2.clients.imap import IMAP4_SSL

# Yahoo OAuth credentials
consumer = oauth2.Consumer('yahoo_consumer_key', 'yahoo_consumer_secret')
token = oauth2.Token('yahoo_access_token', 'yahoo_access_token_secret')

# Connect to Yahoo IMAP
imap_client = IMAP4_SSL('imap.mail.yahoo.com', 993)

# Authenticate with Yahoo IMAP
yahoo_url = 'https://mail.yahoo.com/ws/mail/v1.1/soap'
imap_client.authenticate(yahoo_url, consumer, token)

# List available folders
typ, folders = imap_client.list()
for folder in folders:
    print(f"Folder: {folder}")

# Select inbox and get message count
imap_client.select('INBOX')
typ, data = imap_client.search(None, 'UNSEEN')
unseen_count = len(data[0].split()) if data[0] else 0
print(f"Unseen messages: {unseen_count}")

imap_client.logout()

Gmail SMTP Sending

import oauth2
from oauth2.clients.smtp import SMTP
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# OAuth credentials
consumer = oauth2.Consumer('gmail_consumer_key', 'gmail_consumer_secret')
token = oauth2.Token('user_access_token', 'user_access_token_secret')

# Connect to Gmail SMTP
smtp_client = SMTP('smtp.gmail.com', 587)
smtp_client.starttls()

# Authenticate using XOAUTH
gmail_url = 'https://mail.google.com/mail/b/user@gmail.com/smtp/'
smtp_client.authenticate(gmail_url, consumer, token)

# Create message
msg = MIMEMultipart()
msg['From'] = 'user@gmail.com'
msg['To'] = 'recipient@example.com'
msg['Subject'] = 'OAuth Test Email'

body = 'This email was sent using OAuth authentication!'
msg.attach(MIMEText(body, 'plain'))

# Send message
text = msg.as_string()
smtp_client.sendmail('user@gmail.com', 'recipient@example.com', text)

# Close connection
smtp_client.quit()

Error Handling

import oauth2
from oauth2.clients.imap import IMAP4_SSL
import imaplib
import socket

consumer = oauth2.Consumer('consumer_key', 'consumer_secret')
token = oauth2.Token('token_key', 'token_secret')

try:
    # Connect to IMAP server
    imap_client = IMAP4_SSL('imap.gmail.com', 993)
    
    # Authenticate
    gmail_url = 'https://mail.google.com/mail/b/user@gmail.com/imap/'
    imap_client.authenticate(gmail_url, consumer, token)
    
    print("Authentication successful!")
    
except ValueError as e:
    print(f"Invalid credentials: {e}")
    
except imaplib.IMAP4.abort as e:
    print(f"IMAP connection aborted: {e}")
    
except imaplib.IMAP4.error as e:
    print(f"IMAP error: {e}")
    # Common errors:
    # - Invalid credentials
    # - Authentication failure  
    # - Server rejected XOAUTH
    
except socket.error as e:
    print(f"Network error: {e}")
    
except Exception as e:
    print(f"Unexpected error: {e}")
    
finally:
    try:
        imap_client.logout()
    except:
        pass

Advanced IMAP Operations

import oauth2
from oauth2.clients.imap import IMAP4_SSL
import email
from datetime import datetime, timedelta

consumer = oauth2.Consumer('consumer_key', 'consumer_secret')
token = oauth2.Token('token_key', 'token_secret')

imap_client = IMAP4_SSL('imap.gmail.com', 993)
gmail_url = 'https://mail.google.com/mail/b/user@gmail.com/imap/'
imap_client.authenticate(gmail_url, consumer, token)

# Select inbox
imap_client.select('INBOX')

# Search for messages from last week
week_ago = (datetime.now() - timedelta(days=7)).strftime('%d-%b-%Y')
typ, data = imap_client.search(None, f'SINCE {week_ago}')

message_ids = data[0].split()
print(f"Found {len(message_ids)} messages from last week")

# Process each message
for msg_id in message_ids[:5]:  # Process first 5 messages
    typ, msg_data = imap_client.fetch(msg_id, '(RFC822)')
    
    # Parse email
    email_message = email.message_from_bytes(msg_data[0][1])
    
    print(f"From: {email_message['From']}")
    print(f"Subject: {email_message['Subject']}")
    print(f"Date: {email_message['Date']}")
    
    # Get email body
    if email_message.is_multipart():
        for part in email_message.walk():
            if part.get_content_type() == "text/plain":
                body = part.get_payload(decode=True).decode('utf-8')
                print(f"Body preview: {body[:100]}...")
                break
    else:
        body = email_message.get_payload(decode=True).decode('utf-8')
        print(f"Body preview: {body[:100]}...")
    
    print("-" * 50)

imap_client.close()
imap_client.logout()

Bulk Email Processing

import oauth2
from oauth2.clients.smtp import SMTP
from email.mime.text import MIMEText
import time

consumer = oauth2.Consumer('consumer_key', 'consumer_secret')
token = oauth2.Token('token_key', 'token_secret')

# Connect once for multiple sends
smtp_client = SMTP('smtp.gmail.com', 587)
smtp_client.starttls()

gmail_url = 'https://mail.google.com/mail/b/user@gmail.com/smtp/'
smtp_client.authenticate(gmail_url, consumer, token)

# Send multiple emails
recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com']

for recipient in recipients:
    msg = MIMEText(f'Hello {recipient}! This is a personalized message.')
    msg['From'] = 'sender@gmail.com'
    msg['To'] = recipient
    msg['Subject'] = 'Bulk Email Test'
    
    try:
        smtp_client.sendmail('sender@gmail.com', recipient, msg.as_string())
        print(f"Email sent to {recipient}")
        
        # Rate limiting
        time.sleep(1)
        
    except Exception as e:
        print(f"Failed to send to {recipient}: {e}")

smtp_client.quit()

Service-Specific Notes

Google Services

  • IMAP URL: https://mail.google.com/mail/b/{email}/imap/
  • SMTP URL: https://mail.google.com/mail/b/{email}/smtp/
  • Requirements: Enable "Less secure app access" or use App Passwords
  • Scopes: Requires appropriate Gmail API scopes during OAuth flow

Yahoo Mail

  • IMAP URL: https://mail.yahoo.com/ws/mail/v1.1/soap
  • SMTP URL: https://mail.yahoo.com/
  • Port: IMAP 993 (SSL), SMTP 587 (TLS)
  • Requirements: Yahoo OAuth app registration required

Generic OAuth Email Providers

The XOAUTH mechanism works with any email provider that supports it. Check provider documentation for:

  • XOAUTH support status
  • Required URL format for authentication
  • OAuth scope requirements
  • Server endpoints and ports

Implementation Details

XOAUTH String Format

The clients use oauth2.build_xoauth_string() to generate XOAUTH authentication strings in the format:

GET {url} oauth_consumer_key="{key}",oauth_nonce="{nonce}",oauth_signature="{signature}",oauth_signature_method="HMAC-SHA1",oauth_timestamp="{timestamp}",oauth_token="{token}",oauth_version="1.0"

Base64 Encoding

SMTP client automatically base64-encodes the XOAUTH string as required by the SMTP AUTH command, while IMAP passes the string directly to the IMAP AUTHENTICATE command.

Install with Tessl CLI

npx tessl i tessl/pypi-oauth2

docs

client-extensions.md

http-client.md

index.md

oauth-core.md

server-verification.md

tile.json