Essential Configuration
After completing the Azure Configuration, you need to configure the TYPO3 extension with the values obtained from Microsoft Entra ID.
Attention
The Client ID can be found on the overview page in Azure and should not be confused with the Client Secret ID. For the secret configuration, only the Secret value itself is required, not the Secret ID.
Configuration Variables
The extension reads its settings from $GLOBALS. The
keys are prefixed with transport_ to avoid conflicts with other mail
transports. These settings are used everywhere — backend, CLI, scheduler and, unless
TypoScript overrides them, the frontend.
The steps below write them as environment variables named
TYPO3_, which keeps every secret out of files that end up in
version control.
Important
TYPO3 does not read TYPO3_ variables by itself. The
double-underscore naming is a widespread convention, but the mapping onto
$GLOBALS has to be done by your project — see
Mapping TYPO3_CONF_VARS__… variables. If your project has no such mapping, use
In TYPO3 configuration files, reading the environment instead.
-
Set the mail transport to Exchange365Transport.
Configure TYPO3 to use the Exchange 365 transport instead of the default SMTP transport.
TYPO3_CONF_VARS__MAIL__transport=OliverKroener\\OkExchange365\\Mail\\Transport\\Exchange365TransportCopied! -
Configure the Tenant ID.
Set the Tenant ID obtained from step 4 of the Azure Configuration.
TYPO3_CONF_VARS__MAIL__transport_exchange365_tenantId='your-tenant-id-here'Copied!Attention
Replace
your-with the actual Tenant ID from your Azure application overview page.tenant- id- here -
Configure the Client ID.
Set the Client ID (Application ID) obtained from step 4 of the Azure Configuration.
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientId='your-client-id-here'Copied!Attention
Replace
your-with the actual Client ID from your Azure application overview page.client- id- here -
Configure the Client Secret.
Set the Client Secret value obtained from step 7 of the Azure Configuration.
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientSecret='your-client-secret-here'Copied!Attention
- Replace
your-with the actual Secret Value (not the Secret ID)client- secret- here - This is sensitive information - keep it secure and never expose it in public repositories
- Replace
-
Configure the sender email address.
Set the email address that will be used as the sender for all emails sent through this transport.
TYPO3_CONF_VARS__MAIL__transport_exchange365_fromEmail='service@your-domain.com'Copied!Attention
The email address will fall back to TYPO3's
$GLOBALSif not specified here.['TYPO3_ CONF_ VARS'] ['MAIL'] ['default Mail From Address'] Note
- Replace
service@your-with a valid email address from your organization (it must exist in your Exchange 365 environment as SharedMailbox or User Mailbox)domain. com - This email address must exist in your Exchange 365 environment
- The application needs permission to send emails on behalf of this address
- Replace
-
Configure the Microsoft Graph sender user ID (optional).
Set the mailbox/user ID used as the path parameter for the Microsoft Graph
/users/endpoint. This is the mailbox the API call is made through, which can differ from the visible message{id}/ send Mail Fromaddress — useful when the sender mailbox has Send As or Send On Behalf permissions on another mailbox.TYPO3_CONF_VARS__MAIL__transport_exchange365_graphSenderUserId='account1@your-domain.com'Copied!Note
- Optional. When unset, the extension falls back to the message
Fromaddress, thentransport_, thenexchange365_ from Email $GLOBALS— preserving previous behavior.['TYPO3_ CONF_ VARS'] ['MAIL'] ['default Mail From Address'] - The configured mailbox must exist in your Exchange 365 environment and the Azure application must have
Mail.permission for it.Send - Required Exchange permission on the visible-sender mailbox: Send As or Send On Behalf. See Send mail from another user (Microsoft Graph).
- Example: set
graphtoSender User Id account1@your-while keepingdomain. com from(or the messageEmail Fromheader) asaccount2@your-. The Graph call targetsdomain. com account1; recipients seeaccount2.
- Optional. When unset, the extension falls back to the message
-
Configure save to sent items (optional).
Determine whether sent emails should be saved to the sender's "Sent Items" folder.
TYPO3_CONF_VARS__MAIL__transport_exchange365_saveToSentItems=1Copied!Note
- Set to
1to save emails to Sent Items folder - Set to
0to skip saving emails to Sent Items folder - Default when not set:
0for backend, CLI and scheduler mails. In the frontend, the static template and the site set default to1.
- Set to
Configuration Example
Here's a complete example of all required configuration variables:
Environment Variables (.env file)
You can configure these settings using a .env file in your TYPO3 root directory:
# Exchange 365 Mail Transport Configuration
TYPO3_CONF_VARS__MAIL__transport=OliverKroener\\OkExchange365\\Mail\\Transport\\Exchange365Transport
TYPO3_CONF_VARS__MAIL__transport_exchange365_tenantId='your-tenant-id-here'
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientId='your-client-id-here'
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientSecret='your-client-secret-here'
TYPO3_CONF_VARS__MAIL__transport_exchange365_fromEmail='service@your-domain.com'
# Optional: Graph sender mailbox (Send As / Send On Behalf scenarios).
# Leave unset to use fromEmail as the Graph user ID (default behavior).
#TYPO3_CONF_VARS__MAIL__transport_exchange365_graphSenderUserId='account1@your-domain.com'
TYPO3_CONF_VARS__MAIL__transport_exchange365_saveToSentItems=1
Alternative Configuration Methods
Mapping TYPO3_CONF_VARS__… variables
If your project does not already map TYPO3_ environment variables,
add this loop. Double underscores become array levels, so
TYPO3_ ends up in
$GLOBALS.
<?php
// Variables loaded by a dotenv library live in $_ENV, variables set by the
// web server or container in getenv(). Read both.
foreach (array_merge(getenv(), $_ENV) as $name => $value) {
if (!is_string($name) || !str_starts_with($name, 'TYPO3_CONF_VARS__')) {
continue;
}
$target = &$GLOBALS['TYPO3_CONF_VARS'];
foreach (explode('__', substr($name, strlen('TYPO3_CONF_VARS__'))) as $segment) {
$target = &$target[$segment];
}
$target = $value;
unset($target);
}
On PHP 7.x (TYPO3 v9 and v10 projects), replace str_
with strpos.
In TYPO3 configuration files, reading the environment
Without the mapping, set the values in config/
(TYPO3 v12+) or typo3conf/ (TYPO3 v9–v11) and
read the secrets from the environment there. Use your own variable names:
<?php
$env = static fn (string $name): string => (string)(getenv($name) ?: ($_ENV[$name] ?? ''));
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport'] = \OliverKroener\OkExchange365\Mail\Transport\Exchange365Transport::class;
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_tenantId'] = $env('EXCHANGE365_TENANT_ID');
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_clientId'] = $env('EXCHANGE365_CLIENT_ID');
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_clientSecret'] = $env('EXCHANGE365_CLIENT_SECRET');
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_fromEmail'] = 'service@your-domain.com';
// Optional: distinct Graph sender mailbox (Send As / Send On Behalf).
// $GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_graphSenderUserId'] = 'account1@your-domain.com';
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_saveToSentItems'] = 1;
On PHP 7.x, write the arrow function as a regular closure.
Warning
Do not write the client secret as a literal into config/,
Local or additional.. TYPO3 rewrites
settings. when settings change in the backend, and all of these
files typically end up in version control and backups.
Note
In the frontend, the same values can also come from TypoScript, where
:= get reads them from the environment. See Recommended: read the credentials from the environment.
Testing the Configuration
After configuring all variables, you can test the email functionality by:
- Sending a test email through TYPO3's mail functionality
- Checking the TYPO3 logs for any error messages
- Verifying that emails are received at the intended recipients
- Checking the sender's "Sent Items" folder if
saveis enabledTo Sent Items
Tip
Enable TYPO3's developer log to see detailed information about the email sending process and any potential issues with the Exchange 365 integration.
Security Considerations
Warning
Azure Credential Security
- Store the
clientsecurely using environment variables or encrypted configurationSecret - Never commit secrets to version control systems
- Rotate client secrets regularly before expiration
- Use different Azure applications for different environments (dev/staging/prod)
- Monitor Azure sign-in logs for unauthorized access
Configuration Validation
To verify your configuration is correct:
-
Check in backend:
- Open Admin Tools > Environment > Test Mail Setup (TYPO3 v14: System > Environment)
- Ensure the transport is set to
Exchange365Transportand all required fields are filled
-
Check TYPO3 configuration:
// In TYPO3 backend or debug context \TYPO3\CMS\Core\Utility\DebugUtility::debug($GLOBALS['TYPO3_CONF_VARS']['MAIL']);Copied! -
Test email sending:
// Test email functionality (TYPO3 v10+; v14 removed MailMessage::send()) $mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class); $mail->to('test@example.com') ->subject('Test Email') ->text('This is a test email from TYPO3.'); \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\Mailer::class)->send($mail);Copied! - Check logs: Monitor TYPO3 logs for authentication or sending errors.
A failed send throws a Symfony
Transport(aException Runtime) whose message names the cause, for exampleException Exchange 365 configuration missing required field: client.Secret