Single Sign-On (SSO)
When Opterius Mail is installed alongside Opterius Panel, users can open webmail directly from the Panel without re-entering their email password. This is Single Sign-On (SSO) — the Panel vouches for the user's identity and Opterius Mail trusts that voucher to open an IMAP session via Dovecot's master-user mechanism.
How It Works
1. User clicks "Open Webmail" for an account in Opterius Panel
↓
2. Panel POSTs server-to-server to:
{webmail_url}/sso/issue
Body: { email, timestamp, signature }
signature = HMAC-SHA256(secret, "{email}:{timestamp}")
↓
3. Opterius Mail validates the HMAC + timestamp window (±60 s),
generates a one-time random token (cached for 120 s),
returns: { token, url: "{webmail_url}/sso/login?token=..." }
↓
4. Panel redirects the user's browser to that URL
↓
5. Opterius Mail receives the token at /sso/login:
a. Looks up the email in cache (token is single-use, immediately consumed)
b. Opens IMAP using the Dovecot master-user format:
LOGIN "user@domain*vmail-master" "{IMAP_MASTER_PASS}"
c. On success, flushes any prior session and establishes a fresh one
↓
6. User lands in their inbox with no login prompt
Security Properties
| Property | Detail |
|---|---|
| Server-to-server signed request | Panel proves it knows the shared secret before any token exists. The user's browser never sees the secret. |
| Token TTL | 120 seconds. The single-use token cached on the webmail expires automatically. |
| Single-use token | The cache key is forgotten the moment the token is consumed at /sso/login. Replaying the URL returns "SSO token is invalid or has expired." |
| Timestamp window | The HMAC request must be within ±60 seconds of the webmail server's clock. Larger drift means NTP is broken on one side. |
| Session isolation | Before establishing the new SSO session, the webmail calls session()->flush() and session()->regenerate(true) to destroy any prior user's data. This prevents cross-account leakage when the same browser is used to switch between mailboxes. |
| No password transmission | The user's mail password is never sent over the wire. Authentication happens via Dovecot's master-user mechanism using IMAP_MASTER_PASS. |
Required configuration
SSO needs both shared secrets and Dovecot's master-user mechanism configured.
On the webmail side (/opt/opterius-mail/.env)
PANEL_SSO_ENABLED=true
PANEL_SSO_SECRET=<32+ char shared secret>
IMAP_MASTER_USER=vmail-master
IMAP_MASTER_PASS=<password from /etc/dovecot/master-users>
On the panel side (/opt/opterius/.env)
OPTERIUS_WEBMAIL_URL=http://your-server:8090
OPTERIUS_WEBMAIL_SSO_SECRET=<same value as PANEL_SSO_SECRET above>
The two secrets must be byte-identical. A single character difference causes every SSO attempt to fail with "Invalid signature."
On Dovecot (/etc/dovecot/conf.d/auth-master.conf.ext)
# Required: tells Dovecot to split "user@domain*master-user" into target + master.
# Without this, the entire string is treated as a literal username and IMAP
# login fails with AUTHENTICATIONFAILED.
auth_master_user_separator = *
passdb {
driver = passwd-file
master = yes
args = /etc/dovecot/master-users
result_success = continue
}
The master user file:
echo "vmail-master:{plain}<random-32-hex-string>" > /etc/dovecot/master-users
chown dovecot:dovecot /etc/dovecot/master-users
chmod 640 /etc/dovecot/master-users
systemctl restart dovecot
Automatic configuration (Opterius Panel installer)
When Opterius Mail is installed via the bundled installer or the retrofit script (_docs/install-opterius-mail.sh), all of the above is configured automatically:
- Generates a 32-character hex
PANEL_SSO_SECRET. - Writes it to both panel and mail
.envfiles (matching values). - Creates the Dovecot master user with a random password.
- Writes
auth_master_user_separator = *and the masterpassdbblock. - Restarts Dovecot.
The "Open Webmail" button works immediately after install — no manual steps.
Manual SSO setup
Only needed when reinstalling mail standalone or on a non-standard deployment.
Step 1 — Generate a shared secret
openssl rand -hex 32
Step 2 — Write the secret on both sides
# Webmail
echo "PANEL_SSO_SECRET=<paste>" >> /opt/opterius-mail/.env
# Panel
echo "OPTERIUS_WEBMAIL_SSO_SECRET=<paste>" >> /opt/opterius/.env
echo "OPTERIUS_WEBMAIL_URL=http://<server>:8090" >> /opt/opterius/.env
Step 3 — Set up the Dovecot master user
DOVECOT_MASTER_PASS=$(openssl rand -hex 16)
echo "vmail-master:{plain}${DOVECOT_MASTER_PASS}" > /etc/dovecot/master-users
chown dovecot:dovecot /etc/dovecot/master-users
chmod 640 /etc/dovecot/master-users
cat > /etc/dovecot/conf.d/auth-master.conf.ext <<'EOF'
auth_master_user_separator = *
passdb {
driver = passwd-file
master = yes
args = /etc/dovecot/master-users
result_success = continue
}
EOF
# Make sure 10-auth.conf includes it
grep -q "auth-master.conf.ext" /etc/dovecot/conf.d/10-auth.conf || \
echo '!include auth-master.conf.ext' >> /etc/dovecot/conf.d/10-auth.conf
systemctl restart dovecot
Step 4 — Wire the master credentials into the webmail
# /opt/opterius-mail/.env
IMAP_MASTER_USER=vmail-master
IMAP_MASTER_PASS=<the DOVECOT_MASTER_PASS from above>
Step 5 — Clear caches
cd /opt/opterius-mail && php artisan config:clear
cd /opt/opterius && php artisan config:clear
Testing SSO
# 1. End-to-end via the panel
# Log into the panel, click Email → Open Webmail next to any account.
# You should land in the inbox.
# 2. Verify Dovecot master auth at the protocol level
{
printf 'a1 LOGIN "user@example.com*vmail-master" "<master-pass>"\r\n'
sleep 1
printf 'a2 LOGOUT\r\n'
} | nc -q2 127.0.0.1 143
# Expected: a1 OK Logged in
Sending mail from an SSO session
The user's real mail password is not in the SSO session — only the master password is. So when the user composes and sends a message, the webmail submits via SMTP using the same master-user format:
SMTP AUTH LOGIN
username: user@example.com*vmail-master
password: <master-pass>
Postfix delegates this to Dovecot SASL, which understands the master separator (same auth_master_user_separator = * setting). If SMTP fails with 535 authentication failed for SSO sessions only, that's the missing setting.
Sent folder
Opterius Mail's ComposeController::send() automatically APPENDs the sent message to the user's Sent folder via IMAP after successful SMTP delivery. The folder is auto-created on first use, with \Sent SPECIAL-USE attribute when the server supports it. Failure to APPEND is silent (delivery has already happened) and logged at warning level.
Troubleshooting
"could not authenticate with mail server" on the SSO login page
The SSO request was valid but Dovecot rejected the master-user IMAP login. Most common: auth_master_user_separator = * is missing. Verify with:
doveconf -n | grep separator
# Should print: auth_master_user_separator = *
If empty, add the setting (see Step 3 above) and restart Dovecot.
A real-IMAP-login test confirms it:
{ printf 'a1 LOGIN "user@example.com*vmail-master" "<master-pass>"\r\n'; sleep 1; printf 'a2 LOGOUT\r\n'; } | nc -q2 127.0.0.1 143
a1 NO [AUTHENTICATIONFAILED] means the setting isn't applied or the master pass doesn't match /etc/dovecot/master-users.
"Invalid signature" (403) on /sso/issue
The HMAC didn't verify. Either the secrets don't match, or the timestamp drifted. Check both .env files contain the same secret string (no trailing whitespace), and that NTP is running on both ends.
timedatectl status # both servers
diff <(grep -oP 'PANEL_SSO_SECRET=\K.*' /opt/opterius-mail/.env) \
<(grep -oP 'OPTERIUS_WEBMAIL_SSO_SECRET=\K.*' /opt/opterius/.env)
The diff should be empty.
"SSO token is invalid or has expired"
The token was already used (single-use), or more than 120 s passed between issuance and the user clicking through. Generate a fresh link.
Wrong inbox loads after switching accounts
Pre-2026-04-26 versions of Opterius Mail had a session-leakage bug where stale data from the previous SSO login could surface the wrong mailbox. Fixed by session()->flush() + session()->regenerate(true) before establishing the new session. If you're seeing this, update Opterius Mail (cd /opt/opterius-mail && git pull && php artisan config:clear).
SMTP 535 authentication failed only on SSO sessions
Postfix's SASL backend (Dovecot) needs the same auth_master_user_separator = * setting that IMAP uses. This is the same fix as the IMAP issue above; both auth paths share the Dovecot config.
Disabling SSO
Set PANEL_SSO_ENABLED=false in /opt/opterius-mail/.env and clear cache:
sed -i 's/^PANEL_SSO_ENABLED=.*/PANEL_SSO_ENABLED=false/' /opt/opterius-mail/.env
cd /opt/opterius-mail && php artisan config:clear
The /sso/issue and /sso/login routes will return 403. Users will see the standard login page when they click "Open Webmail."