---
title: "Frontend Configuration"
manual: "Microsoft Exchange 365 Mailer"
version: "main"
permalink: "https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:frontend@main"
source: "Configuration/Frontend.rst"
rendered: "2026-10-04T20:03:13+00:00"
---

# Frontend Configuration {#frontend}

Mails sent from the frontend — by the **Form Framework**, **Powermail** or any other
extension that uses TYPO3's mailer — go through the same transport as backend and CLI
mails. In the frontend, the transport additionally reads TypoScript, so a site can use
its own credentials or sender.

> [!NOTE]
> **On TYPO3 v13 and v14, use the** [site set](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:sitesets@main) **instead.** It provides
> the same settings under the same names, but activated per site and editable in the
> backend. This page describes the static TypoScript template, which remains the only
> option on TYPO3 v12. Do not use both at the same time — see [Site Set Configuration](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:sitesets@main) for
> details.

## How frontend configuration works {#how-frontend-configuration-works}

The transport itself is always selected in `$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport']`
(see [Essential Configuration](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:essential@main)). TypoScript cannot switch the transport.

For the credentials and the sender, TypoScript **overlays** the
`$GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport_exchange365_*']` settings **per
parameter**:

-   A parameter with a value in TypoScript wins.
-   A parameter that is empty in TypoScript falls back to `TYPO3_CONF_VARS`.

So if the credentials are already configured for the backend, the frontend needs no
TypoScript at all. Add TypoScript only where a site should differ.

![TYPO3 TypoScript configuration showing Exchange365 frontend parameters](../_Images/image-frontend.png)

## Recommended: read the credentials from the environment {#frontend-getenv}

Never write the tenant ID, client ID or client secret into TypoScript. TypoScript is
stored in the database or in a site package, both of which end up in backups,
exports and version control. Read them from environment variables with the
TypoScript function `getEnv()` instead:

**setup.typoscript**

```typoscript
plugin.tx_okexchange365mailer.settings.exchange365 {
    tenantId := getEnv(EXCHANGE365_TENANT_ID)
    clientId := getEnv(EXCHANGE365_CLIENT_ID)
    clientSecret := getEnv(EXCHANGE365_CLIENT_SECRET)
    fromEmail := getEnv(EXCHANGE365_FROM_EMAIL)
}
```

The same works for constants, if you prefer to set them in
`constants.typoscript` and keep the static template's mapping:

**constants.typoscript**

```typoscript
plugin.tx_okexchange365mailer.settings.exchange365.clientSecret := getEnv(EXCHANGE365_CLIENT_SECRET)
```

Then provide the variables to the PHP process, for example:

**.env (DDEV: .ddev/.env.web)**

```bash
EXCHANGE365_TENANT_ID=00000000-0000-0000-0000-000000000000
EXCHANGE365_CLIENT_ID=00000000-0000-0000-0000-000000000000
EXCHANGE365_CLIENT_SECRET=your-client-secret-value
EXCHANGE365_FROM_EMAIL=service@your-domain.com
```

`getEnv()` is available on every TYPO3 version this extension supports.

> [!IMPORTANT]
> **Three things to know about** `getEnv()`. All three are verified by the
> extension's [test matrix](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:testing@main) on every supported TYPO3 version.
>
> -   **It reads the real process environment.** `getEnv()` calls PHP's
>     `getenv()`. A variable that is only loaded into `$_ENV` — which
>     is what `symfony/dotenv` and `helhum/dotenv-connector` do by default —
>     is **invisible** to it. Set the variable where the web server starts PHP
>     (DDEV `web_environment` or `.ddev/.env.web`, an `env[...]` line in
>     the PHP-FPM pool, `SetEnv` in Apache, the container environment), or
>     configure the dotenv loader to use `putenv()`.
> -   **An unset variable keeps the previous value.** If the variable does not
>     exist, `getEnv()` leaves the property unchanged — it does **not** empty
>     it. Clear the property first if a missing variable must not fall back to an
>     earlier value:
>
>     ```typoscript
>     plugin.tx_okexchange365mailer.settings.exchange365.clientSecret =
>     plugin.tx_okexchange365mailer.settings.exchange365.clientSecret := getEnv(EXCHANGE365_CLIENT_SECRET)
>     ```
>
>     The empty value then falls back to `TYPO3_CONF_VARS`. If that is empty
>     too, sending fails with
>     `Exchange 365 configuration missing required field: clientSecret`.
> -   **The value is cached.** TypoScript is parsed once and cached. After
>     changing an environment variable, flush the caches.

## TypoScript parameters {#typoscript-parameters}

All parameters live below `plugin.tx_okexchange365mailer.settings.exchange365`.
Include the static template **\[kroener.DIGITAL\] Exchange 365 Mailer** to get the
constants editor entries; its defaults are all empty, so including it changes
nothing until you set a value.

| Parameter | Meaning |
| --- | --- |
| `tenantId` | Microsoft Entra ID tenant ID ([Azure Configuration](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:azure@main), step 4). Use `:= getEnv(...)`. |
| `clientId` | Application (client) ID of the app registration (step 4). Use `:= getEnv(...)`. |
| `clientSecret` | The secret **Value** of the app registration (step 7). Always use `:= getEnv(...)`. |
| `fromEmail` | Sender address used when a mail has no From address. Must be a user or shared mailbox in your tenant. |
| `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](https://learn.microsoft.com/en-us/graph/outlook-send-mail-from-other-user). |
| `saveToSentItems` | `1` saves a copy in the mailbox's *Sent Items*, `0` does not. The static template sets `1`. Unlike the other parameters, an empty value here means `0` and does **not** fall back to `TYPO3_CONF_VARS`. |

## Per-environment credentials {#per-environment-credentials}

Use different environment variables per environment rather than TypoScript
conditions with literal IDs — the TypoScript stays identical everywhere and only
the server configuration differs:

```bash
# staging server
EXCHANGE365_CLIENT_ID=staging-app-id
EXCHANGE365_CLIENT_SECRET=staging-secret

# production server
EXCHANGE365_CLIENT_ID=production-app-id
EXCHANGE365_CLIENT_SECRET=production-secret
```

A different sender per site is a plain value, not a secret, so it can live in
TypoScript directly:

```typoscript
[site("identifier") == "shop"]
    plugin.tx_okexchange365mailer.settings.exchange365.fromEmail = shop@your-domain.com
[END]
```

## Integration with form extensions {#integration-with-form-extensions}

**Form Framework**, **Powermail** and other extensions use TYPO3's mailer and
therefore this transport automatically. They need no extra configuration; their
own sender settings become the message From address.

```yaml
finishers:
  -
    identifier: EmailToReceiver
    options:
      recipients:
        recipient@example.com: 'Recipient Name'
```

## Security considerations {#security-considerations}

> [!CAUTION]
> **Danger**
>
> -   **Never write the client secret into TypoScript** — not in a template
>     record, a site package or `settings.yaml`. Use `:= getEnv(...)`
>     or `TYPO3_CONF_VARS` filled from the environment.
> -   TypoScript is not sent to the browser, but it **is** readable in the
>     backend (*Site Management > TypoScript*) by every user with access to
>     that module, and it is part of every database dump.
> -   In *System > Configuration*, the extension masks the tenant ID, client ID
>     and client secret held in `TYPO3_CONF_VARS` — and, from TYPO3 v12 on,
>     the same settings in a site's configuration (*Sites YAML configuration*).

## Troubleshooting {#troubleshooting}

-   ****"Exchange 365 configuration missing required field: …"****

    The parameter is empty in TypoScript **and** in `TYPO3_CONF_VARS`. With
    `getEnv()`, the variable is most likely not in the PHP process environment —
    see the note on `getenv()` above. Run
    `php -r 'var_dump(getenv("EXCHANGE365_CLIENT_SECRET"));'` in the same
    environment as the web server to check.

-   ****Old credentials still used after a change****

    Flush the caches — TypoScript, including `getEnv()` results, is cached.

-   ****Authentication errors (AADSTS…)****

    Tenant ID or client ID is wrong, or the client secret has expired.

-   ****Permission errors (403)****

    The app registration lacks the `Mail.Send` application permission or admin
    consent, or the sender mailbox is outside an application access policy.

> [!NOTE]
> **See also**
>
> For backend configuration details, see [Essential Configuration](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:essential@main).
> For Azure setup instructions, see [Azure Configuration](https://docs.typo3.org/permalink/oliverkroener/ok-exchange365-mailer:azure@main).
