mutt-oauth2

Python versions PyPI - Version GitHub tag (with filter) License GitHub commits since latest release (by SemVer including pre-releases) CodeQL QA Tests Coverage Status Dependabot Documentation Status mypy uv pytest Ruff Downloads Stargazers pre-commit Prettier Follow @Tatsh Mastodon Follow

This is an update of Alexander Perlis’ script and conversion to a package. Instead of using GPG for token storage, this package uses Keyring.

Installation

Commands

mutt-oauth2

Obtain and print a valid OAuth2 access token.

Usage

mutt-oauth2 [OPTIONS]

Options

-a, --authorize

Manually authorise new tokens.

-d, --debug

Enable debug logging.

-t, --test

Test authentication.

-u, --username <username>

Keyring username.

Usage

Start by calling mutt-oauth2 -a. Be sure to have your client ID and and client secret available.

Scopes required

Provider

Scopes

Gmail

Gmail API

Microsoft

offline_access IMAP.AccessAsUser.All POP.AccessAsUser.All SMTP.Send

To support other accounts, use the --username argument with a unique string such as the account email address.

Test the script with the --test argument.

mutt configuration

Add the following to muttrc:

set imap_authenticators="oauthbearer:xoauth2"
set imap_oauth_refresh_command="/path/to/mutt-oauth2"
set smtp_authenticators=${imap_authenticators}
set smtp_oauth_refresh_command=${imap_oauth_refresh_command}

Library

Constants.

mutt_oauth2.constants.KEYRING_SERVICE_NAME

Keyring service name.

Registration information for OAuth2 providers.

mutt_oauth2.registrations.registrations

Registrations.

Utilities.

exception mutt_oauth2.utils.OAuth2Error

Generic OAuth2 error.

class mutt_oauth2.utils.SavedToken(access_token_expiration: datetime | None, client_id: str, client_secret: str | None, email: str, registration: Registration, access_token: str = '', refresh_token: str = '', tenant: str | None = None)

Data class for OAuth2 token information.

access_token : str

Access token.

access_token_expiration : datetime | None

Access token expiration.

as_json(indent: int | None = None) str

Convert the token to JSON.

Parameters:
indent : int | None

Indentation width for pretty-printing, or None for compact output.

Returns:

JSON representation of the token.

Return type:

str

client_id : str

Client ID.

client_secret : str | None

Client secret, if applicable.

async device_poll(device_code: str, session: AsyncSession) Any

Poll the device code endpoint for the access token.

Parameters:
device_code : str

Device code from get_device_code().

session : niquests.AsyncSession

HTTP session.

Returns:

Token response, or an error payload while authorisation is pending.

Return type:

Any

Raises:

OAuth2Error – If polling fails with a terminal error.

email : str

Email address.

async exchange_auth_for_access(auth_code: str, verifier: str, redirect_uri: str, session: AsyncSession) Any

Exchange the authorisation code for an access token.

Parameters:
auth_code : str

Authorisation code from the redirect.

verifier : str

PKCE code verifier.

redirect_uri : str

Redirect URI used in the authorisation request.

session : niquests.AsyncSession

HTTP session.

Returns:

Token response data from the authorisation server.

Return type:

Any

Raises:

OAuth2Error – If the token exchange fails.

static from_keyring(username: str) SavedToken | None

Create an instance using the Keyring.

Parameters:
username : str

Keyring username.

Returns:

The saved token if present, otherwise None.

Return type:

SavedToken | None

async get_device_code(session: AsyncSession) Any

Get the device code.

Parameters:
session : niquests.AsyncSession

HTTP session.

Returns:

Device authorisation response from the server.

Return type:

Any

Raises:

OAuth2Error – If the device code request fails.

is_access_token_valid() bool

Check if the access token is valid.

Returns:

True if the access token is still valid.

Return type:

bool

persist(username: str) None

Persist the token to the Keyring.

Parameters:
username : str

Keyring username.

async refresh(username: str, session: AsyncSession) None

Refresh the access token using the refresh token.

Parameters:
username : str

Keyring username.

session : niquests.AsyncSession

HTTP session.

Raises:

OAuth2Error – If the token refresh fails.

refresh_token : str

Refresh token.

registration : Registration

Registration.

tenant : str | None

Tenant ID, if applicable.

update(data: dict[str, Any]) None

Update the token.

Parameters:
data : dict[str, Any]

Token response data from the authorisation server.

mutt_oauth2.utils.get_localhost_redirect_uri() tuple[int, str]

Find an available port and return a localhost URI.

Returns:

The listening port and matching http://localhost:{port}/ URI.

Return type:

tuple[int, str]

mutt_oauth2.utils.log_oauth2_error(data: dict[str, Any]) None

Log OAuth2 error information.

Parameters:
data : dict[str, Any]

Token response containing potential error fields.

async mutt_oauth2.utils.try_auth(token: SavedToken, *, debug: bool = False) None

Try to authenticate using the passed-in token.

Parameters:
token : SavedToken

Token to test.

debug : bool

Enable debug output on protocol connections.

Raises:

RuntimeError – If any authentication test fails.

Indices and tables