---
title: Identity Assertion (ID-JAG)
description: Wie sich ein Agent, der bei einem vertrauenswürdigen Identity-Provider angemeldet ist, mit einem ID-JAG registriert, sich von einem Nutzer Lesezugriff und ausgewählte Änderungsrechte freigeben lässt und wie der Provider ihn wieder sperrt.
---

# Identity Assertion (ID-JAG)

Ein Agent, der schon eine Identität bei einem **vertrauenswürdigen Identity-Provider** hat, kann
sich statt mit `anonymous` oder `service_auth` mit einem **Identity Assertion JWT Authorization
Grant** (ID-JAG) registrieren. Gehört die bestätigte E-Mail darin zu einem Uptimeify-Konto, gibt
die Kontoinhaberin oder der Kontoinhaber den Agenten einmal auf der Seite `/claim` frei und legt
dabei fest, was er ändern darf. Danach registriert sich der Agent einfach mit einem frischen
ID-JAG neu und bekommt sein Token ohne erneute Freigabe.

Dieser Registrierungstyp ist **aus, solange Uptimeify keinem Identity-Provider vertraut**. Schau
in die Metadaten des Autorisierungsservers: Nur wenn `agent_auth.identity_types_supported`
`"identity_assertion"` enthält, sind die Endpunkte auf dieser Seite aktiv. Sonst antwortet
`POST /agent/identity {"type":"identity_assertion"}` mit `400 issuer_not_enabled` und
`POST /agent/event/notify` mit `404`.

## 1. Mit einem ID-JAG registrieren

`POST https://uptimeify.io/agent/identity`

| Feld | Typ | Erforderlich | Beschreibung |
| --- | --- | --- | --- |
| `type` | string | ja | `"identity_assertion"` |
| `assertion_type` | string | ja | `"urn:ietf:params:oauth:token-type:id-jag"` |
| `assertion` | string | ja | Das ID-JAG, das dein Identity-Provider ausgestellt hat. |
| `scope` | string | nein | Die gewünschten Scopes, durch Leerzeichen getrennt, zum Beispiel `"api.read api.write.monitors"`. Unbekannte Werte fallen weg; ist gar kein bekannter Scope dabei, gilt die Anfrage als `"api.read"`. |

Das ID-JAG muss:

- mit einem Schlüssel aus dem JWKS eines Ausstellers von Uptimeifys Vertrauensliste signiert sein
  (`iss`),
- `aud` = `https://uptimeify.io/api/`, ein nicht abgelaufenes `exp` und eine noch nie benutzte
  `jti` tragen (jede `jti` wird genau einmal angenommen),
- `email` mit `email_verified: true` tragen,
- eine `auth_time` tragen, die höchstens 3600 Sekunden zurückliegt; sonst lautet die Antwort
  `401 login_required` mit `max_age: 3600`, und du brauchst eine frische Anmeldung beim Provider.

```bash
curl -X POST https://uptimeify.io/agent/identity \
  -H 'Content-Type: application/json' \
  -d '{"type":"identity_assertion","assertion_type":"urn:ietf:params:oauth:token-type:id-jag","assertion":"<ID-JAG>","scope":"api.read api.write.monitors"}'
```

Die Antwort ist einer von drei Ausgängen, alle mit `Cache-Control: no-store`.

### Freigabe nötig: `401 interaction_required`

Die E-Mail gehört zu einem Uptimeify-Konto, und dieser Agent ist noch nicht freigegeben.

```json
{
  "error": "interaction_required",
  "registration_id": "reg_…",
  "claim": {
    "user_code": "WDJB-MJHT",
    "verification_uri": "https://uptimeify.io/claim",
    "verification_uri_complete": "https://uptimeify.io/claim?claim_attempt=cat_…",
    "expires_in": 600,
    "interval": 5
  },
  "claim_token": "clm_…",
  "requested_scope": "api.read api.write.monitors"
}
```

Schick deine Nutzerin oder deinen Nutzer zu `verification_uri_complete`. Angemeldet mit dem Konto
zu dieser E-Mail sieht sie oder er, was du angefragt hast: Lesezugriff und jeden angefragten
Schreibbereich als eigenes Kästchen. **Vorausgewählt ist kein Schreibbereich**; angehakt wird nur,
was der Agent wirklich braucht. Danach pollst du `POST /oauth2/token` mit dem Claim-Grant und dem
`claim_token`, genau wie in [Claim-Flow, Schritt 4](/de/api/agent-auth/claim-flow#4-auf-abschluss-pollen).
Die Erfolgsantwort trägt den freigegebenen `scope` und eine `identity_assertion` für spätere
Tauschvorgänge.

Eine passende E-Mail verknüpft nie von selbst ein Konto: Freigegeben ist erst etwas, wenn die
angemeldete Person auf `/claim` bestätigt.

### Schon freigegeben: `200` mit Konto-Assertion

Die Registrierung zu deiner Provider-Identität (`iss`, `sub`) wurde bereits freigegeben.

```json
{
  "registration_id": "reg_…",
  "identity_assertion": "<jwt>",
  "scope": "api.read api.write.monitors",
  "token_endpoint": "https://uptimeify.io/oauth2/token"
}
```

`scope` ist das Angefragte, wenn die Freigabe es abdeckt, sonst alles Freigegebene. Ohne Feld
`scope` bekommst du alles Freigegebene. Die `identity_assertion` tauschst du über den
`jwt-bearer`-Grant wie unter [Agent-Authentifizierung](/de/api/agent-auth#gegen-ein-zugriffstoken-tauschen)
beschrieben.

### Kein passendes Konto: `200` mit `tools.public`

Zu dieser E-Mail gibt es kein Uptimeify-Konto. Der Agent ist registriert und kann sofort die
öffentlichen Prüfwerkzeuge nutzen, mehr nicht.

```json
{
  "registration_id": "reg_…",
  "identity_assertion": "<jwt>",
  "scope": "tools.public",
  "token_endpoint": "https://uptimeify.io/oauth2/token"
}
```

## 2. Was der Agent darf

- **Nie mehr als die freigebende Person.** Bei jedem ausgestellten Token liest Uptimeify deren
  aktuelle Rolle und Kundenzuordnung. Hat sich die Rolle seit der Freigabe geändert, bekommt das
  Token die aktuelle; an der Registrierung wird keine Rolle gespeichert.
- **Nur das Freigegebene.** Ein Tausch, der einen nicht freigegebenen Scope verlangt, wird mit
  `400 invalid_grant` abgelehnt (`Identity-assertion grant refused: scope_exceeds_grant.`).
- **Global Supporter schreiben nie.** Bei so einem Konto fallen alle Schreibbereiche bei der
  Freigabe und bei jedem Tausch erneut weg; wer dann nur Schreibzugriff verlangt, bekommt
  `400 invalid_grant` (`nothing_left`).
- **Geänderte Kundenzuordnung heißt neu freigeben.** Weicht die Kundenbindung der Person von der
  freigegebenen ab, antwortet der Tausch mit `400 invalid_grant` (`binding_changed`).
- **Ohne Mensch kein Token.** Ist die freigebende Person gelöscht, antwortet der Tausch mit
  `400 invalid_grant` (`no_human`).
- Schreiben folgt denselben Regeln wie bei jedem Agenten-Token: Der Schreib-Schalter dieser
  Installation muss an sein, und jeder Schreibbereich erreicht nur seine eigenen Pfade; siehe
  [Fehlercodes](/de/api/error-codes-and-known-pitfalls#agent-auth-fehlercodes). Wo Uptimeify
  festhält, wer etwas geändert hat, wird eine Änderung des Agenten der freigebenden Person
  zugeschrieben.

## 3. Widerruf

Die Kontoinhaberin oder der Kontoinhaber sieht den Agenten unter **Einstellungen → Verbundene
Apps** als `<Provider-Host> (Agent)` und kann ihn dort widerrufen (siehe
[Verbundene Apps](/de/api/oauth-connections)). Der Widerruf beendet die Registrierung und jedes
daraus abgeleitete Zugriffstoken sofort.

Auch der Identity-Provider kann widerrufen, mit einem Security Event Token
([RFC 8417](https://www.rfc-editor.org/rfc/rfc8417)), das er an Uptimeify schickt
([RFC 8935](https://www.rfc-editor.org/rfc/rfc8935)):

`POST https://uptimeify.io/agent/event/notify`

```bash
curl -X POST https://uptimeify.io/agent/event/notify \
  -H 'Content-Type: application/secevent+jwt' \
  --data-binary '<SET>'
```

Das SET muss von einem vertrauten Aussteller signiert sein, `aud` = `https://uptimeify.io/api/`
und das `sub` des ID-JAG tragen und einen der Ereignistypen nennen, die die Metadaten unter
`agent_auth.events_supported` auflisten:

- `https://schemas.openid.net/secevent/risc/event-type/session-revoked`
- `https://schemas.openid.net/secevent/caep/event-type/session-revoked`

Ein SET, das eine Registrierung widerrufen hat, bekommt `202` mit leerem Körper. Alles andere
(Signatur, Aussteller, Audience, Ereignistyp, unbekanntes `sub`) bekommt `400` mit
`{"err":"invalid_request"}`, ohne zu verraten, welche Prüfung gescheitert ist, und widerruft
nichts. Die Adresse des Endpunkts steht auch als `agent_auth.events_endpoint` in den Metadaten.

## Häufige Fehler

| HTTP | `error` / `err` | Wo | Bedeutung |
| --- | --- | --- | --- |
| 400 | `issuer_not_enabled` | `POST /agent/identity` | Uptimeify vertraut in dieser Installation keinem Identity-Provider. |
| 400 | `invalid_request` | `POST /agent/identity` | Falscher `assertion_type`, fehlende `assertion`, oder das ID-JAG hat eine Prüfung nicht bestanden (Signatur, nicht vertrauter Aussteller, Audience, Ablauf, wiederverwendete `jti`, unbestätigte E-Mail). |
| 401 | `login_required` | `POST /agent/identity` | `auth_time` ist älter als `max_age` (3600 s): beim Provider neu anmelden. |
| 401 | `interaction_required` | `POST /agent/identity` | Kein Fehler zum Wiederholen: erst muss die Kontoinhaberin oder der Kontoinhaber auf `/claim` freigeben. |
| 400 | `invalid_grant` | `POST /oauth2/token` (jwt-bearer) | Der Tausch geht über die Freigabe hinaus, die Kundenbindung hat sich geändert, die Person wurde gelöscht, oder nach dem Streichen der Schreibrechte bleibt nichts übrig. |
| 400 | `expired_token` | `POST /oauth2/token` (Claim-Grant) | Wie im Claim-Flow; außerdem, wenn sich die freigebende Person beim Ausstellen nicht mehr auflösen lässt. |
| 400 | `invalid_request` (`err`) | `POST /agent/event/notify` | Falscher `Content-Type`, leerer Körper, oder das SET wurde nicht angenommen. |
| 404 | - | `POST /agent/event/notify` | Uptimeify vertraut keinem Identity-Provider, oder Aufruf über eine Custom-Domain. |

Die vollständige Liste steht in der
[Fehlercode-Referenz](/de/api/error-codes-and-known-pitfalls#agent-auth-fehlercodes).
