-
Notifications
You must be signed in to change notification settings - Fork 16
Issue 45 IMAP Mailboxes Reported As Not Authenticated
Open a company under Settings β Companies with a Basic IMAP mailbox routed to it, and the How email reaches this company panel said:
π Dedicated mailbox (not authenticated)
β οΈ A mailbox on a route above is not authenticated, so mail won't flow until it's reconnected in Settings.
None of that was true. The mailbox was connected, was being checked, and was collecting mail. The warning told people to go and reconnect something that was working, and reconnecting it could not have changed the answer.
Reported by mbsouth on issue #45 β the issue that added Basic IMAP support in the first place. That is a fitting place for it to land, because this bug is the incomplete half of that very feature: four screens needed teaching about IMAP mailboxes and exactly one of them ever got it.
Fixed in a1f64426, released as update #1172.
The companion bug reported alongside this one is The Verify button only ever worked for Microsoft. The general guides are Basic IMAP mailboxes and Multi-tenancy: email routing.
Four separate queries each carried their own copy of the same expression:
CASE WHEN token_data IS NOT NULL AND token_data != '' THEN 1 ELSE 0 END AS is_authenticatedEvery copy was written before Basic IMAP mailboxes existed, and every copy was correct on the day it was written.
token_data holds the OAuth token. Microsoft and Google mailboxes have one. A Basic IMAP mailbox authenticates with a stored username and password and has no token at all β no code path ever writes that column for one, and the only mention of it in save_mailbox.php sets it to NULL.
So for an IMAP mailbox that expression does not return the wrong answer occasionally. It returns 0 always, on every install, no matter how correct the credentials are or how much mail the mailbox is collecting.
| Screen | Verdict on a working IMAP mailbox | |
|---|---|---|
| Mailboxes list | Connected | β correct |
| Company routing panel | not authenticated, plus "mail won't flow" | β |
| Routing tester | not authenticated | β |
| Topology view | not authenticated | β |
The mailbox list was right for a reason worth noticing: when Basic IMAP was added, get_mailboxes.php was taught about it separately, in PHP, months later β
if ($mailbox['provider'] === 'imap') {
// Basic IMAP has no OAuth sign-in - it connects straight to the target
// inbox with the stored password. "Connected" once a password is stored.
$mailbox['is_authenticated'] = !empty($mailbox['imap_password_set']);
}One screen was updated. The other three were never found, because there was nothing to find them by β four unrelated files, each with its own copy, none of them referring to any shared thing.
The general lesson: a definition that is copied is a definition that will drift, and it drifts silently. Nothing failed, nothing errored, no test went red. Three screens simply went on answering a question using a rule that had stopped being true, and the only visible symptom was that they disagreed with a fourth screen that nobody was comparing them against.
The clearest reproduction had two pinned mailboxes on one company:
| Mailbox | Provider | Panel said |
|---|---|---|
support@β¦ |
Microsoft | (nothing β fine) |
edmozley@β¦ |
Basic IMAP | (not authenticated) |
Same screen, same company, both working. One line of SQL between them.
One definition, in one file. mailboxAuthenticatedSql() now lives in includes/mailbox_auth.php and is the only place that decides what "authenticated" means:
function mailboxAuthenticatedSql(string $prefix = ''): string {
return "CASE WHEN ({$prefix}token_data IS NOT NULL AND {$prefix}token_data <> '')"
. " OR ({$prefix}provider = 'imap' AND {$prefix}imap_password IS NOT NULL AND {$prefix}imap_password <> '')"
. " THEN 1 ELSE 0 END";
}OAuth providers prove it with a token; Basic IMAP proves it with a password. Either one means the mailbox can log in.
Patching the three wrong copies would have fixed the reported symptom and left the actual defect in place, ready to reappear the next time a provider is added. The copies were the bug.
No schema change. provider and imap_password are already in db_verify's target_mailboxes schema and in database/freeitsm.sql, and are already selected by get_mailboxes.php and get_topology.php β so this adds no requirement the mailbox screens did not already have.
| File | Role |
|---|---|
includes/mailbox_auth.php |
New. mailboxAuthenticatedSql() β the single definition, with the history of why it exists written above it |
| File | Screen |
|---|---|
api/system/get_tenant_email_routing.php |
How email reaches this company β the panel that carried the false "mail won't flow" warning |
api/system/email_routing_test.php |
The routing dry-run tester |
api/system/get_topology.php |
System β Topology |
| File | Why |
|---|---|
api/tickets/get_mailboxes.php |
Handles IMAP in PHP and computes a richer auth_status (ok, mismatch, unverified, β¦) that the SQL flag alone cannot express |
api/tickets/get_sendable_mailboxes.php |
Asks a different question β can this mailbox send β and already allowed for IMAP |
A differential run against the three real mailboxes on a live install, old expression and new, side by side in one query:
| Mailbox | Provider | Old | New |
|---|---|---|---|
| ITSM Support | microsoft | 1 | 1 |
| freeitsm.com inbox | 1 | 1 | |
| Zoho Mail | imap | 0 | 1 |
Exactly one row changes, and it is the intended one. The two unchanged rows are the control: an expression that made everything authenticated would have fixed the symptom and broken the feature, and would have looked identical if only the IMAP row had been checked.
Then live through each of the three endpoints:
| Endpoint | Result |
|---|---|
get_tenant_email_routing.php |
Both pinned mailboxes "authenticated": true, and "warnings": [] β the yellow warning is gone |
email_routing_test.php |
IMAP mailbox "authenticated": true
|
get_topology.php |
IMAP mailbox "is_authenticated": true, Microsoft and Google unchanged |
What is not proven. Every mailbox on the test install holds a credential, so the negative case β an IMAP mailbox saved with no password, which should read as unauthenticated β was reasoned from the SQL rather than reproduced.
- If a company's routing panel warned that mail would not flow from your IMAP mailbox, ignore it β and no, you did not need to reconnect anything. Mail was flowing.
- The Mailboxes list was the screen telling the truth, and it still is. If it says Connected and the last-checked time is recent, the mailbox is working.
- A genuinely unauthenticated IMAP mailbox is one with no stored password, and it will now be reported as such β including on these three screens.
- Bugs resolved β the index of write-ups like this one
- The Verify button only ever worked for Microsoft β the other half of the same report
- Verifying a mail folder β Developer Guide β how Graph, IMAP and Gmail are each checked, with the code
- Basic IMAP mailboxes Β· Multi-tenancy: email routing
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)