oliverkroener / ok-exchange365-mailer
A TYPO3 extension for sending emails using Exchange 365 and Microsoft Graph API
Package info
github.com/oliverkroener/ok_exchange365_mailer
Type:typo3-cms-extension
pkg:composer/oliverkroener/ok-exchange365-mailer
Requires
- php: >=8.1.0 <8.6.0
- microsoft/microsoft-graph: ^2
- oliverkroener/ok-typo3-helper: ^3
- typo3/cms-core: ^12.4 || ^13 || ^14
Requires (Dev)
- phpunit/phpunit: ^10.5 || ^11.5
- typo3/testing-framework: ^8.3.3 || ^9.1
Suggests
- typo3/cms-lowlevel: Blinds the Exchange 365 credentials in the backend Configuration module.
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 4.4.0
- 4.3.0
- 4.2.1
- 4.2.0
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 3.2.0
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.2.0
- 2.1.1
- 2.1.0
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.1.0
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- dev-feature-typo3-9
- dev-feature-typo3-10
- dev-feature-typo3-11
- dev-release/4.3.0
- dev-codex/find-and-fix-a-bug
This package is auto-updated.
Last update: 2026-10-04 20:01:56 UTC
README
A TYPO3 extension for sending emails via Microsoft Exchange 365 using the MS Graph API instead of SMTP. Uses OAuth 2.0 client credentials flow for secure, token-based authentication.
Features
- Send emails through Microsoft Graph API — no SMTP required
- OAuth 2.0 client credentials flow for server-to-server authentication
- Supports backend (environment variables / TYPO3 settings) and frontend configuration, the latter via a TYPO3 13/14 site set or the classic TypoScript static template
- Compatible with Powermail, TYPO3 Form Framework, and other form extensions
- Optional saving of sent emails to the sender's "Sent Items" folder
- Automatic credential blinding in TYPO3's configuration module, for
TYPO3_CONF_VARSand site settings - Credentials can be read from the environment in TypoScript with
:= getEnv(...)— no secrets in templates - One OAuth token per request, short timeouts (10 s connect / 30 s total) and no duplicate sends on a stalled connection
- Tested on every supported TYPO3 version with real Microsoft Graph sends (see Testing)
- Works with shared mailboxes and Application Access Policies
- Send As / Send On Behalf — optional
graphSenderUserIddecouples the Graph mailbox used for/users/{id}/sendMailfrom the visibleFromheader, so a configured mailbox can send on behalf of another
Requirements
- TYPO3: 12.4 LTS, 13.4 LTS, or 14.x
- PHP: 8.1 – 8.5
- Dependencies:
microsoft/microsoft-graph^2oliverkroener/ok-typo3-helper^3
Compatibility
This extension is maintained as one release line per TYPO3 major. Composer picks the right one automatically, but if you pin a version yourself, use this table:
| TYPO3 | Extension | Branch | PHP | Graph SDK | Status |
|---|---|---|---|---|---|
| 14.x | 4.4.x | main |
8.2 – 8.5 | ^2 | Active |
| 13.4 LTS | 4.4.x | main |
8.2 – 8.5 | ^2 | Active |
| 12.4 LTS | 4.4.x | main |
8.1 – 8.4 | ^2 | Active |
| 11.5 ELTS | 3.2.x | feature-typo3-11 |
7.4 – 8.3 | ^2 | Maintenance |
| 10.4 ELTS | 2.2.x | feature-typo3-10 |
7.2 – 7.4 | none (Guzzle) | Maintenance |
| 9.5 | 1.1.x | feature-typo3-9 |
7.2 – 7.4 | none (Guzzle) | Maintenance |
The TYPO3 9.5 line predates Symfony Mailer and uses SwiftMailer. Every line, 9.5 included, is covered by the test matrix (see Testing).
Installation
Install via Composer (recommended):
composer require oliverkroener/ok-exchange365-mailer
Local path repository
If using a local path (e.g., in a monorepo), add the repository to your root composer.json:
{
"repositories": [
{
"type": "path",
"url": "packages/ok_exchange365_mailer"
}
]
}
Then install:
composer require oliverkroener/ok-exchange365-mailer:@dev
Configuration
1. Register an Azure App
Register an application in Microsoft Entra ID (formerly Azure AD) with Mail.Send and User.ReadBasic.All application permissions. Grant admin consent. See the full Azure setup guide.
2. Configure TYPO3
Set the mail transport to Exchange365Transport and provide your Azure credentials.
Via environment variables (.env):
| Variable | Description |
|---|---|
TYPO3_CONF_VARS__MAIL__transport |
OliverKroener\OkExchange365\Mail\Transport\Exchange365Transport |
TYPO3_CONF_VARS__MAIL__transport_exchange365_tenantId |
Microsoft Entra ID Tenant ID |
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientId |
Azure Application (Client) ID |
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientSecret |
Azure Application Secret Value |
TYPO3_CONF_VARS__MAIL__transport_exchange365_fromEmail |
Sender email address (must exist in Exchange 365) |
TYPO3_CONF_VARS__MAIL__transport_exchange365_graphSenderUserId |
(optional) Graph mailbox/user ID used for /users/{id}/sendMail. When set, this mailbox sends the message; the visible From header still comes from the message or fromEmail. Use for Send As / Send On Behalf. |
TYPO3_CONF_VARS__MAIL__transport_exchange365_saveToSentItems |
1 to save to Sent Items, 0 to skip (default: 0; the static template and site set default to 1 in the frontend) |
TYPO3 does not map
TYPO3_CONF_VARS__…variables by itself. Your project needs a small loop inconfig/system/additional.phpthat copies them into$GLOBALS['TYPO3_CONF_VARS'], or set the values there withgetenv(). Both are shown in Essential Configuration.
Via the site set (TYPO3 13 / 14, for frontend forms):
Add the set to your site configuration, then edit the values under Site Management → Sites → Settings:
# config/sites/<identifier>/config.yaml dependencies: - oliverkroener/ok-exchange365-mailer
Via TypoScript (per-site overrides in the frontend):
Include the static template [kroener.DIGITAL] Exchange 365 Mailer, then read the
credentials from the environment with getEnv() — never write the secret into
TypoScript:
plugin.tx_okexchange365mailer.settings.exchange365 {
tenantId := getEnv(EXCHANGE365_TENANT_ID)
clientId := getEnv(EXCHANGE365_CLIENT_ID)
clientSecret := getEnv(EXCHANGE365_CLIENT_SECRET)
fromEmail = service@your-domain.com
# Optional: route via a different mailbox using Send As / Send On Behalf
# graphSenderUserId = service@your-domain.com
saveToSentItems = 1
}
getEnv() reads PHP's real process environment (getenv()), not $_ENV: set the
variables where the web server starts PHP. A variable that is not set leaves the
previous value in place, and the result is cached until the next cache flush. Details:
Frontend Configuration.
The environment variables are the baseline for every context (frontend, backend, CLI, scheduler). In the frontend, non-empty site-set or TypoScript values override them per setting — leave a value empty to fall back. Use the site set or the static template on TYPO3 13/14, never both. Full details: Site Set Configuration.
Send As / Send On Behalf
The optional graphSenderUserId (or transport_exchange365_graphSenderUserId)
decouples the Graph mailbox used for the API call from the visible
From address of the message:
- The Graph endpoint
/users/{id}/sendMailis called againstgraphSenderUserId. - The message
Fromheader still resolves through$graphMessage['from']→fromEmail→defaultMailFromAddress, so recipients see the configured sender, not the Graph mailbox.
The configured Graph mailbox must hold Send As or Send On Behalf permission on the visible sender mailbox in Exchange. See the Microsoft Graph documentation on sending mail from another user.
When graphSenderUserId is unset (or empty), the same value resolution chain
is used to pick the Graph mailbox — i.e. the visible sender also sends the
message, which is the standard single-mailbox setup.
Sender Display Name
The Graph API uses the Display name configured on the mailbox in Exchange Online. TYPO3's defaultMailFromName has no effect. Configure the display name in the Microsoft 365 Admin Center or Exchange Admin Center.
Testing
make test-matrix provisions one throwaway DDEV installation per supported TYPO3
version — 9.5 to 14.3, each against its own branch — and runs unit, functional,
PHPStan and coding-standard checks in each. make test-matrix-live additionally sends
real mail through Microsoft Graph from the CLI and from the frontend (credentials only
via := getEnv()), and checks in a headless browser that the secret is masked in the
backend. See Testing.
Architecture
| Component | Description |
|---|---|
Exchange365Transport |
Custom Symfony mailer transport; handles OAuth2 auth and sends via Graph API |
ModifyBlindedConfigurationOptionsEventListener |
PSR-14 event listener; blinds credentials in TYPO3 configuration module |
MSGraphMailApiService (from ok-typo3-helper) |
Converts Symfony email messages to Microsoft Graph format |
Classes/
├── Mail/Transport/
│ └── Exchange365Transport.php
└── Lowlevel/EventListener/
└── ModifyBlindedConfigurationOptionsEventListener.php
Configuration/
├── Services.yaml
├── Sets/Exchange365Mailer/ # TYPO3 13/14 site set
│ ├── config.yaml
│ ├── settings.definitions.yaml
│ └── setup.typoscript
├── TCA/Overrides/sys_template.php # static template (TYPO3 12)
└── TypoScript/
├── constants.typoscript
└── setup.typoscript
Changelog
- Inline images (
cid:) fix — broken inline images in received mails were caused by Microsoft Graph not carrying over theContent-IDof inline attachments. Fixed in the related dependencyoliverkroener/ok-typo3-helper3.1.2 (MSGraphMailApiServicenow sets the attachment'sContent-ID). No change needed here — ensureok-typo3-helperis>= 3.1.2; the^3constraint already allows it.
License
GPL-2.0-or-later
Author — Oliver Kroener
Automated. Scaled. Done.
Web3 · Cloud · Automation
Technology is only valuable when it solves a real problem. For over 30 years I've been translating between business and tech — so your investment in digitalisation doesn't stall at proof-of-concept but delivers measurable results.
- Website: oliver-kroener.de
- Web3: web3.oliver-kroener.de
- Email: ok@oliver-kroener.de
- Web3 Email: oliverkroener@ethermail.io