Claude Magic Link
Auto-open Claude.ai magic links from your inbox.
The problem: Claude.ai uses passwordless "magic link" authentication. Every login sends an email, you open it, find the link, click it, confirm. With multiple accounts this gets tedious fast.
The solution: claude-magic-link watches your IMAP mailbox via IDLE (push-based, near-instant) and opens the magic link in the right browser automatically. You just confirm in the browser.
Features
- IMAP IDLE -- push-based, opens links within seconds of arrival
- Multi-Account -- watch multiple mailboxes, each with its own browser and, if needed, its own IMAP server
- Security-First -- 6-layer verification (sender, HTTPS, host, path, recipient match, DMARC/DKIM)
- Zero Dependencies -- pure Python standard library (3.14+)
- Read-Only -- never modifies, moves, or deletes your mail
- Platform-Aware -- macOS (
open -b) and Linux (xdg-open) support - Desktop Notifications -- macOS and Linux (notify-send)
- Single Instance -- flock-based lock prevents duplicate opens
- Daemon Mode -- run continuously with auto-reconnect and exponential backoff
Quick Start
Prerequisites
- Python 3.14+ (for the IMAP IDLE API)
- An IMAP mailbox that receives Claude.ai login emails
- IMAP server with IDLE support (most providers have this)
Install
# From PyPI
pipx install claude-magic-link
# Or from source
git clone https://github.com/fidpa/claude-magic-link.git
cd claude-magic-link
pip install .
Configure
# Copy the example config to the platform-specific location
# macOS:
mkdir -p ~/Library/Application\ Support/claude-magic-link
cp config.example.toml ~/Library/Application\ Support/claude-magic-link/config.toml
# Linux:
mkdir -p ~/.config/claude-magic-link
cp config.example.toml ~/.config/claude-magic-link/config.toml
# Edit the config
$EDITOR ~/Library/Application\ Support/claude-magic-link/config.toml # macOS
$EDITOR ~/.config/claude-magic-link/config.toml # Linux
Set your IMAP password as an environment variable:
export CLAUDE_MAGIC_LINK_PASSWORD="your-imap-password"
Run
# Test run (logs what would happen, opens nothing)
claude-magic-link --once --dry-run
# Single scan
claude-magic-link --once
# Continuous watching (daemon mode)
claude-magic-link
# With custom config location
claude-magic-link --config /path/to/config.toml
Configuration
See config.example.toml for the full reference. Key sections:
IMAP Server
[imap]
host = "imap.example.com"
port = 993
[imap] is the default server; an account can override it with its own
host / port to watch mailboxes at different providers from one instance.
Accounts
[[accounts]]
email = "you@example.com"
password_env = "CLAUDE_MAGIC_LINK_PASSWORD"
browser = "default"
[[accounts]]
email = "work@example.com"
password_cmd = "security find-generic-password -s claude-magic-link -w"
browser = "com.google.Chrome"
host = "imap.workmail.example" # optional per-account override
The password comes from exactly one of three sources per account, checked
in this order: password (inline), password_cmd (stdout of a command --
e.g. macOS Keychain or pass -- run once at startup without a shell; wrap
in sh -c '...' for pipes), password_env (environment variable).
Each account maps an email address to a browser:
| Value | Platform | Effect |
|---|---|---|
"default" |
Both | System default browser |
"com.google.Chrome" |
macOS | Chrome via bundle ID |
"com.microsoft.edgemac" |
macOS | Edge via bundle ID |
"org.mozilla.firefox" |
macOS | Firefox via bundle ID |
"google-chrome" |
Linux | Chrome via command |
"firefox" |
Linux | Firefox via command |
DMARC/DKIM Verification
For maximum security, configure your mail provider's authserv-id:
[security]
trusted_authserv_suffix = "mailhosting.your-provider.com"
This verifies that incoming Anthropic mails actually passed DMARC/DKIM at your provider's mail server, preventing spoofed sender addresses. Without this, the tool still checks sender domain, link host/path, and recipient matching.
To find your provider's authserv-id, check the Authentication-Results header of any email in your inbox. The first field before the semicolon is the authserv-id.
How It Works
┌─────────────┐ IMAP IDLE ┌──────────────────┐
│ Mail Server │ ──── push ─────> │ claude-magic-link │
│ (Dovecot, │ │ │
│ Exchange, │ │ 1. Extract link │
│ Gmail...) │ │ 2. Verify sender │
│ │ │ 3. Check DMARC │
└─────────────┘ │ 4. Match To addr │
│ 5. Dedup check │
│ 6. Open browser │
└───────┬──────────┘
│
open -b / xdg-open
│
▼
┌──────────────┐
│ Browser │
│ (you confirm) │
└──────────────┘
Running as a Service
macOS (LaunchAgent)
LaunchAgents do not inherit your shell environment. The cleanest setup is
password_cmd in the config (e.g. the macOS Keychain, as shown above) --
then the agent starts the binary directly and no password ever touches the
plist or the environment. If you rely on password_env instead, point
ProgramArguments at a small wrapper script that exports the variable(s)
first (e.g. set -a; source ~/.config/claude-magic-link/env; set +a).
Create ~/Library/LaunchAgents/com.user.claude-magic-link.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.claude-magic-link</string>
<key>ProgramArguments</key>
<array>
<string>/Users/yourname/.local/bin/claude-magic-link</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>ThrottleInterval</key>
<integer>60</integer>
<key>StandardOutPath</key>
<string>/tmp/claude-magic-link.stdout.log</string>
<key>StandardErrorPath</key>
<string>/tmp/claude-magic-link.stderr.log</string>
</dict>
</plist>
# macOS 13+:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.claude-magic-link.plist
# Older versions:
launchctl load ~/Library/LaunchAgents/com.user.claude-magic-link.plist
Linux (systemd)
Create ~/.config/systemd/user/claude-magic-link.service:
[Unit]
Description=Claude Magic Link Watcher
After=network-online.target
[Service]
ExecStart=/path/to/claude-magic-link
# With password_cmd in the config (e.g. `pass show claude-magic-link`),
# no Environment= line is needed. For password_env, prefer a credential
# store or EnvironmentFile= over an inline value:
# Environment=CLAUDE_MAGIC_LINK_PASSWORD=your-password
Restart=always
RestartSec=10
[Install]
WantedBy=default.target
systemctl --user enable --now claude-magic-link
CLI Reference
usage: claude-magic-link [-h] [--config PATH] [--once] [--dry-run]
[--max-age N] [--verbose] [--version]
options:
--config, -c PATH Path to config.toml
--once Single scan instead of continuous watching
--dry-run Log what would happen without opening anything
--max-age N Only process mails younger than N minutes
--verbose, -v Enable debug logging
--version Show version and exit
Security
See SECURITY.md for the full security model. In short, every mail must pass 6 independent checks before a link is opened:
- Sender domain is
mail.anthropic.com - Link uses HTTPS
- Link host is exactly
claude.ai - Link path is
/magic-link - To-header matches the address encoded in the link
- DMARC/DKIM passes at your provider (when configured)
Design Decisions
Why IMAP IDLE instead of EWS/Graph API? IMAP IDLE is provider-agnostic -- it works with Gmail, Dovecot, Exchange, Fastmail, and any standards-compliant server. EWS is Exchange-only and deprecated by Microsoft. Graph API requires Azure AD app registration. IMAP IDLE is the simplest path that works everywhere.
Why Python 3.14? The IMAP IDLE API (IMAP4.idle()) was added in Python 3.14. It handles the protocol correctly (DONE, tags, unsolicited responses) without third-party libraries. This keeps the project dependency-free.
Why no auto-confirm? Auto-confirming would make this tool a complete authentication bypass, which is a security risk. The manual confirmation step ensures a human is present.
Why EXAMINE instead of SELECT? The tool uses select(readonly=True), which sends the IMAP EXAMINE command. This guarantees the mailbox is never modified -- no flags are set, no messages moved or deleted.
FAQ
Why not use the API instead? Claude.ai uses magic-link authentication for the web interface, not the API. The API uses API keys. This tool is for the web/desktop app login flow.
Does it work with Gmail? Yes, if you enable IMAP access and use an app-specific password. Gmail supports IMAP IDLE.
Does it work with OAuth/XOAUTH2? Not yet. The current version uses plain IMAP LOGIN. OAuth support would be a welcome contribution.
Why not just stay logged in? Sessions expire, and with several accounts spread across browsers, profiles, and devices you end up logging in regularly anyway. This tool removes the inbox round-trip from that flow -- only the deliberate confirmation click remains.
Does it work on Windows?
Not yet -- the single-instance lock uses fcntl and browser launching uses open/xdg-open. Contributions welcome.
Development
git clone https://github.com/fidpa/claude-magic-link.git
cd claude-magic-link
python3.14 -m venv .venv
.venv/bin/pip install pytest ruff
.venv/bin/pytest # run the test suite
.venv/bin/ruff check . # lint
The test suite covers the security checks (including the attack cases from SECURITY.md), the IMAP helpers, config loading, and state persistence -- all without a network connection.
License
(c) 2026 Marc Allgeier
Author
Marc Allgeier (@fidpa)
Why I Built This: I manage multiple Claude.ai accounts for work and personal use. The login flow -- open email, find the link, click it, wait for the browser, confirm -- is a minor friction that adds up fast. This tool eliminates everything except the final confirmation click, which intentionally remains as a security boundary.
See Also
- cc-telegram-bot -- Security-hardened Telegram bot for remote Claude Code access (24 security layers)
- lydia-bible-bot -- AI Bible study assistant for Telegram groups
- ubuntu-server-security -- Server hardening (14 components, CIS Benchmark)
- bash-production-toolkit -- Production-ready Bash libraries
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 claude_magic_link-0.3.0.tar.gz.
File metadata
- Download URL: claude_magic_link-0.3.0.tar.gz
- Upload date:
- Size: 26.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42f9cf448c0e15c9d1db0c3863c9ec62be58241b3f37b090964fb885cacd6f16
|
|
| MD5 |
71873e0da18a4a4b6ba89d3903d1fd54
|
|
| BLAKE2b-256 |
79048385ed84228a930c78a2d43847225852aa664df9baa7523cab8f6fbc3137
|
Provenance
The following attestation bundles were made for claude_magic_link-0.3.0.tar.gz:
Publisher:
release.yml on fidpa/claude-magic-link
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_magic_link-0.3.0.tar.gz -
Subject digest:
42f9cf448c0e15c9d1db0c3863c9ec62be58241b3f37b090964fb885cacd6f16 - Sigstore transparency entry: 2635547596
- Sigstore integration time:
-
Permalink:
fidpa/claude-magic-link@8c0f5c3aa593b67387beab1e42c9fb51e9a7f0cd -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/fidpa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8c0f5c3aa593b67387beab1e42c9fb51e9a7f0cd -
Trigger Event:
release
-
Statement type:
File details
Details for the file claude_magic_link-0.3.0-py3-none-any.whl.
File metadata
- Download URL: claude_magic_link-0.3.0-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
027857179ff679549d65d8eaa3dade88d9886b5ebc281275696c33e0293050c5
|
|
| MD5 |
10c680597de0209608da18f0c2818267
|
|
| BLAKE2b-256 |
867cfc8620a521ada8d6241e6435fc802fba1ba529bd42b6e34c66f3207d99dc
|
Provenance
The following attestation bundles were made for claude_magic_link-0.3.0-py3-none-any.whl:
Publisher:
release.yml on fidpa/claude-magic-link
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_magic_link-0.3.0-py3-none-any.whl -
Subject digest:
027857179ff679549d65d8eaa3dade88d9886b5ebc281275696c33e0293050c5 - Sigstore transparency entry: 2635547625
- Sigstore integration time:
-
Permalink:
fidpa/claude-magic-link@8c0f5c3aa593b67387beab1e42c9fb51e9a7f0cd -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/fidpa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8c0f5c3aa593b67387beab1e42c9fb51e9a7f0cd -
Trigger Event:
release
-
Statement type: