.. include:: Includes.rst.txt
Documentation
=============
.. contents:: Table of Contents
:depth: 2
:local:
Introduction
------------
The **ok_exchange365_mailer** is a TYPO3 extension that enables your
TYPO3 installation to send emails using Microsoft Exchange 365 via the
Microsoft Graph API. This ensures secure and reliable email delivery by
leveraging Microsoft’s cloud services directly from your TYPO3 site.
Features
--------
- **Microsoft Exchange 365 Integration**: Seamlessly integrate Exchange
365 as your email sending service.
- **Microsoft Graph API**: Utilize the powerful Microsoft Graph API for
email transmission.
- **Secure OAuth2 Authentication**: Secure communication with Exchange
365 using OAuth2 (client credentials).
- **Send As / Send On Behalf**: An optional ``graphSenderUserId`` sends
through a different Graph mailbox than the visible From address.
- **Credential masking**: Tenant ID, client ID and client secret from
``TYPO3_CONF_VARS`` are masked in **System > Configuration**.
Requirements
------------
- **TYPO3 CMS**: 9.5 LTS (this is the 1.x line; other TYPO3 versions
are served by other major versions of the extension).
- **PHP**: 7.2 – 7.4.
- **Composer**: For installation via Composer.
- **Dependencies**:
- oliverkroener/ok-typo3-helper https://packagist.org/packages/oliverkroener/ok-typo3-helper
Version ^1.
- guzzlehttp/guzzle Version ^6.3 || ^7.0.
Installation
------------
Install via Composer
~~~~~~~~~~~~~~~~~~~~
Run the following command in your TYPO3 project root directory:
.. code-block:: bash
composer require oliverkroener/ok-exchange365-mailer
This will install the extension along with its dependencies.
Configuration
-------------
To configure the extension to send emails via Exchange 365, follow these
steps:
Step 1: Register an Application in Azure Portal
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. **Log in** to the `Azure Portal `__.
2. Navigate to **Microsoft Entra ID** > **App registrations**.
3. Click **New registration**.
4. **Name** your application (e.g., “TYPO3 Mailer”).
5. Set **Supported account types** as per your requirements.
6. Click **Register**. No redirect URI is needed (client credentials flow).
Step 2: Configure API Permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. In your registered app, go to **API Permissions**.
2. Click **Add a permission**.
3. Select **Microsoft Graph**.
4. Choose **Application permissions**.
5. Find and add **Mail.Send**, **User.ReadBasic.All** permission.
6. Click **Grant admin consent** to grant permissions.
Step 3: Create a Client Secret
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Go to **Certificates & secrets**.
2. Click **New client secret**.
3. Provide a description and set an expiration.
4. Click **Add**.
5. Copy the **Value** of the client secret (not the Secret ID). **This is
shown only once**.
Step 4: Configure TYPO3
~~~~~~~~~~~~~~~~~~~~~~~
The extension reads its settings from ``$GLOBALS['TYPO3_CONF_VARS']['MAIL']``.
These settings are used everywhere — backend, CLI, scheduler and, unless
TypoScript overrides them, the frontend. The transport is selected **only**
in ``$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport']``; there is no
TypoScript option for it.
.. list-table::
:header-rows: 1
:widths: 40 60
* - ``$GLOBALS['TYPO3_CONF_VARS']['MAIL'][…]``
- Meaning
* - ``transport``
- ``OliverKroener\OkExchange365\Mail\Transport\Exchange365Transport``
* - ``transport_exchange365_tenantId``
- Microsoft Entra ID tenant ID
* - ``transport_exchange365_clientId``
- Application (client) ID of the app registration
* - ``transport_exchange365_clientSecret``
- The client secret **Value**
* - ``transport_exchange365_fromEmail``
- Sender mailbox (user or shared mailbox). Falls back to
``MAIL.defaultMailFromAddress``.
* - ``transport_exchange365_graphSenderUserId``
- Optional. The mailbox the Graph call ``/users/{id}/sendMail`` is
made through, when it differs from the visible From address
(*Send As* / *Send On Behalf*). Falls back to the message From
address, then ``fromEmail``, then ``MAIL.defaultMailFromAddress``.
See `Send mail from another user
`__.
* - ``transport_exchange365_saveToSentItems``
- ``1`` saves a copy in *Sent Items*, ``0`` does not. Default: ``0``.
Never write the client secret as a literal value into
:file:`LocalConfiguration.php` or :file:`AdditionalConfiguration.php` — TYPO3
rewrites :file:`LocalConfiguration.php` when settings change in the backend,
and both files typically end up in version control and backups. Read the
values from the environment instead.
**Option A: environment variables named** ``TYPO3_CONF_VARS__…``
.. code-block:: bash
TYPO3_CONF_VARS__MAIL__transport=OliverKroener\\OkExchange365\\Mail\\Transport\\Exchange365Transport
TYPO3_CONF_VARS__MAIL__transport_exchange365_tenantId='your-tenant-id'
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientId='your-client-id'
TYPO3_CONF_VARS__MAIL__transport_exchange365_clientSecret='your-client-secret'
TYPO3_CONF_VARS__MAIL__transport_exchange365_fromEmail='service@your-domain.com'
# Optional: Send As / Send On Behalf
#TYPO3_CONF_VARS__MAIL__transport_exchange365_graphSenderUserId='shared-mailbox@your-domain.com'
TYPO3_CONF_VARS__MAIL__transport_exchange365_saveToSentItems=1
.. important::
**TYPO3 does not read** ``TYPO3_CONF_VARS__…`` **variables by itself.**
The double-underscore naming is a widespread convention, but the mapping
onto ``$GLOBALS['TYPO3_CONF_VARS']`` has to be done by your project. If
your project has no such mapping, add this loop:
.. code-block:: php
:caption: public/typo3conf/AdditionalConfiguration.php
$value) {
if (!is_string($name) || strpos($name, 'TYPO3_CONF_VARS__') !== 0) {
continue;
}
$target = &$GLOBALS['TYPO3_CONF_VARS'];
foreach (explode('__', substr($name, strlen('TYPO3_CONF_VARS__'))) as $segment) {
$target = &$target[$segment];
}
$target = $value;
unset($target);
}
**Option B: read the environment in AdditionalConfiguration.php**
Without the mapping, set the values in
:file:`public/typo3conf/AdditionalConfiguration.php` and read the secrets
from the environment there. Use your own variable names:
.. code-block:: php
:caption: public/typo3conf/AdditionalConfiguration.php
Template**) by every user with access to that module, and
it is part of every database dump.
Usage
-----
Once configured, the extension handles email sending via Exchange 365
automatically. All emails sent by TYPO3 (system emails, form
notifications, etc.) will use the Microsoft Graph API.
Testing Email Sending
~~~~~~~~~~~~~~~~~~~~~
To verify that emails are being sent correctly, open **Admin Tools** >
**Environment** > **Test Mail Setup** and send a test mail. Check that it
arrives, and — with ``saveToSentItems`` enabled — that a copy appears in
the sender mailbox's *Sent Items*.
Automated tests
~~~~~~~~~~~~~~~
This branch is covered by the cross-version test matrix that lives on the
extension's main branch (``make test-matrix`` / ``make test-matrix-live``
there). For TYPO3 9.5 it runs the unit and functional tests, PHPStan and
the coding standard check, sends real mail through Microsoft Graph from the
CLI and from the frontend (credentials via ``:= getEnv()``), and checks in
a headless browser that the credentials are masked in the backend.
Troubleshooting
---------------
Common Issues
~~~~~~~~~~~~~
A failed send throws a ``RuntimeException`` whose message names the cause,
for example
``… Error: Exchange 365 configuration missing required field: clientSecret``.
- **"Exchange 365 configuration missing required field: …"**: The value is
empty in TypoScript **and** in ``TYPO3_CONF_VARS``. With ``getEnv()`` or
``TYPO3_CONF_VARS__…`` variables, check that the variable reaches PHP and
that your project maps it (see Step 4).
- **Old credentials still used after a change**: Flush the caches.
- **Authentication Errors (AADSTS…)**: Double-check your Tenant ID and
Client ID, and that the Client Secret has not expired.
- **Permission Denied (403)**: Ensure that **Mail.Send** application
permission is granted with admin consent, and that the sender mailbox is
not excluded by an application access policy.
Checking Logs
~~~~~~~~~~~~~
- **TYPO3 System Log**: Navigate to **System** > **Log** to view
system messages.
- **PHP Error Log**: Check your server’s PHP error logs for any runtime
errors.
- **Microsoft Entra ID sign-in logs**: Use the Azure Portal to monitor
the application's sign-ins and identify issues.
Security
~~~~~~~~
In **System > Configuration**, the extension masks the tenant ID, client ID
and client secret held in ``TYPO3_CONF_VARS`` (via a hook of the lowlevel
configuration module). Values set in TypoScript are not masked.
Support
~~~~~~~
If issues persist:
- **Contact the Author**: See `Author and
Support <#author-and-support>`__ section.
- **Consult Documentation**: Review Microsoft’s documentation on
`Microsoft Graph
API `__ for
additional insights.
License
-------
This extension is licensed under the `GNU General Public License v2.0 `__.
Author and Support
------------------
- **Author**: Oliver Kroener
- **Email**: ok@oliver-kroener.de
- **Website**: `oliver-kroener.de `__
For support, feature requests, or bug reports, please contact the author
via email.