OAuth2 authentication for Microsoft 365 services (IMAP/SMTP/Calendar/etc)
Project description
M365-Auth: OAuth2 Authentication for Microsoft 365
A Python-based OAuth2 authentication tool for Microsoft 365 services including IMAP, SMTP, and Calendar access. Works with any mail client or application that supports OAuth2 authentication.
Based on the original M365-IMAP by Gerrit Oomens, which itself was forked from ag91/M365-IMAP.
Features
- Universal OAuth2 support: Works with any M365 service (mail, calendar, etc.)
- Multiple profiles: Separate token management for different services
- Secure storage: Tokens stored in OS keychain (macOS Keychain, GNOME Keyring, Windows Credential Locker)
- Cross-platform: Linux, macOS, Windows
- Mail client support: mbsync, msmtp, OfflineIMAP, and any OAuth2-compatible client
- Python library: Import and use in your own scripts
- XDG compliant: Follows XDG Base Directory specification
Installation
Option 1: Using pipx (Recommended)
pipx installs Python CLI tools in isolated environments while making them globally available:
# Install pipx if you don't have it
brew install pipx # macOS
# or: python3 -m pip install --user pipx
# Ensure pipx is in PATH
pipx ensurepath
# Install M365-Auth
git clone https://github.com/YOUR-USERNAME/M365-Auth
cd M365-Auth
pipx install .
Option 2: System-wide installation
git clone https://github.com/YOUR-USERNAME/M365-Auth
cd M365-Auth
pip3 install --user .
# Or without --user for system-wide (may require sudo)
Option 3: Development installation
For development or if you want to modify the code:
git clone https://github.com/YOUR-USERNAME/M365-Auth
cd M365-Auth
pip install -e .
Note: With development installation in a virtual environment, you'll need to either activate the venv or use full paths (e.g., /path/to/venv/bin/refresh-token) in your mail client configurations.
Commands installed
All installation methods create three commands:
get-token- Interactive OAuth2 flow to obtain tokensrefresh-token- Refresh and print access tokensget-refresh-token- Get refresh token from keychain (for OfflineIMAP)
Quick Start
Step 1: Get a Client ID
To connect to Azure AD for authentication, you need a client ID and optionally a client secret (an "app registration" in Azure AD).
Understanding client secrets: Confusingly, the client secret doesn't actually need to be secret for mail clients and desktop applications. Public clients (like mail clients) can't keep secrets anyway - anyone can decompile the app and extract them. That's why many public clients use empty secrets or publicly-known ones.
Your options:
Option 1: Create your own app registration (recommended)
This gives you full control and ensures you have all the permissions you need:
- Go to Azure Portal → Azure Active Directory → App registrations
- Click "New registration"
- Name: Something like "My Mail Client" or "Personal M365 Tools"
- Supported account types: Choose based on your account type
- Redirect URI: Web →
https://localhost:7598
- After creation, copy the Application (client) ID
- Go to "API permissions" → "Add a permission" → "Microsoft Graph" → "Delegated permissions"
- Add the permissions you need (see API Permissions section below)
- Important: If using a work/school account, you may need admin consent - click "Grant admin consent" or ask your IT admin
Note: The redirect URI is where Azure AD sends the user after authentication. We run a local HTTPS server on port 7598 to catch this redirect.
Option 2: Use a public client ID
Some applications publish their client IDs in their source code, which you can use. For example:
- Thunderbird: Available in their source code
- Other mail clients: Check their public repositories or documentation
To find a public client ID:
- Look in the application's source code repository
- Search for files related to OAuth2 or Microsoft authentication
- Look for strings that match the format of Azure AD client IDs (typically 36-character GUIDs)
Pros of using public client IDs:
- No need to create your own app registration
- Often already approved by IT departments for common applications
- Works immediately for personal accounts
Cons:
- You don't control the permissions
- May not have calendar or other non-mail permissions
- Could theoretically be revoked by the original publisher
- You're using an ID intended for a different application
Whatever client ID you use, it needs to have been granted the appropriate permissions in your M365 tenant (for work/school accounts) or support the permissions you need (for personal accounts).
Step 2: Configure
On first run, get-token will automatically create a default configuration file at ~/.config/m365auth/config.py. You must edit this file to add your client ID:
# The config file will be created automatically on first run
# Edit it to add your client ID
nano ~/.config/m365auth/config.py
Set ClientId = "your-client-id-here" in the config file.
The default config includes two profiles:
- mail:
IMAP.AccessAsUser.All,SMTP.Send - calendar:
Calendars.ReadWrite
Step 3: Get Your Tokens
Understanding OAuth2 tokens: The OAuth2 flow gives you two types of tokens:
- Refresh token: Long-lived (months/years), used to get new access tokens. Stored securely in your OS keychain.
- Access token: Short-lived (~1 hour), used to actually access your mail/calendar. Never persisted to disk for security.
Mail clients need fresh access tokens frequently, so they call refresh-token which uses your stored refresh token to get a new access token each time.
Get your initial tokens:
# For mail clients (IMAP/SMTP)
get-token --profile mail
# For calendar access
get-token --profile calendar
# Over SSH with port forwarding
get-token --profile mail --server
What happens:
- The script opens your browser to Microsoft's login page
- You authenticate with your Microsoft account
- Microsoft redirects to
https://localhost:7598with an authorization code - The script exchanges this code for tokens using Microsoft's MSAL library
- The refresh token is stored in your OS keychain (encrypted, secure)
- The access token is printed (but not saved - you don't need it,
refresh-tokenwill generate new ones)
Security note: The refresh token allows access to your full mailbox/calendar (depending on permissions), so it's stored in your system's secure keychain, not a plain file. Even if someone gets filesystem access, they can't use your credentials without also accessing your keychain.
Usage Examples
mbsync (isync)
IMAPAccount account
Host outlook.office365.com
User your.email@example.com
PassCmd "refresh-token --profile mail"
AuthMechs XOAUTH2
SSLType IMAPS
msmtp
account myaccount
host smtp.office365.com
port 587
from your.email@example.com
user your.email@example.com
auth xoauth2
passwordeval "refresh-token --profile mail"
tls on
tls_starttls on
OfflineIMAP
[Repository Remote]
type = IMAP
remotehost = outlook.office365.com
remoteuser = your.email@example.com
auth_mechanisms = XOAUTH2
oauth2_request_url = https://login.microsoftonline.com/common/oauth2/v2.0/token
oauth2_client_id = <your client ID>
oauth2_client_secret =
oauth2_refresh_token_eval = get-refresh-token --profile mail
# Optional: filter non-mail folders
folderfilter = lambda folder: not folder.startswith('Calendar') and not folder.startswith('Contacts')
Python Scripts
from m365auth import get_access_token
# Get access token for mail
token = get_access_token('mail')
# Get access token for calendar
token = get_access_token('calendar')
# Use with requests
headers = {'Authorization': f'Bearer {token}'}
response = requests.get('https://graph.microsoft.com/v1.0/me/calendar', headers=headers)
API Permissions
Common Microsoft Graph delegated permissions:
Mail:
https://outlook.office.com/IMAP.AccessAsUser.All- IMAP accesshttps://outlook.office.com/SMTP.Send- SMTP sending
Calendar:
https://graph.microsoft.com/Calendars.ReadWrite- Calendar read/write
Other:
https://graph.microsoft.com/Mail.ReadWrite- Mail via Graph APIhttps://graph.microsoft.com/Calendars.Read- Calendar read-only
Note: Azure AD doesn't allow mixing outlook.office.com and graph.microsoft.com scopes in one token. Use separate profiles for different resource types.
Configuration
Directory Structure
- Config:
~/.config/m365auth/config.py(Linux) or platform equivalent - Cache:
~/.cache/m365auth/(Linux) or platform equivalent- Auto-generated SSL certificates for OAuth callback server
- Keychain: OS-native secure storage
- Refresh tokens stored as
m365auth-{profile}
- Refresh tokens stored as
Custom Profiles
Edit ~/.config/m365auth/config.py:
Profiles = {
'mail': {
'scopes': [
'https://outlook.office.com/IMAP.AccessAsUser.All',
'https://outlook.office.com/SMTP.Send'
]
},
'calendar': {
'scopes': [
'https://graph.microsoft.com/Calendars.ReadWrite'
]
},
'custom': {
'scopes': [
# Your custom scopes here
]
}
}
Tenant-Specific Configuration
For single-tenant (work/school) accounts:
Authority = "https://login.microsoftonline.com/YOUR-TENANT-ID/"
Security
- Refresh tokens: Stored encrypted in OS keychain
- Access tokens: Never persisted to disk - only printed to stdout or held in memory
- File fallback: For headless environments, tokens stored in
~/.cache/m365auth/with mode 600 - Benefit: Even with filesystem access, attackers need keychain access to get credentials
SSH / Headless Usage
When running over SSH, the browser can't reach localhost:7598 on the remote machine. Two options:
Option 1: SSH Port Forwarding (recommended)
SSH port forwarding creates a tunnel so that localhost:7598 on your local machine connects to localhost:7598 on the remote machine.
Step-by-step:
# Step 1: On your local machine, connect with port forwarding
ssh -L 7598:localhost:7598 user@remote-host
# Step 2: On the remote machine (in the SSH session)
get-token --profile mail --server
# Step 3: The script will print a URL and open it in your local browser
# (The --server flag tells the script to start the HTTPS server even over SSH)
# Step 4: Authenticate in your browser
# The redirect to localhost:7598 will go through the SSH tunnel to the remote machine
# Step 5: Done! The token is stored in the remote machine's keychain
How it works:
-L 7598:localhost:7598creates a tunnel from local port 7598 to remote port 7598- When Microsoft redirects to
https://localhost:7598, your browser connects to your local port 7598 - SSH forwards that connection through the tunnel to the remote machine
- The remote machine's OAuth server receives the authorization code
Option 2: Manual URL Entry (for existing SSH sessions)
If you're already connected via SSH without port forwarding:
# On the remote machine
get-token --profile mail
# Script detects SSH (no --server flag) and skips starting the server
# It prints the OAuth URL and waits
# Open the URL in your local browser and authenticate
# Microsoft will redirect to https://localhost:7598/?code=...
# This will fail to load (that's expected!)
# Copy the entire error page URL from your browser
# Paste it back into the SSH terminal
# Done! The script extracts the code and completes authentication
Note: For headless servers with no browser access at all, you need to open the URL on any machine with a browser, then paste the redirect URL back to the server.
Troubleshooting
"No refresh token found"
# Run get-token first to authenticate
get-token --profile mail
"Cannot store in keychain"
- File fallback is automatic
- Tokens stored in
~/.cache/m365auth/refresh_token_{profile} - On headless Linux, consider installing
gnome-keyring
"Permission denied" errors
- Your Azure app needs the required API permissions
- Contact your IT admin to grant consent
SSL certificate warnings
- Normal for self-signed certificates on localhost
- Safe to click through during OAuth flow
Contributing
Contributions welcome! This is a fork of the original M365-IMAP project, extended to support multiple services beyond just IMAP.
License
MIT License - see original M365-IMAP repository.
Credits
- Original implementation: Gerrit Oomens
- Earlier fork: ag91/M365-IMAP
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file m365auth-0.2.2.tar.gz.
File metadata
- Download URL: m365auth-0.2.2.tar.gz
- Upload date:
- Size: 12.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13aaa72718975997656e0265012057289f0632aaab723bef3b443ef1307a0db4
|
|
| MD5 |
d5f074ab5f552ccac515e27e89035f61
|
|
| BLAKE2b-256 |
e94c3031c5a1f15053090f7e41c4f7d5e8a05909a305037a3a1d2a409d3292b5
|
File details
Details for the file m365auth-0.2.2-py3-none-any.whl.
File metadata
- Download URL: m365auth-0.2.2-py3-none-any.whl
- Upload date:
- Size: 12.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbd5d5511c3d7b010bba2e0579b15d540fc49a1fa45bce160f50b241a36e5651
|
|
| MD5 |
bbb8efcb645d0a64b8c428fc3acf26da
|
|
| BLAKE2b-256 |
1681c17e71edc60b4bf55da1f8b85eb168a1f57617b7dc386aa1434a47700c1a
|