Sign in with Apple Server to Server Notification Documentation

Is there any documentation about the server to server notification, specifically what sort of data Apple servers send to our server when there is an update to users who used Sign in with Apple?
Post not yet marked as solved Up vote post of marpies Down vote post of marpies
5.5k views

Replies

Curious to see this documentation as well - very surprised that the docs are non-existent. Even the learn more button in the endpoint section of configuration of signin with apple links to documentation without any mention of the endpoint.

This video has some info, but not enough. Would love a documentation page equivalent: https://developer.apple.com/videos/play/wwdc2020/10173/.
Just adding my agreement -- please provide documentation for server-to-server notifications. Thank you.
I am also searching for more information about this...
I assume that this could/should be used to remove users from firebase (and our own database) if they decide to stop using their apple id to sign into our app.
This is not happening automatically and might cause a strange user experience if the user comes back to the app and is still logged in (or able to login).
I have this working on a test server now. See my notes here: https://stackoverflow.com/questions/58178187/apple-sign-in-how-to-use-it-for-custom-server-endpoint-authentication/62604213#62604213

It would still be nice to have some Apple docs reflecting this. Feels like I'm kinda operating in the blind.
I've spent some time digging into how this works. Here's what I've found so far:

Endpoint

The endpoint for the notifications can have any path desired.  It's a POST request sending JSON.  HTTPS is required.  There is no authentication.  Events are sent as signed JWTs using the OIDC JWS keys for verification.
 

Body

The body is a simple json structure:
Code Block
{ "payload": "<event Token>" }

Event Token Structure

Code Block
{
 "iss": "https://appleid.apple.com",
 "aud": "<app id>",
 "exp": 1611852260,
 "iat": 1611765860,
 "jti": "<token id string>",
 "events": "<Event String>"
}


The aud field will be the corresponding backend App ID for the source of the emission.

The events field is stringified json.  So far I've only seen a single object, but based on the name an array doesn't seem impossible.

General Event Structure

Code Block
{
"type": "<event type>",
"sub": "<apple user id>",
"event_time": 1611765847700
}

Some event types have additional fields.

Event Types

Here are the events I've received so far.

email-enabled
Sent when user enabled receiving email from service.
Additional Fields:
  • email - user's email

  • is_private_email - is the email an apple email proxy email.

email-disabled
Sent when a user disables email sending through the proxy.
Additional Fields:
  • email - user's email

  • is_private_email - is the email an apple email proxy email.

consent-revoked
Sent when consent for using the application is revoked.
No additional fields.
The Server-to-Server Notification responses are documented here—

Processing Changes for Sign in with Apple Accounts

We are also facing the same issues while trying to integrate.

The documentation leaves out too many questions unanswered and we have to rely on the community and third-party information in order to implement anything to start with and yet, a lot of try-n-error will be coming up most likely. Simply the fact that we have to search for answers outside of Apple's documentation has already made us 3 times the time we originally had planned for this integration.

The following is a review of the current documentation at Processing Changes for Sign in with Apple Accounts, respecting all pages it links to.

Overview

These notifications are JSON Web Tokens (JWTs) ...

This is wrong. The content seems to be a JSON object with only one field named payload which contains the actual JWT.

Set Up the Endpoint URL

All JWTs arrive at the same endpoint URL.

What exactly is meant by "all" here?

The following information from Enabling Server to Server Notifications may have an impact here:

A Server to Server endpoint URL can only be registered on a primary App ID.

Combining those two, my interpretation of the sentence would be:

Notifications for all apps within the same App Group, identified by the primary App ID, arrive at the same endpoint URL.

That would clarify it a lot from the beginning, including resulting limitations.

Also:

Your server examines the JWT and performs work according to the type in the events array in the token.

What do you mean by "examine"? I am pretty sure you're talking about Validating a JWT, but it would be really helpful to use industry standards terminology to avoid uncertainty at the readers side.

Additionally, the events is not an array, so that is misleading information here.

Open Questions:

  1. Which HTTP method will be used by Apple? Community says it is POST.
  2. Which concrete structure does the request body have?
  3. How stable is the request body structure?
  4. Which values will request header Content-Type have?
  5. Will request header Content-Length be correctly set?
  6. How large can the request body get?
  7. Is there a list of IPv4/v6 addresses or CIDRs available for allow-listing Apple servers as valid clients to this endpoint?
  8. Can Apples clients be reliably reverse-resolved via DNS to allow maintaining a dynamic allow-list?
  9. Which requirements need to be met by the endpoint response?
    • Which HTTP status codes are respected by Apple?
    • How does it behave in case of a 503 or other 5xx status codes?
    • Does it follow redirects?
    • What is the timeout for a request and how does Apple react in case of exceeding it?
  10. Is there any chance that Apple will refuse to send notifications to a specific endpoint URL and what are possible reasons?
  11. From where to get public keys for signature validation?

Would also be nice if there was some reference to the fact that AppleID (at least at the moment) conforms to the OpenID Provider Configuration (see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig - fun fact: this forum blocks URL's for openid.net, maybe because of previous debates) by providing the following well-known location:

That tells us the location of the JWK Set for getting the public keys as well.

Examples

In section Process a User’s Email Forwarding Change we can see the following example (note the JSON syntax bug - I hope we won't see such things in real life):

{
    "iss": "https://appleid.apple.com",
    "aud": "com.mytest.app",
    "iat": 1508184845,
    "jti": "abede...67890",
    "events": [
        {
            "type": "email-enabled",
            "sub": "820417.faa325acbc78e1be1668ba852d492d8a.0219",
            "email": "ep9ks2tnph@privaterelay.appleid.com",
            "is_private_email": "true"
            "event_time": 1508184845
        }
    ]
}

Now, according to the example that @MikeNV provided and from other community sources that is simply wrong, since the events claim is a string and not a JSON array.

Open Questions:

  1. Will claim iss always be https://appleid.apple.com?
  2. Is claim aud an App ID? If so:
    • Will it contain the concrete App ID?
    • Will it contain the primary App ID? If so:
      • How do we determine the specific app related to the notification?
  3. If events is a string, how to parse it correctly?
    • Is it just plain serialized JSON?
    • Is it base64 (URL-safe) encoded JSON?
    • Is the decoded JSON guaranteed to be a single object?
  4. Which fields will events contain and what do they mean (apart from the incomplete examples provided)?
  5. Which values can the event type have and what do they refer to?

The situation is even worse.

On https://developer.apple.com/documentation/sign_in_with_apple/processing_changes_for_sign_in_with_apple_accounts Apple is stating, that "account-delete" is sent as a type in case a user deleted his or her apple id. The event type we actually saw in production was "account-deleted".

While we of course can't be sure if it is actually a typo, or some additional undocumented event type, I assume that this is a typo in the documentation.