Sign in with Apple sendet vier Benachrichtigungen, nicht drei
In Apples Entwicklerankündigung zu den Server-zu-Server-Benachrichtigungen von Sign in with Apple stehen drei Dinge, die Ihr Endpunkt empfangen wird: Änderungen an den Einstellungen zur E-Mail-Weiterleitung, Kontolöschungen innerhalb Ihrer App und dauerhafte Löschungen des Apple Accounts.3 Die Dokumentation zur API definiert vier eigenständige Ereignistypen.1
Eine Implementierung, die aus der Ankündigung heraus entsteht, behandelt drei Zweige und verwirft stillschweigend ein viertes Ereignis. Die Ankündigung fasst email-enabled und email-disabled zu einem einzigen Stichpunkt über Weiterleitungseinstellungen zusammen. Tatsächlich treffen sie als getrennte Benachrichtigungen mit getrennten Typwerten ein, und Code, der ohne Default-Zweig auf den Typ verzweigt, ignoriert genau jenen Wert, den der Autor vergessen hat.
Zwei der vier Ereignisse bedeuten zudem mehr, als ihre Namen vermuten lassen, und eines davon verändert den Authentifizierungszustand Ihrer App.
Kurzfassung
Sign in with Apple liefert vier Typen von Server-zu-Server-Benachrichtigungen: email-enabled, email-disabled, consent-revoked und account-deleted.1 Die Nutzlast trifft als JWS ein, eingebettet in ein JSON-Objekt unter dem Schlüssel payload, signiert mit Apples privatem Schlüssel – und muss mit dem Algorithmus validiert werden, der im Parameter alg des Headers genannt ist, bevor Sie darauf reagieren.1 consent-revoked macht die Anmeldedaten des Benutzers ungültig und ist damit ein Authentifizierungsereignis, keine Änderung einer Einstellung. Native Apps erhalten keinen clientseitigen Callback, wenn ein Apple Account gelöscht wird; die Server-Benachrichtigung ist also das einzige Signal.1 Zu Wiederholungsversuchen oder Zustellsemantik dokumentiert Apple nichts.
Die vier Ereignistypen
Jede Benachrichtigung trägt einen type-Wert innerhalb des events-Claims.1
type |
Was passiert ist |
|---|---|
email-enabled |
Der Benutzer hat die E-Mail-Weiterleitung an seine persönliche Adresse über Hide My Email aktiviert |
email-disabled |
Der Benutzer hat die E-Mail-Weiterleitung deaktiviert |
consent-revoked |
Der Benutzer hat seine Zustimmung für Ihre App widerrufen, und seine Anmeldedaten sind ungültig geworden |
account-deleted |
Der Benutzer hat die dauerhafte Löschung seines Apple Accounts beantragt |
Die beiden E-Mail-Ereignisse sind genau jene, die die Ankündigung in einen Topf wirft. Sie zählen unabhängig voneinander: email-disabled bedeutet, dass Post an die Relay-Adresse den Benutzer nicht mehr erreicht, email-enabled bedeutet, dass sie ihn wieder erreicht. Wer beide als ein einziges Ereignis „Einstellungen geändert“ behandelt, muss anschließend den aktuellen Zustand erfragen – den die Benachrichtigung bereits mitgeteilt hatte.
Zwei Ereignisse, die nicht das sind, wonach sie aussehen
consent-revoked ist ein Authentifizierungsereignis. Apple beschreibt es so: Der Benutzer „widerruft die Zustimmung zur Nutzung seines Apple Accounts durch Ihre App, und seine Anmeldedaten werden ungültig.“1 Nicht veraltet, nicht demnächst ablaufend. Ungültig.
Eine App, die das zusammen mit den E-Mail-Ereignissen protokolliert und eine Einstellungszeile aktualisiert, zeigt weiterhin eine angemeldete Oberfläche – gestützt auf Anmeldedaten, die nicht mehr authentifizieren. Der Benutzer sieht sein Konto, bis die nächste Token-Aktualisierung fehlschlägt, und danach sieht er Schlimmeres. Richtig ist, die Sitzung zu beenden und zur erneuten Authentifizierung zu leiten – über denselben Codepfad, den Sie auch bei einer widerrufenen OAuth-Berechtigung nehmen würden.
account-deleted ist möglicherweise der einzige Hinweis, den Sie bekommen. Apple hält fest: Löscht ein Benutzer seinen Apple Account dauerhaft, macht Sign in with Apple sämtliche Benutzer-Token ungültig und deaktiviert die E-Mail-Weiterleitung für alle zugehörigen Apps; bei nativen Apps sendet das System dabei keinen clientseitigen Callback.1
Dieser Satz ist das stärkste Argument dafür, überhaupt einen Endpunkt zu betreiben. Ein reines iOS-Team ohne Server-zu-Server-Endpunkt hat keinerlei Mechanismus, um zu erfahren, dass das Konto verschwunden ist. Der Datensatz bleibt bestehen, die Relay-Adresse funktioniert nicht mehr, und jede Löschpflicht, die Sie haben, bleibt unerfüllt – weil nichts Ihnen mitgeteilt hat, dass es überhaupt etwas zu löschen gab.
Den Endpunkt registrieren
Konfiguriert wird in Certificates, Identifiers & Profiles: Wählen Sie Identifiers, dann Ihre App-ID, aktivieren Sie den Dienst Sign in with Apple, klicken Sie auf Configure und hinterlegen Sie die Endpunkt-URL.2
Die Einschränkungen sollten Sie lesen, bevor Sie darauf aufbauen.2
- Eine URL pro Sign-in-with-Apple-App-Gruppierung und Schlüssel. Nicht eine pro App.
- Registrierbar nur auf einer primären App-ID.
- Die URL muss ein absoluter URI mit Schema, Host und Pfad sein:
https://example.com/path/to/endpoint - TLS 1.2 oder neuer ist erforderlich, um Benachrichtigungen zu empfangen.
Apples Dokumentation zur API ergänzt, dass Sie dieselbe URL für mehrere Entwicklerteams und Apps verwenden dürfen.1 Zusammen mit der Regel „eine URL pro Gruppierung“ gelesen, ergibt sich als sinnvolle Deutung: Ein einzelner Dienst kann alles entgegennehmen, während jede Gruppierung ihren eigenen Verweis darauf registriert. Apple erläutert dieses Zusammenspiel nicht, also betrachten Sie einen gemeinsam genutzten Endpunkt als machbar, nicht als abgesegnet – und stellen Sie sicher, dass Ihr Handler erkennen kann, welche App eine Benachrichtigung betrifft, bevor Sie sich darauf verlassen.
Die Untergrenze TLS 1.2 steht in einem größeren Zusammenhang. OS 27 begann, strengere TLS-Anforderungen an den Verwaltungsverkehr durchzusetzen – mit derselben Mindestversion 1.2, dazu ATS-konformen Ciphersuites und Zertifikaten. Ein Endpunkt, der Apples heutige Anforderung erfüllt, ist damit nicht automatisch ATS-konform, und die Entwicklung geht in Richtung strengerer, nicht lockerer Anforderungen.
Die Nutzlast lesen
Die Zustellung erfolgt als HTTP-POST, dessen Body ein JSON-Objekt ist, mit dem signierten Token darin:1
{
"payload": "<SERVER_TO_SERVER_NOTIFICATION_JWS>"
}
Das JWS steckt in einer Hülle, es ist nicht der rohe Body. Parsen Sie das JSON, ziehen Sie payload heraus, validieren Sie erst dann. Eine Implementierung, die den gesamten Request-Body an einen JWS-Verifier übergibt, scheitert schon an der ersten Benachrichtigung – und der Fehler zeigt sich als fehlerhaftes Token statt als übersehene Hülle, was Sie an der falschen Stelle suchen lässt.
Validierung kommt vor Interpretation. Die Nutzlast ist mit Apples privatem Schlüssel im Format JSON Web Signature kryptografisch signiert, und Apples Anweisung lautet, das JWS zu prüfen und die Signatur mit dem im Parameter alg des Headers angegebenen Algorithmus zu validieren.1 Erst wenn die Signatur stimmt, lesen Sie den events-Claim und verzweigen auf type.
Zwei Gewohnheiten aus der allgemeinen JWS-Praxis sind es wert, beibehalten zu werden: Vertrauen Sie niemals einem alg-Wert, mit dem ein Aufrufer die Prüfung herabstufen könnte, und bestätigen Sie, dass Aussteller und Adressat des Tokens Ihren Erwartungen entsprechen, statt jedes wohlgeformte, von Apple signierte Token zu akzeptieren.
Die dekodierte Form
Nach erfolgreicher Validierung sieht eine dekodierte consent-revoked-Benachrichtigung so aus:1
{
"iss": "https://appleid.apple.com",
"aud": "com.mytest.app",
"iat": 1508184845,
"jti": "abede...67890",
"events": {
"type": "consent-revoked",
"sub": "820417.faa325acbc78e1be1668ba852d492d8a.0219",
"event_time": 1508184845
}
}
account-deleted trägt dieselben Felder mit einem anderen type. Die E-Mail-Ereignisse ergänzen zwei weitere: email und is_private_email.
Drei Details dieser Form kosten Sie Zeit, wenn Sie ihnen unvorbereitet begegnen.
events ist ein Objekt, kein Array. Der Name steht im Plural, der Wert ist ein einzelnes Ereignis. Code, der sich am Namen statt an der Form orientiert, iteriert über ein Dictionary und bekommt Schlüssel.
is_private_email ist ein String. Apples Beispiele zeigen "true" in Anführungszeichen, nicht den booleschen JSON-Wert true. Ein strikter Decoder, der das auf Bool abbildet, scheitert; ein nachsichtiger, der jeden nicht-leeren String als wahr wertet, kommt aus dem falschen Grund zum richtigen Ergebnis – und liegt bei "false" dann daneben.
sub ist die stabile Benutzerkennung, derselbe Wert, den Sie bei der Anmeldung erhalten haben, und darüber finden Sie das Konto, um das es in dieser Benachrichtigung geht. aud ist Ihre Client-Kennung, und sie ist es, die einem gemeinsam genutzten Endpunkt erlaubt, Benachrichtigungen für mehrere Apps zuzuordnen.
Eine Anmerkung zu Apples eigenen Beispielen: In den beiden E-Mail-Nutzlasten fehlt das Komma zwischen "is_private_email": "true" und "event_time". Kopieren Sie einen der beiden Blöcke in einen JSON-Parser, und er wird das Dokument zurückweisen. Die Struktur stimmt, die Zeichensetzung nicht – und wer das in ein Test-Fixture kopiert, verliert zehn Minuten an einen Syntaxfehler, der nicht der eigene ist.
Auch Apples Terminologie schwankt. Der Fließtext beschreibt eine Nutzlast im Format JSON Web Signature, während das Beispiel für die Hülle den Wert SERVER_TO_SERVER_NOTIFICATION_JWT nennt.1 Gemeint ist dasselbe Objekt: Ein signiertes JWT ist ein JWS mit einer JSON-Nutzlast. Gut zu wissen, wenn Sie Apples Dokumentation durchsuchen und auf beide Begriffe stoßen.
Ein Handler, von Anfang bis Ende
Die Form eines korrekten Handlers ergibt sich aus den obigen Einschränkungen. In Python, mit PyJWT:
import json
import jwt
from jwt import PyJWKClient
# Apple publishes its signing keys as a JWKS. Cache the client;
# it fetches and caches keys rather than hitting Apple per request.
JWKS = PyJWKClient("https://appleid.apple.com/auth/keys")
CLIENT_ID = "com.mytest.app" # your aud value
def handle_notification(request_body: bytes):
# 1. The JWS is wrapped in JSON under "payload", not the raw body.
wrapper = json.loads(request_body)
token = wrapper["payload"]
# 2. Resolve the signing key by the token's kid, then verify.
# Pin the algorithm. Never read alg from the token to decide.
signing_key = JWKS.get_signing_key_from_jwt(token)
claims = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
audience=CLIENT_ID,
issuer="https://appleid.apple.com",
)
# 3. Only now is anything trustworthy.
event = claims["events"] # an object, not a list
apple_user_id = event["sub"] # stable identifier from sign-in
match event["type"]:
case "consent-revoked" | "account-deleted":
end_all_sessions(apple_user_id)
mark_account_unlinked(apple_user_id)
case "email-disabled":
set_email_forwarding(apple_user_id, enabled=False)
case "email-enabled":
set_email_forwarding(apple_user_id, enabled=True)
case other:
log_unknown_event(other) # do not fail silently
Einige dieser Zeilen tragen die eigentliche Last.
Legen Sie den Algorithmus fest. Apples JWKS veröffentlicht derzeit drei RSA-Schlüssel, allesamt RS256 mit use: sig und unterschiedlichen kid-Werten. algorithms=["RS256"] zu übergeben, statt alg aus dem Token zu lesen, schließt den klassischen Downgrade-Pfad, bei dem ein Angreifer ein Token mit alg: none unterschiebt.
Lösen Sie über kid auf, nicht über den ersten Schlüssel. Drei Schlüssel sind gleichzeitig aktiv – so sieht Schlüsselrotation von außen aus. Ein Handler, der sich keys[0] greift, funktioniert bis zur nächsten Rotation und scheitert dann für einen Teil der Token, was sich elend debuggen lässt.
Prüfen Sie aud und iss. Ohne sie akzeptieren Sie jedes gültig signierte Apple-Token, auch eines, das für eine andere App ausgestellt wurde.
Behalten Sie einen Default-Zweig bei. Sonst verschwindet ein fünfter Ereignistyp spurlos. Genau so verliert eine Implementierung, die aus Apples dreiteiliger Ankündigung entstanden ist, heute email-enabled oder email-disabled.
Was Apple nicht dokumentiert
Weder die Dokumentation zur API noch die Hilfeseite zum Konto sagt irgendetwas zu Zustellgarantien. Die Suche auf beiden Seiten nach Wiederholung, erneuter Zustellung, Bestätigung, Statuscodes, Zeitüberschreitungen und Idempotenz liefert nichts.
Folgendes bleibt in Apples Dokumentation zum Zeitpunkt dieses Textes also unbeantwortet:
- Ob eine fehlgeschlagene Zustellung wiederholt wird, und wie oft
- Über welchen Zeitraum Wiederholungen erfolgen, falls sie erfolgen
- Welchen Statuscode Ihr Endpunkt zurückgeben soll, um Erfolg zu signalisieren
- Ob dieselbe Benachrichtigung zweimal eintreffen kann
Diese Leerstelle hat Folgen für den Entwurf – und hier gehe ich über das hinaus, was Apple festhält, statt es nur wiederzugeben. Ein Endpunkt, dessen Zustellsemantik unspezifiziert ist, taugt nicht als maßgeblicher Ereignisstrom. Die defensive Haltung besteht darin, jede Benachrichtigung als Hinweis darauf zu behandeln, dass sich etwas geändert hat, und gegen die eigenen Datensätze abzugleichen, statt das Ereignis blind anzuwenden. Machen Sie Handler idempotent, denn Duplikate lassen sich nicht ausschließen. Bauen Sie keinen Ablauf, dessen Korrektheit davon abhängt, jede Benachrichtigung erhalten zu haben – denn Sie können nicht bestätigen, dass Sie das haben.
Wenn Ihr Endpunkt eine Stunde lang ausfällt, wissen Sie aus der Dokumentation nicht, ob Sie eine Stunde Kontolöschungen verloren haben oder ob diese irgendwo in einer Warteschlange liegen. Entwerfen Sie so, als hätten Sie sie verloren.
Und es gibt auch keinen dokumentierten Weg, das zu testen
Die Suche auf beiden Seiten nach Sandbox, Simulation und Auslösen liefert nichts. Apple dokumentiert keinen Mechanismus, um eine Benachrichtigung auf Zuruf auszulösen.
Daraus ergibt sich eine unangenehme Schleife. Ausgerechnet die wichtigsten Ereignisse, consent-revoked und account-deleted, entstehen dadurch, dass ein Benutzer Ihrer App den Zugriff entzieht oder seinen Apple Account löscht. Ihren Handler gegen ein echtes account-deleted zu prüfen, bedeutet, dass jemand einen Apple Account löscht. Das ist kein Test, den man zweimal durchführt.
Der gangbare Ersatz besteht darin, das Problem zu teilen. Bauen Sie die dekodierten Nutzlasten anhand der obigen Formen selbst nach und prüfen Sie damit im Unit-Test die Verzweigung, die Idempotenz und die Abgleichslogik. Testen Sie den Transport- und Signaturpfad getrennt davon mit einer echten Benachrichtigung, die Sie tatsächlich erzeugen können: Die Zustimmung für einen Apple-Test-Account zu widerrufen, lässt sich rückgängig machen – eine Löschung nicht –, und dabei werden das Auspacken der Hülle, die kid-Suche und die Signaturprüfung von Anfang bis Ende durchlaufen.
Was auch immer Sie tun: Vergewissern Sie sich vor der Registrierung, dass der Endpunkt erreichbar ist und zügig antwortet. Ein registrierter, nie überprüfter Endpunkt ist der Grund, warum ein Team Monate später feststellt, dass jede Benachrichtigung seit dem Start an eine URL hinter einem abgelaufenen Zertifikat ging.
Eine Anforderung, die bereits gilt
Der Grund, warum das überhaupt in den Entwicklernachrichten auftauchte: Seit dem 1. Januar 2026 müssen Entwickler mit Sitz in der Republik Korea beim Registrieren einer neuen Services-ID oder beim Aktualisieren einer bestehenden einen Endpunkt für Server-zu-Server-Benachrichtigungen angeben, um eine Website mit einer App über Sign in with Apple zu verknüpfen.3 Apple kündigte das am 9. Oktober 2025 an.
Die Anforderung ist eng gefasst, und sie gilt seit Monaten, statt etwas zu sein, worauf man sich noch vorbereiten müsste. Interessant ist ihre Richtung. Apple hat begonnen, den Endpunkt in mindestens einer Rechtsordnung verpflichtend zu machen, aus Gründen, die sich verallgemeinern lassen: Menschen die Kontrolle über die von ihnen geteilten persönlichen Daten zu geben und dafür zu sorgen, dass eine Kontolöschung tatsächlich durchschlägt. An dieser Logik ist nichts spezifisch koreanisch.
Wenn Sie den Endpunkt ohnehin bauen, bauen Sie ihn, bevor eine Aufsichtsbehörde daraus Ihre Frist macht.
Die wichtigsten Erkenntnisse
Für Backend-Entwickler:
- Behandeln Sie vier Typen, nicht drei. email-enabled und email-disabled treffen getrennt ein.
- Parsen Sie den JSON-Body und extrahieren Sie payload, bevor Sie irgendetwas an einen JWS-Verifier übergeben.
- Validieren Sie die Signatur mit dem Algorithmus aus dem alg-Parameter des Headers, bevor Sie den events-Claim lesen.
- Machen Sie Handler idempotent und gleichen Sie gegen Ihre eigenen Datensätze ab. Apple dokumentiert keine Zustellgarantien.
Für iOS-Teams:
- consent-revoked macht Anmeldedaten ungültig. Behandeln Sie es als sitzungsbeendendes Authentifizierungsereignis, nicht als Aktualisierung einer Einstellung.
- Native Apps erhalten keinen clientseitigen Callback bei der Löschung eines Apple Accounts. Ohne Endpunkt erfahren Sie nie davon.
Für alle, die abwägen, ob sich der Aufwand lohnt: - Für Entwickler mit Sitz in Korea ist der Endpunkt seit Januar 2026 bereits verpflichtend, und die Begründung lässt sich verallgemeinern.
FAQ
Wie viele Benachrichtigungstypen gibt es?
Vier: email-enabled, email-disabled, consent-revoked und account-deleted.1 Apples Ankündigung in den Entwicklernachrichten beschreibt drei und fasst die beiden E-Mail-Ereignisse zu einem Stichpunkt über Weiterleitungseinstellungen zusammen.3
Was bedeutet consent-revoked für meine Sitzung?
Die Anmeldedaten des Benutzers werden ungültig.1 Behandeln Sie es wie eine widerrufene OAuth-Berechtigung: Beenden Sie die Sitzung und schicken Sie den Benutzer zur erneuten Authentifizierung, statt eine Einstellung zu aktualisieren und weiterzumachen.
Brauche ich einen Endpunkt, wenn ich nur eine native iOS-App ausliefere?
Apple hält fest, dass das System bei nativen Apps keinen clientseitigen Callback sendet, wenn ein Apple Account dauerhaft gelöscht wird.1 Ohne einen Server-zu-Server-Endpunkt gibt es keinen Mechanismus, der Sie darüber informiert.
Was soll mein Endpunkt zurückgeben, und was, wenn er ausfällt?
Apple dokumentiert weder Erwartungen an Statuscodes noch das Verhalten bei Wiederholungen oder ob es zu doppelten Zustellungen kommt. Entwerfen Sie für eine Zustellung mindestens einmal oder womöglich höchstens einmal, machen Sie Handler idempotent und gleichen Sie gegen Ihre eigenen Datensätze ab, statt anzunehmen, dass jede Benachrichtigung angekommen ist.
Kann ein Endpunkt mehrere Apps bedienen?
Apples Dokumentation sagt, dass Sie dieselbe URL für mehrere Entwicklerteams und Apps verwenden dürfen.1 Registriert wird eine URL pro Sign-in-with-Apple-App-Gruppierung und Schlüssel, auf einer primären App-ID.2 Ein gemeinsam genutzter Dienst funktioniert, sofern Ihr Handler bestimmen kann, welche App die jeweilige Benachrichtigung betrifft.
Quellen
-
Apple, “Processing changes for Sign in with Apple accounts.” Quelle für die vier Ereignistypen (
email-enabled,email-disabled,consent-revoked,account-deleted), das JWS-Nutzlastformat und die Anweisung, mit demalg-Parameter des Headers zu validieren, die Umhüllung{"payload": "<JWS>"}, die Aussage, dass widerrufene Zustimmung Anmeldedaten ungültig macht, den Hinweis, dass native Apps bei einer Kontolöschung keinen clientseitigen Callback erhalten, die Serveranforderung TLS 1.2 sowie die Erlaubnis, eine URL für mehrere Teams und Apps zu verwenden. Abgerufen am 2. August 2026. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, “Enabling server-to-server notifications.” Quelle für den Registrierungsweg über Certificates, Identifiers & Profiles, die Regel einer URL pro App-Gruppierung und Schlüssel, die Beschränkung auf die primäre App-ID, die Anforderung eines absoluten URI und die TLS-1.2-Anforderung. Abgerufen am 2. August 2026. ↩↩↩
-
Apple Developer News, “New requirement for apps using Sign in with Apple for account creation,” 9. Oktober 2025. Quelle für die ab dem 1. Januar 2026 geltende Korea-Anforderung und für die dreiteilige Zusammenfassung dessen, was der Endpunkt empfängt. ↩↩↩