# Provisionierung mit SCIM 2.0

Nach dieser Seite bindet ein Verzeichnisdienst die Konten einer Organisation an Szenera an: Eintritt legt ein Konto an, Austritt sperrt es, ein Gruppenwechsel setzt die Rolle. Sie erfahren, wie der Zugang aussieht, welche Endpunkte es gibt, was aufeinander abgebildet wird und was bei Konflikten passiert.

> **Typ:** nachschlagen
> **Für wen:** Wer einen Verzeichnisdienst anbindet — Microsoft Entra ID, Okta oder eine eigene Anbindung nach RFC 7643 und RFC 7644.
> **Voraussetzung:** ein aktiver Anmeldeweg über den Anbieter der Organisation und ein SCIM-Zugang `sct_…` aus dem Portal

## Der Zugang

Die Schnittstelle liegt unter `/scim/v2` und **nicht** unter `/v1`: Der Pfad gehört dem fremden Protokoll, und ein Verzeichnisdienst prüft seine eigenen Angaben gegen die Adresse, unter der er spricht.

| Was | Wert |
|---|---|
| Basis-Adresse | `$SZENERA_API/scim/v2` |
| Ausweis | `Authorization: Bearer sct_…` |
| Inhaltstyp | `application/scim+json` in beide Richtungen |
| Reichweite | genau eine Organisation je Zugang |

Hat Ihre Umgebung einen Anmelde-Wirt, nennt das Portal als Basis-Adresse künftig `https://auth.<ihre-domain>/scim/v2`, und genau diese steht auch in `meta.location` der Antworten. Neue Einrichtungen tragen sie von Anfang an ein, eine bestehende an der Basis-Adresse der API bleibt bis auf Weiteres erreichbar.

Der Zugang entsteht im Portal unter **Einstellungen › Organisation › Unternehmens-Anmeldung**, Abschnitt **Provisionierung (SCIM)**. Sein Wert erscheint dort ein einziges Mal. Er kommt auf **keine** andere Route der Plattform: Überall sonst wird er mit `403` abgewiesen, und umgekehrt erreicht kein API-Schlüssel `/scim/v2`.

**Der Anmeldeweg ist Bedingung, nicht Empfehlung.** Ein so angelegtes Konto bekommt kein Passwort und meldet sich über den Anbieter der Organisation an. Ohne aktiven Anbieter lässt sich kein Zugang ausstellen.

## Die Endpunkte

| Route | Methoden |
|---|---|
| `/scim/v2/ServiceProviderConfig` | `GET` — Funktionsumfang: Teiländerung ja, Filter ja, Stapel nein, Sortierung nein |
| `/scim/v2/ResourceTypes`, `/scim/v2/ResourceTypes/{id}` | `GET` |
| `/scim/v2/Schemas`, `/scim/v2/Schemas/{id}` | `GET` |
| `/scim/v2/Users` | `GET` (Liste und Filter), `POST` |
| `/scim/v2/Users/{id}` | `GET`, `PUT`, `PATCH`, `DELETE` |
| `/scim/v2/Groups` | `GET` (Liste und Filter), `POST` |
| `/scim/v2/Groups/{id}` | `GET`, `PUT`, `PATCH`, `DELETE` (wird abgewiesen, siehe unten) |

Ein Verzeichnisdienst beginnt mit der ersten Zeile:

```bash
curl -sS "$SZENERA_API/scim/v2/ServiceProviderConfig" \
  -H "Authorization: Bearer $SZENERA_SCIM_TOKEN"
```

Ein Konto anlegen — wiederholbar, dieselbe Adresse liefert dasselbe Konto:

```bash
curl -sS -X POST "$SZENERA_API/scim/v2/Users" \
  -H "Authorization: Bearer $SZENERA_SCIM_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
        "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
        "userName": "deniz.acar@demo.local",
        "externalId": "entra-8f3c1a20",
        "name": { "givenName": "Deniz", "familyName": "Acar" },
        "active": true
      }'
```

Ein Konto sperren — die Teiländerung ist der Regelweg:

```bash
curl -sS -X PATCH "$SZENERA_API/scim/v2/Users/usr_01KFB7QPW0TEAM000000000002" \
  -H "Authorization: Bearer $SZENERA_SCIM_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
        "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
        "Operations": [{ "op": "replace", "path": "active", "value": false }]
      }'
```

## Blättern und Filtern

`startIndex` zählt ab **eins**, `count` ist auf 200 gedeckelt. Als Filter wird genau die Form `attribut eq "wert"` angenommen: `userName eq` und `externalId eq` auf `Users`, `displayName eq` auf `Groups`. Alles andere ist `400` mit `scimType: "invalidFilter"` — ein still ignorierter Filter lieferte sonst den ganzen Bestand.

```bash
curl -sS --get "$SZENERA_API/scim/v2/Users" \
  -H "Authorization: Bearer $SZENERA_SCIM_TOKEN" \
  --data-urlencode 'filter=userName eq "deniz.acar@demo.local"'
```

## Die Abbildung

| SCIM | Szenera |
|---|---|
| `userName` | die E-Mail-Adresse; sie ist je Organisation eindeutig und damit der gemeinsame Schlüssel |
| `externalId` | wird gespeichert, ist je Organisation eindeutig und über `externalId eq` auffindbar |
| `name.givenName`, `name.familyName`, `displayName` | der Anzeigename des Profils |
| `active: false` | das Konto sperren; `active: true` reaktiviert es |
| `DELETE /Users/{id}` | **sperrt**, löscht nicht |
| `Group` | eine Rolle der Organisation; `displayName` ist ihr Name |
| `members` | die Konten, die diese Rolle tragen |

Felder, die Szenera nicht führt (`title`, `phoneNumbers`, Erweiterungs-Schemas), werden still verworfen. Ihre Zuordnung im Verzeichnisdienst muss dafür nicht ausgedünnt werden.

Vier Eigenheiten, die eine Anbindung kennen muss:

- **Gelöscht wird nie.** An einem Konto hängen Protokoll-Einträge, hochgeladene Medien und angelegte Programme. Das endgültige Löschen bleibt ein bewusster Schritt eines Menschen im Portal.
- **Ein Konto trägt genau eine Rolle, nie keine.** Ein `add` weist die Rolle zu und ersetzt die bisherige; ein `remove` setzt auf die Basis-Rolle zurück; ein `PUT` ist beides zusammen, denn wer in der Liste fehlt, fällt zurück.
- **Gruppen entstehen nicht über SCIM.** Die Substanz einer Rolle sind ihre Rechte, und die trägt das Protokoll nicht mit. `POST /Groups` trifft deshalb eine bestehende Rolle über ihren `displayName`; ein unbekannter Name bekommt `400` mit `scimType: "invalidValue"`. Legen Sie die Rollen zuerst im Portal an und benennen Sie die Gruppen im Verzeichnis genauso.
- **Jede Änderung ist wiederholbar.** Ein Verzeichnisdienst wiederholt nach jeder Zeitüberschreitung, auch wenn der erste Anlauf durchlief. Ein zweites `POST /Users` mit derselben Adresse liefert dasselbe Konto; ein `PATCH active:false` auf ein bereits gesperrtes Konto ist ein stiller Erfolg.

Jede Änderung schreibt einen Eintrag ins Prüfprotokoll der Organisation, als Urheber steht dort „SCIM“.

## Fehler

Unter `/scim/v2` gilt das Fehlerformat des Protokolls, nicht [das dieser API](/de/api/error-format): Ein Verzeichnisdienst wertet `scimType` maschinell aus und entscheidet danach, ob er es erneut versucht, den Eintrag überspringt oder anhält.

```json
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "status": "409",
  "scimType": "uniqueness",
  "detail": "A user with this userName already exists in this organization"
}
```

`status` ist eine Zeichenkette, so schreibt es das Protokoll vor.

| Fall | Antwort |
|---|---|
| Der Ausweis fehlt oder gilt nicht | `401` |
| Der Ausweis ist ein anderer als `sct_…` | `403` |
| Die Adresse gibt es in dieser Organisation schon | `409` mit `scimType: "uniqueness"` |
| Der Filter hat eine andere Form als `attribut eq "wert"` | `400` mit `scimType: "invalidFilter"` |
| `POST /Groups` nennt eine Rolle, die es nicht gibt | `400` mit `scimType: "invalidValue"` |
| `DELETE /Groups/{id}` | `400` mit `scimType: "mutability"` — Rollen werden im Portal gelöscht |

## Weiterführendes

- [Anmeldung: Schlüssel und Sitzungen](/de/api/authentication)
- [Basis-URL, /v1 und Stabilität](/de/api/base-url-and-stability)
- [Handbuch: Konten aus dem Personalverzeichnis (SCIM)](https://szenera.com/de/docs/organization/scim-provisioning)
