“Sign in with Apple” lets users log in to different apps and services using their Apple account. I document a few nuances and subtleties that I noticed in the course of integrating Apple authentication provider into a web app.
If your app lets a user sign in using any third-party service, then you must add Apple Login to the available options.
On September 12, 2019, Apple introduced new guidelines for “Sign In With Apple”. It says, in the fine print:
Apps that use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.
Apple also announced the cutoff dates on which it will start enforcing this requirement.
New apps must follow guideline 4.8 and Human Interface Guidelines on Sign in with Apple starting April 30, 2020. App updates must follow these guidelines starting June 30, 2020.
You must have an Apple Developer Program account to offer Apple login service in your app.
You need Apple developer console to create an App ID for your app. Similarly, you need to register a
client_id and redirect URL for the app under the same App ID in the Apple developer console.
In the production, you will have a public redirect URL. During development and testing, we almost always use
127.0.0.1. But Apple does not permit localhost URLs.
To quote the documentation,
The host specified in appleid-signin-redirect-uri must include a domain name. It can’t be an IP address or localhost.
An easy workaround is to map
localhost to the registered redirect URL. You can do this by adding an entry to your hosts file.
For example, you set the redirect URL to
myapp.com/auth/apple/redirect in the Apple developer console. Edit your
/etc/hosts file and add this entry.
This mapping will let you develop and test Apple sign in locally, without touching your production server running at
A couple of caveats
You cannot use
.localhost TLD, like
myapp.localhost. It is because
localhost is reserved as a special-use domain.
If you use
.dev TLD like
myapp.dev, then your dev server must support SSL. It is because browsers force
.dev domains to HTTPS.
You can use it:
- to create and customize “Sign in with Apple” buttons,
- to set authorization settings,
- and to launch the authentication process.
The official documentation is available here.
Apple is very particular about how derivate work uses its brand. They have extensive Human Interface Guidelines on how to display and offer Sign in With Apple button.
You can make your life easy, and use Apple JS Framework to create the button automatically.
appleid.auth.js script will automatically replace the
div with an SVG button. You can customize the button using
data- attributes. Read more about it here.
The drawback of this method is that the button will not become visible until the browser fetches, loads and runs the
appleid.auth.js file. On slow networks and browsers, this will make for less than ideal user experience.
The better way is to add branded “Sign in with Apple” button as an image to your assets. You can embed the image with base64 encoding to your HTML. Then the browser will paint the button as soon as it receives the HTML of the login page of your app.
Apple has set up a web service to generate a button for you.
Simply send it GET request or open the link in your browser to download a PNG button. You can customize the button using query parameters. For instance, to download the button with a white background, and a border-radius of 5px, you would use the following URL.
You can read more about the available parameters here.
When you use
appleid.auth.js framework, you have to configure the authorization object using
AppleID.auth.init(). Then call
AppleID.auth.signIn() when user clicks the sign-in button.
In the source code of
appleid.auth.js, you will notice the
response_mode is fixed to
form_post. After Apple processes the authorization request, it sends the result using the HTTP POST method to the redirect URL you have set in the developer console. Therefore, your redirect URL should be a REST endpoint that can receive the POST and process it.
If you want to break away from the
appleid.auth.js framework, you can send authorization request using HTTP GET method to this URL
client_id and other information using query parameters which are documented here.
Focus on query parameter
response_mode. Its valid values are,
If you set the
Remember, the authorization result contains a JWT token, which is why I would not advise you to use
query response mode.
query, a URL fragment is not sent in HTTP request messages. It is not added to Referer header. The server does not receive the fragment. These benefits make
fragment a more secure choice than a
query to receive the result.
You can set the
scope to either name, or email, or both or neither. If you set the scope to name or email, Apple will send you a user object. The catch is, Apple sends user object only once.
To quote the documentation,
Apple only returns the user object the first time the user authorizes the app. Persist this information from your app; subsequent authorization requests won’t contain the user object.
Most apps will use the user information received from Apple. They will use it, for example, to send emails to the users, or to display the user’s name. Consequently, your app should store the user information for reuse.
Apple sends the user object again only if the user removes your app from the authorized apps from his Apple account and then tries to log in to your app again. You can leverage this tidbit to test the login multiple times during the development.
A user logs in using a third-party authentication provider, like Google. The user uses the email address “[email protected]” to log in to your app. User logs in to another instance of your app using Google. Your app will identify it is the same user because the email “[email protected]” matches in both cases.
It is a common scenario. But it does not apply to Apple.
Apple lets your users protect their email address from your app. When they sign in using Apple, Apple asks them if they want to hide their email address. If they do, then Apple sends a unique random email address in place of the actual email address. You can use this anonymous email address to communicate with your user, but you should not use it to identify the user.
Let’s say, a user signs in to your app for the first time using Apple sign in. The user decides to conceal the email address. Apple creates a random email address for her, and sends it to your app, “[email protected]”. The same user removes your app from authenticated apps in her Apple account, and signs in to your app again. This time the user decides to share the email address from you. Apple sends the email address “exam[email protected]”. Now your app has two email addresses,
Both these email address belong to the same user, but your app cannot identify her if you only rely on email addresses to recognize the users.
You should use
sub claim in JWT token for user identification. A
sub claim is a unique identifier for the user, and it does not change when users hide or unhide their email addresses.
email_verified is one of the JWT claims. The documentation says:
A Boolean value that indicates whether the service has verified the email.
But it is returned as a string,
"true", instead of a boolean value,
You can probably ignore it because the doc also mentions,
The value of this claim is always true because the servers only return verified email addresses.
You can send emails to the Apple generated anonymous email addresses of the users. But to do that, you must register your email domain in the Apple developer console.
Only registered email domains can send emails to the user. This feature eliminates spam emails for users who opt anonymous addresses.
https://jwt.io/ is an outstanding tool to parse and validate tokens quickly.
You will fetch Apple’s public key for verifying a token signature from this link. However, for unit testing, you might need to sign tokens using a custom generated key. https://mkjwk.org/ is a simple JSON web key generator to generate random keys for testing.