OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

m365-oauth-email.md

8 KB
# Microsoft 365 / Exchange Online für das Ticketsystem (OAuth2)

Diese Anleitung beschreibt, wie das Ticket-Postfach statt mit klassischem
IMAP/SMTP-Passwort über **Microsoft 365 / Exchange Online mit OAuth2**
abgerufen und versendet wird.

Hintergrund: Microsoft hat die **Basic-Authentifizierung** (Anmeldung mit
Benutzername + Passwort) für IMAP, POP und SMTP AUTH in Exchange Online
abgekündigt. Für M365-Postfächer ist daher OAuth2 erforderlich.

OpenNIT bietet zwei OAuth-Verfahren zur Auswahl (Admin → Tickets →
Einstellungen → *Verbindungsverfahren*):

| Verfahren | Transport | Empfehlung |
|-----------|-----------|------------|
| **Microsoft 365 – Graph API** | REST-API (`Mail.ReadWrite`, `Mail.Send`) | **Empfohlen.** Robust, rein über HTTPS, keine veralteten Protokolle. |
| **Microsoft 365 – IMAP/SMTP (XOAUTH2)** | IMAP + SMTP mit OAuth-Token | Nur, wenn Graph organisatorisch nicht erlaubt ist, IMAP/SMTP aber schon. |

> **Wichtig:** Die native PHP-Erweiterung `ext/imap` unterstützt **kein**
> XOAUTH2. OpenNIT bringt dafür einen eigenen IMAP-Client mit – `php-imap`
> wird beim XOAUTH2-Verfahren also **nicht** benötigt. Graph kommt komplett
> ohne `php-imap` aus.

Beide Verfahren nutzen standardmäßig den **App-only-Flow (Client
Credentials)** – also **keinen** interaktiven Benutzer-Login, ideal für den
automatischen Abruf per Cron. Optional steht ein delegierter Flow mit
Refresh-Token zur Verfügung.

---

## Teil 1 – Einrichtung in Microsoft Entra ID (Azure AD)

### 1.1 App registrieren

1. [Microsoft Entra Admin Center](https://entra.microsoft.com) → **Identität →
   Anwendungen → App-Registrierungen → Neue Registrierung**.
2. Name z. B. `OpenNIT Ticket-Mail`.
3. **Unterstützte Kontotypen:** „Nur Konten in diesem Organisationsverzeichnis".
4. **Registrieren**.
5. Auf der Übersichtsseite notieren:
   - **Anwendungs-(Client-)ID**
   - **Verzeichnis-(Tenant-)ID**

### 1.2 Client-Secret erstellen

1. In der App → **Zertifikate & Geheimnisse → Neuer geheimer Clientschlüssel**.
2. Beschreibung + Ablaufdatum wählen (z. B. 24 Monate) → **Hinzufügen**.
3. Den **Wert** (nicht die „Geheimnis-ID") sofort kopieren – er wird nur
   einmal angezeigt.

> Den Ablauf rechtzeitig im Kalender vermerken: Läuft das Secret ab, schlägt
> der Mailabruf/-versand fehl, bis ein neues Secret hinterlegt wird.

### 1.3 API-Berechtigungen hinzufügen

#### Variante A – Graph API (empfohlen)

1. App → **API-Berechtigungen → Berechtigung hinzufügen → Microsoft Graph →
   Anwendungsberechtigungen**.
2. Hinzufügen:
   - `Mail.ReadWrite` – Lesen + als gelesen markieren / verschieben
   - `Mail.Send` – Versenden
3. **„Administratorzustimmung für <Tenant> erteilen"** klicken (grüner Haken
   bei beiden Berechtigungen).

#### Variante B – IMAP/SMTP (XOAUTH2)

1. App → **API-Berechtigungen → Berechtigung hinzufügen → APIs, die meine
   Organisation verwendet** → nach **Office 365 Exchange Online** suchen →
   **Anwendungsberechtigungen**.
2. Hinzufügen:
   - `IMAP.AccessAsApp` – Postfachzugriff per IMAP
   - `SMTP.SendAsApp` – Versand per SMTP
3. **Administratorzustimmung erteilen**.

### 1.4 Zugriff auf ein einzelnes Postfach beschränken (dringend empfohlen)

Standardmäßig dürfte die App auf **alle** Postfächer zugreifen. Den Zugriff
sollte man per Richtlinie auf das eine Ticket-Postfach (oder eine
Sicherheitsgruppe) einschränken.

**Für Graph (ApplicationAccessPolicy):**

```powershell
# Exchange Online PowerShell (Connect-ExchangeOnline)
New-ApplicationAccessPolicy `
  -AppId <CLIENT-ID> `
  -PolicyScopeGroupId tickets-mailbox-group@example.com `
  -AccessRight RestrictAccess `
  -Description "OpenNIT darf nur das Ticket-Postfach nutzen"

# Test:
Test-ApplicationAccessPolicy -Identity tickets@example.com -AppId <CLIENT-ID>
```

`PolicyScopeGroupId` ist eine Mail-aktivierte Sicherheitsgruppe, die nur das
Ticket-Postfach enthält.

**Für IMAP/SMTP (XOAUTH2)** muss der Service-Principal der App zusätzlich als
Postfach-Berechtigung registriert werden:

```powershell
# Service-Principal aus der App-Registrierung in Exchange registrieren
New-ServicePrincipal -AppId <CLIENT-ID> -ObjectId <ENTERPRISE-APP-OBJECT-ID> `
  -DisplayName "OpenNIT Ticket-Mail"

# Vollzugriff des Service-Principals auf das Ticket-Postfach
Add-MailboxPermission -Identity tickets@example.com `
  -User <SERVICE-PRINCIPAL-OBJECT-ID> -AccessRights FullAccess
```

> Die `<ENTERPRISE-APP-OBJECT-ID>` ist die Objekt-ID der zugehörigen
> *Unternehmensanwendung* (Entra ID → Unternehmensanwendungen → App suchen).

### 1.5 Vorgaben für SMTP (nur XOAUTH2)

- **SMTP AUTH** muss für das Postfach aktiviert sein
  (Exchange Admin Center → Postfach → E-Mail-Apps → *Authentifiziertes SMTP*),
  bzw. organisationsweit nicht deaktiviert.
- Endpunkt: `smtp.office365.com:587` (STARTTLS).
- IMAP-Endpunkt: `outlook.office365.com:993` (SSL).

---

## Teil 2 – Einrichtung in OpenNIT

1. **Admin → Tickets → Einstellungen** öffnen.
2. Unter **Verbindungsverfahren** das gewünschte Verfahren wählen:
   - *Microsoft 365 – Graph API* **oder**
   - *Microsoft 365 – IMAP/SMTP mit OAuth2 (XOAUTH2)*.
3. Im Block **Microsoft 365 – App-Registrierung** eintragen:
   - **Verzeichnis-(Tenant-)ID** (aus 1.1)
   - **Anwendungs-(Client-)ID** (aus 1.1)
   - **Client-Secret** (aus 1.2)
   - **Postfach (UPN/E-Mail)** – das Ticket-Postfach, z. B. `tickets@example.com`
   - **OAuth-Flow:** *App-only (Client Credentials)* (Standard/empfohlen)
4. Unter **Abruf & Versand** optional Absender-Name / abweichende
   Absenderadresse setzen und festlegen, was nach dem Abruf mit der Mail
   passiert (löschen oder in einen Ordner verschieben).
5. **Abruf testen** und **Versand testen** klicken – beide sollten „Verbindung
   erfolgreich" melden. Beim Abruf werden die verfügbaren Ordner geladen.
6. **Einstellungen speichern**.

Das Client-Secret (und ein evtl. Refresh-Token) werden mit dem `APP_KEY`
**verschlüsselt** in `config/runtime.php` abgelegt (AES-256-GCM).

### Delegierter Flow (optional)

Falls die Organisation App-only nicht erlaubt, kann der **Refresh-Token-Flow**
genutzt werden:

- In der App-Registrierung **delegierte** Berechtigungen vergeben
  (`Mail.ReadWrite`, `Mail.Send` bzw. `IMAP.AccessAsUser.All`, `SMTP.Send`,
  `offline_access`).
- Einen Refresh-Token über einen einmaligen Authorization-Code-Consent
  ermitteln und in OpenNIT unter **OAuth-Flow → Delegiert** eintragen.

---

## Teil 3 – Cron / Betrieb

Am Cron-Setup ändert sich **nichts**. Der bestehende Scheduler bzw. die
Legacy-Skripte erkennen das gewählte Verfahren automatisch:

```
* * * * * /usr/bin/php /pfad/zu/bin/scheduler.php >> /pfad/zu/storage/logs/scheduler.log 2>&1
```

bzw. die Legacy-Skripte `bin/fetch-tickets.php` (Abruf) und
`bin/process-mail-queue.php` (Versand).

OAuth-Access-Tokens werden in `storage/cache/m365-tokens/` zwischengespeichert
und automatisch erneuert, sobald sie ablaufen.

---

## Fehlerbehebung

| Meldung / Symptom | Ursache & Lösung |
|-------------------|------------------|
| `OAuth-Token abgelehnt (HTTP 401): AADSTS7000215` | Falsches Client-Secret. Neues Secret erzeugen und eintragen. |
| `AADSTS700016` / `AADSTS90002` | Falsche Client-ID bzw. Tenant-ID. |
| Graph: `ErrorAccessDenied` / `403` | Administratorzustimmung fehlt **oder** ApplicationAccessPolicy verbietet das Postfach. |
| Graph: `404` beim Postfach | `Postfach (UPN/E-Mail)` falsch geschrieben oder Postfach existiert nicht. |
| XOAUTH2 IMAP: `XOAUTH2-Authentifizierung abgelehnt` | `IMAP.AccessAsApp` fehlt, Service-Principal/`Add-MailboxPermission` nicht gesetzt, oder IMAP für das Postfach deaktiviert. |
| XOAUTH2 SMTP: `535 5.7.3 Authentication unsuccessful` | `SMTP.SendAsApp` fehlt oder *Authentifiziertes SMTP* ist für das Postfach deaktiviert. |
| Tokens werden ständig neu geholt | `storage/cache/` nicht beschreibbar. Schreibrechte prüfen. |

Bei tieferer Analyse hilft das Webserver-Error-Log (`error_log`) – die Klassen
schreiben aussagekräftige Meldungen bei fehlgeschlagenen Abrufen/Sendungen.