# Identity & Access Management (IaM) – Microsoft Entra ID SSO

## Zusammenfassung

Dieses Dokument beschreibt die Integration von **Microsoft Entra ID Single Sign-On (SSO)** in den Sizing Shop V2. Ziel ist es, bestehenden Shop-Benutzern die Anmeldung mit ihrem Microsoft-Konto zu ermöglichen, ohne das bisherige Benutzername/Passwort-Login zu ersetzen.

## Ziele

- Entra ID SSO als **zusätzliche** Anmeldemethode anbieten.
- Bestehende Shop-Benutzer können über ihre **E-Mail-Adresse** mit ihrem Entra ID-Konto verknüpft werden.
- Benutzername/Passwort bleibt als Fallback erhalten.
- Admin-Bereich zeigt den Entra ID-Verknüpfungsstatus und erlaubt das Entfernen der Verknüpfung.
- Sichere Token-Validierung auf dem Backend.

## Nicht-Ziele

- Kein automatisches Anlegen neuer Shop-Benutzer durch Entra ID (nur vorhandene Benutzer können sich verknüpfen).
- Keine Rollen- oder Rechteverwaltung über Entra ID Gruppen (Shop-Rechte bleiben in `users.is_admin`).
- Kein Austausch des bestehenden JWT-Systems.

## Architektur

```
┌─────────────────┐      ┌──────────────────────┐      ┌─────────────────┐
│  Browser        │      │  Node.js / Express   │      │  MySQL 8.0      │
│  MSAL.js (CDN)  │◄────►│  REST-API            │◄────►│  sizing_shop    │
│  js/shop.js     │      │  /api/auth/entra     │      │  users.entra_id │
└─────────────────┘      └──────────────────────┘      └─────────────────┘
                              │
                              ▼
                       Microsoft Entra ID
                       (OpenID Connect)
```

## Ablauf

1. Benutzer klickt im Login-Modal auf **"Mit Microsoft anmelden"**.
2. Frontend leitet über MSAL.js zum Microsoft Login weiter (PKCE).
3. Nach erfolgreicher Anmeldung erhält das Frontend ein **ID Token**.
4. ID Token wird an `POST /api/auth/entra` gesendet.
5. Backend validiert das Token gegen die Microsoft öffentlichen Keys.
6. Backend liest die E-Mail-Adresse aus dem Token (`email` oder `preferred_username`).
7. Backend sucht einen aktiven Benutzer mit dieser E-Mail in `users.email`.
8. Bei Treffer wird ein Shop-JWT ausgestellt und der Benutzer ist eingeloggt.
9. Optional: Bei erstem Login eines bestehenden Benutzers wird `users.entra_id` mit der Microsoft Object ID gespeichert.

## Datenmodell

### Neue Spalte in `users`

```sql
ALTER TABLE users
  ADD COLUMN entra_id VARCHAR(255) DEFAULT NULL,
  ADD COLUMN entra_email VARCHAR(255) DEFAULT NULL,
  ADD UNIQUE KEY unique_entra_id (entra_id);
```

| Spalte | Bedeutung |
|--------|-----------|
| `entra_id` | Microsoft Object ID des Benutzers (optional, für explizite Verknüpfung) |
| `entra_email` | Die E-Mail-Adresse aus Entra ID zum Zeitpunkt der Verknüpfung |

### Matching-Logik

1. Falls `users.entra_id` gesetzt ist und mit der Object ID im Token übereinstimmt → direkter Match.
2. Sonst: Suche nach aktivem Benutzer mit `users.email` = E-Mail aus Token.
3. Bei Treffer ohne `entra_id`: Speichere `entra_id` und `entra_email` als Verknüpfung.
4. Kein Treffer → Fehler: "Kein Shop-Benutzer mit dieser E-Mail gefunden. Bitte wenden Sie sich an einen Administrator."

## API-Endpunkte

### `GET /api/auth/entra-config`

Liefert die für MSAL.js benötigten Konfigurationswerte (Client-ID, Tenant-ID, Redirect-URI). Kein Admin-Geheimnis wird ausgegeben.

**Response:**
```json
{
  "clientId": "11111111-2222-3333-4444-555555555555",
  "tenantId": "common" | "organizations" | "contoso.onmicrosoft.com",
  "redirectUri": "http://localhost:3000/index.html",
  "authority": "https://login.microsoftonline.com/common/v2.0"
}
```

### `POST /api/auth/entra`

Empfängt das ID Token und gibt bei erfolgreicher Validierung ein Shop-JWT zurück.

**Request:**
```json
{
  "idToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
}
```

**Response (Erfolg):**
```json
{
  "user": {
    "id": 42,
    "username": "max.mustermann",
    "email": "max.mustermann@contoso.com",
    "isAdmin": false,
    "isActive": true,
    "entraLinked": true
  },
  "token": "<shop-jwt>"
}
```

**Response (Fehler):**
- `401` – Token ungültig oder abgelaufen.
- `403` – Benutzer inaktiv.
- `404` – Kein Shop-Benutzer mit dieser E-Mail gefunden.

### `POST /api/users/:id/unlink-entra` (Admin)

Entfernt die Entra ID-Verknüpfung eines Benutzers.

**Response:**
```json
{ "ok": true }
```

## Frontend-Änderungen

### `index.html`

- Neuer Button **"Mit Microsoft anmelden"** im Auth-Modal.
- MSAL.js wird über CDN geladen:
  ```html
  <script src="https://alcdn.msauth.net/browser/2.37.0/js/msal-browser.min.js"></script>
  ```

### `js/data.js`

Neue Methoden im `Auth`-Objekt:

- `Auth.getEntraConfig()` – lädt `GET /api/auth/entra-config`.
- `Auth.entraLogin(idToken)` – sendet Token an `POST /api/auth/entra` und speichert Shop-JWT/User.

### `js/shop.js`

- `initMsal()` – initialisiert MSAL mit Konfiguration vom Backend.
- `loginWithMicrosoft()` – startet Popup- oder Redirect-Login.
- `handleMsalRedirect()` – verarbeitet den Redirect nach Microsoft-Login.
- Auth-Modal zeigt Microsoft-Button nur im Login-Modus (nicht bei Registrierung).

### `admin.html` / `js/admin.js`

- Benutzerliste zeigt **Entra ID Status** (verknüpft / nicht verknüpft).
- Bearbeiten-Modal zeigt verknüpfte E-Mail und bietet **"Entra ID Verknüpfung entfernen"**.

## Backend-Änderungen

### `server/schema.sql`

Neue Spalten in `users`:
```sql
ALTER TABLE users
  ADD COLUMN entra_id VARCHAR(255) DEFAULT NULL AFTER email,
  ADD COLUMN entra_email VARCHAR(255) DEFAULT NULL AFTER entra_id,
  ADD UNIQUE KEY unique_entra_id (entra_id);
```

### `server/package.json`

Neue Abhängigkeit:
```json
"jwks-rsa": "^3.1.0"
```

### `server/server.js`

Neue Umgebungsvariablen:
- `ENTRA_CLIENT_ID`
- `ENTRA_TENANT_ID` (default: `common`)
- `ENTRA_REDIRECT_URI` (default: `http://localhost:3000/index.html`)

Neue Hilfsfunktionen:
- `validateEntraIdToken(token)` – validiert Signatur, Issuer, Audience, Expiry.
- `findOrLinkUser(oid, email)` – Matching-Logik wie oben beschrieben.

Neue Endpunkte:
- `GET /api/auth/entra-config`
- `POST /api/auth/entra`
- `POST /api/users/:id/unlink-entra`

### `server/.env.example`

```
ENTRA_CLIENT_ID=11111111-2222-3333-4444-555555555555
ENTRA_TENANT_ID=common
ENTRA_REDIRECT_URI=http://localhost:3000/index.html
```

## Sicherheit

- ID Token wird serverseitig gegen Microsoft JWKS validiert.
- Es wird nur die E-Mail aus dem validierten Token verwendet.
- Token wird nicht im Browser gespeichert; nur das Shop-JWT wird im `localStorage` gehalten.
- Inaktive Benutzer können sich auch über Entra ID nicht anmelden.
- Admin-Entknüpfung erfordert Admin-Rechte.

## Entra ID App-Registrierung (Anleitung)

1. **Azure Portal** → **Microsoft Entra ID** → **App registrations** → **New registration**.
2. **Name**: z.B. `Sizing Shop V2`.
3. **Supported account types**: je nach Organisation wählen:
   - *Accounts in this organizational directory only* (nur eigener Tenant)
   - *Accounts in any organizational directory* (mehrere Tenants)
4. **Redirect URI**: `Single-page application (SPA)` → `http://localhost:3000/index.html` (lokal) bzw. die Produktiv-URL.
5. Nach der Registrieration: **Application (client) ID** kopieren → `ENTRA_CLIENT_ID`.
6. **Directory (tenant) ID** kopieren → `ENTRA_TENANT_ID`.
7. Unter **Authentication** → **Implicit grant and hybrid flows**: **ID tokens** aktivieren.
8. Unter **Token configuration** → **Add optional claim** → Token type **ID** → Claim **email** hinzufügen.
9. Unter **API permissions** → **Microsoft Graph** → **Delegated permissions**:
   - `openid`
   - `profile`
   - `email`
   - `User.Read`
10. **Grant admin consent** für die Berechtigungen erteilen.
11. `ENTRA_CLIENT_ID`, `ENTRA_TENANT_ID` und `ENTRA_REDIRECT_URI` in `.env` eintragen.

## Produktionshinweise

- `ENTRA_REDIRECT_URI` muss auf die Produktiv-URL zeigen (z.B. `https://sizing-shop.example.com/index.html`).
- In Azure Static Web Apps kann die alte `staticwebapp.config.json` entfernt oder deaktiviert werden, da die Authentifizierung jetzt über das eigene Backend läuft.
- Für Produktion sollte `ENTRA_TENANT_ID` auf den konkreten Tenant gesetzt werden (nicht `common`).
- Das Shop-JWT sollte langfristig auf httpOnly-Cookies umgestellt werden.

## Abgrenzung zur bestehenden `staticwebapp.config.json`

Die vorhandene `staticwebapp.config.json` wurde für das alte reine Static-Web-App-Hosting mit integriertem Entra ID Auth erstellt. Sie ist **nicht mehr aktiv**, sobald das eigene Backend die Authentifizierung übernimmt. Sie kann entweder entfernt oder als Dokumentationsartefakt belassen werden.

## Akzeptanzkriterien

- [ ] Benutzer kann sich im Shop mit "Mit Microsoft anmelden" einloggen.
- [ ] Nach erfolgreichem Microsoft-Login wird der Benutzer im Shop als eingeloggt angezeigt.
- [ ] Existiert kein Shop-Benutzer mit der Entra ID-E-Mail, wird eine klare Fehlermeldung angezeigt.
- [ ] Inaktive Benutzer können sich nicht über Entra ID anmelden.
- [ ] Admin sieht in der Benutzerverwaltung, ob ein Benutzer mit Entra ID verknüpft ist.
- [ ] Admin kann die Entra ID-Verknüpfung entfernen.
- [ ] Benutzername/Passwort-Login funktioniert weiterhin.
- [ ] Entra ID App-Registrierungsanleitung ist dokumentiert.
