This week at Apple’s developer conference WWDC, Apple announced a new feature, “Sign In with Apple” enabling users to sign in to apps using their Apple ID. This new feature is positioned as a secure and privacy-friendly way for users to create an account in apps. Most iOS and Mac users already have an Apple ID, and this new feature lets them use that Apple ID to sign in to other apps and websites.
If this sounds a lot like “Sign in with Facebook” or “Sign in with Twitter”, that’s because it is! This is Apple’s way of providing a way for users to sign in to apps without having to rely on an external identity provider like Facebook or Twitter.
Apple is taking a firm stance to protect user’s privacy with this new feature. Rather than letting apps see the user’s real email address, they will provide the app with a proxy email address unique to each app. App developers will still be able to send emails to the proxy addresses, it just means developers won’t be able to use the email addresses to correlate users between apps, and it also means users can shut off email forwarding per app.
The Sign In with Apple Flow
Part 1: Official iPhone Unlock. Official iPhone Unlock is more of a service than a tool, but it works much the same way as the tools we have already seen. The iCloud Activation and lock removal service offered here is one of the best in the business, and since it is a service, there is very little chance of something going wrong. Screwdriver Set for MacBook, Tri-Wing Phillips Pentalobe 5 Pentalobe T5 Pentalobe T6 Screwdrivers Repair Tool Kit for Apple MacBook Mac Retina Pro Air 4.5 out of 5 stars 4,267 $6.99 $ 6. Questions about Apple Service Toolkit and issues with El Capitan. For those of you that work at an Apple Authorized Service Provider, I was wondering if you have experienced any issues with OS X El Capitan and Apple Service Toolkit/Apple Service Diagnostics.
Let’s take a look at how this new flow works using your Apple ID to sign in to a website.
It starts with a button in the app or website labeled “Sign In with Apple”.
Elcomsoft iOS Forensic Toolkit 7.0 brings low-level extraction support for the latest generation of Apple devices based on the A14 Bionic. This includes the entire range of iPhone 12 models running all versions of iOS 14 from the original iOS 14.0 all the way through iOS 14.3.
Clicking on this button will take the user to the Apple sign-in screen.
If the user has two-factor authentication enabled then they will have to confirm this login from another device.
And enter the two-factor auth code.
Finally, the user will see a prompt confirming that they want to sign in to this application using their Apple ID.
Clicking “Continue” will take the user back to the app where they will be signed in!
How Sign In with Apple Works (Hint: it uses OAuth and OIDC)
Thankfully, Apple adopted the existing open standards OAuth 2.0 and OpenID Connect to use as the foundation for their new API. While they don’t explicitly call out OAuth or OIDC in their documentation, they use all the same terminology and API calls. This means if you’re familiar with these technologies, you should have no trouble using Sign in with Apple right away!
Let’s walk through building a short sample application that can leverage Apple’s new API to sign users in. You should be able to translate this code to your language of choice relatively easily once you see the requests being made.
The hardest part of this whole process is registering an application in the Apple Developer Portal. Typically, OAuth providers have a relatively straightforward process to register a new application which will give you a client ID and secret. But since Apple’s API is tied to their whole iOS app ecosystem, it’s a bit more complicated. They’ve also opted to use a public/private key client authentication method rather than a traditional client secret. But don’t worry, we’ll go through this step by step.
At the end of this process, you’ll end up with a registered client_id
(which they call a Service ID), a private key downloaded as a file, and you’ll verify a domain and set up a redirect URL for the app.
First, sign in to the Apple Developer Portal and click on Certificates, Identifiers and Profiles.
Create an App ID
From the sidebar, choose Identifiers then click the blue plus icon.
Choose App IDs in this first step.
In the next screen, you’ll choose a description and Bundle ID for the App ID. The description isn’t too important, but type something descriptive. The Bundle ID is best when it’s a reverse-dns style string.
In this example, I’m using lol.avocado
since the domain this app will be running on is avocado.lol
.
You’ll also want to scroll down through the list of capabilities and check the box next to Sign In with Apple.
Go ahead and confirm this step.
Create a Services ID
The App ID in the previous step is a sort of way to collect things about this app. It seems redundant when you’re only making a simple web app login experience like this example, but it makes more sense once you have a native app and web app that you want to tie together under a single login experience.
The Services ID will identify the particular instance of your app, and is used as the OAuth client_id
.
Go ahead and create a new identifier and choose Services IDs.
In the next step, you’ll define the name of the app that the user will see during the login flow, as well as define the identifier which becomes the OAuth client_id
. Make sure to also check the Sign In with Apple checkbox.
You’ll also need to click the Configure button next to Sign In with Apple in this step. This is where you’ll define the domain your app is running on, as well as define the redirect URLs used during the OAuth flow.
Make sure your associated App ID is chosen as the Primary App ID. (If this is the first App ID you’ve made that uses Sign In with Apple, then it will probably already be selected.)
Enter the domain name your app will eventually be running at, and enter the redirect URL for your app as well. If you want to follow along with this blog post, then you can use the placeholder redirect URL https://example-app.com/redirect
which will catch the redirect so you can see the authorization code returned.
It’s worth noting that Apple doesn’t allow localhost
URLs in this step, and if you enter an IP address like 127.0.0.1
it will fail later in the flow. You have to use a real domain here.
Go ahead and click Save and then Continue and Register until this step is all confirmed.
Ok! At this point, you now have an App ID container to hold everything, and you’ve created a Services ID which you’ll use as your OAuth client_id
. The Identifier you entered for your Services ID is your OAuth client_id
. In my example, that is lol.avocado.client
.
Create a Private Key for Client Authentication
Rather than using simple strings as OAuth client secrets, Apple has decided to use a public/private key pair, where the client secret is actually a signed JWT. This next step involves registering a new private key with Apple.
Back in the main Certificates, Identifiers & Profiles screen, choose Keys from the side navigation.
Click the blue plus icon to register a new key. Give your key a name, and check the Sign In with Apple checkbox.
Click the Configure button and select your primary App ID you created earlier.
Apple will generate a new private key for you and let you download it only once. Make sure you save this file, because you won’t be able to get it back again later! The file you download will end in .p8
, but it’s just text inside, so rename it to key.txt
so that it’s easier to use in the next steps.
Lastly, go back and view the key information to find your Key ID which you’ll need in the next step.
Alright, that was a lot, but we are now ready to start writing some code! If you ran into any trouble, try checking out Apple’s documentation in case anything has changed since the publication of this blog post.
Generate the Client Secret
Rather than static client secrets, Apple requires that you derive a client secret yourself from your private key. They use the JWT standard for this, using an elliptic curve algorithm with a P-256 curve and SHA256 hash. In other words, they use the ES256 JWT algorithm. Some JWT libraries don’t support elliptic curve methods, so make sure yours does before you start trying this out.
The Ruby JWT library supports this algorithm, so we’ll use that to generate the secret.
First, make sure you’ve got Ruby installed, and then install the JWT gem by running this from the command line:
Now the jwt
gem should be available for use. Fill in the missing values at the top of this file, and save it as client_secret.rb
. You should already have the client_id
value from the previous step. You’ll also need your Apple Team ID. This is displayed in a few places, but the most convenient is in the top right corner of the screen. Use the Key ID you found in the previous step.
client_secret.rb
This code generates a JWT using the ES256 algorithm which includes a handful of claims. This JWT expires in 6 months, which is the maximum lifetime Apple will allow. If you’re generating a new client secret JWT every time a user authenticates, then you should use a much shorter expiration date, but this allows us to generate the secret once and use it in our sample apps easily.
Now you can run this from the command line and it will output a JWT.
This is described in Apple’s documentation Generate and validate tokens.
Begin the OAuth 2.0 Flow
Now that we’ve got a client secret, we can actually begin the OAuth flow! For this step, we’ll create a simple PHP file that will handle both the beginning of the flow as well as the redirect step.
Go ahead and create a file called index.php
with the following contents, filling in the values for your own application.
index.php
Now you can start building the URL that the user will go visit to start the flow. First, you’ll generate a random state
value and save it in the session. Then you’ll use your app’s configured values to create a URL that the user can visit. These parameters should all look familiar to you if you’ve used OAuth or OpenID Connect before.
I couldn’t find any documentation on which URL to use as the authorization endpoint, or even whether these were the right parameters, but thankfully the rest of the API looked like OAuth so I was able to figure it out despite the missing docs.
Now you can create a link the user can click to log in.
Of course, you should probably use the recommended button provided by Apple so that it looks a bit nicer than this text link, but this is good enough for now.
You can run this app now using the built-in PHP web server!
Once that’s running, you can visit http://127.0.0.1:8080
in your browser and you should see your sign-in link!
Handle the OAuth Redirect
Before you click Sign In, you should set up the mechanism for handling the redirect back from Apple.
Add the following code to index.php
above the line $_SESSION['state'] = bin2hex(random_bytes(5));
.
This is a handful of code, so let’s walk through it.
if(isset($_POST['code']))
First, we check for the presence of the authorization code in the query string which indicates that Apple has completed the initial step and sent the user back to the app.
Next, we verify the state
parameter matches the one we set at the beginning. This is to protect your app against CSRF attacks.
Then we’re ready to exchange the authorization code for an access token. Again this will look familiar if you’ve written OAuth code before.
This request is actually documented on Apple’s website, although as of this writing, their docs have a couple of typos. The typos make it look like Apple has done something non-standard with the parameter names, but in reality, the OAuth parameters work properly.
This uses a helper function http()
that we need to define, so go ahead and add the following to the bottom of your index.php
file.
One particularly tricky thing I noticed while testing this is that Apple requires the presence of a User-Agent
header in the HTTP request, otherwise, it returns a 403
error and an HTML web page. This is not documented in their API, because I’m guessing this check happens at their API gateways before the request hits the real backend server.
At this point, assuming everything worked, you can output the response from this and see what Apple has returned!
Let’s try this out for reals now.
Try Sign In with Apple Now!
Click the “Sign In with Apple” button, and you’ll be taken to the Apple sign-in screen.
You should see the name of your app and a placeholder icon. Enter your Apple ID and password.
If your Apple ID has two-factor auth enabled, you’ll be prompted for that as well.
It does seem to prompt for two-factor auth every time you log in to an app, which is definitely secure, but can be a little frustrating especially while testing.
Once you confirm that, you’ll see a screen asking if you would like to continue signing in to the app.
Note: You will only see this permissions screen the very first time you log in with this App ID. In subsequent logins, you’ll see a confirmation prompt like the below.
If you want to reset this so that you see the permissions screen and get the name and email back from Apple, visit your Apple ID App Security page and revoke your application.
Now click Continue and you’ll be redirected to your redirect URL! If you entered the placeholder https://example-app.com/redirect
URL then you’ll see a message like this.
Apple Service Toolkit 2
Since Apple sends a POST request to the redirect URL, you’ll have to actually set up a redirect handler yourself to catch the values, since the authorization code will likely expire before you can copy it out of this debug screen.
Once you’ve set up the sample code above and registered your own redirect URL, your app will exchange the authorization code for an access token and ID token, and will show the output on the screen!
The last step to getting the user’s info is to decode the ID token. Since in this example we used response_type=code
to get the ID token, the ID token was obtained via the back channel, which means we don’t need to worry about validating the JWT signature of the ID token. We can just parse out the middle claims section and read the data directly.
Apple Service Toolkit
The sub
value in the claims is the unique identifier for the user. It’s notable that this value doesn’t mean anything in particular, which is Apple’s way of preserving user privacy. You can store this value in your own database now, and use it to determine whether the same user logged back in a second time.
You can also find the user’s email or proxy email in the claims as well.
Note: Apple will send the user’s name and email in the form post response back to your redirect URL. You should not treat these values as authoritative, because like the OAuth Implicit flow, the data cannot be safely trusted at this point. Unfortunately Apple does not return the user’s name in the ID token where it would be safe to trust. Thankfully they do return the user’s email address that way, so that’s where you should get it from.
You can find the complete code from this tutorial on GitHub!
Changelog
- August 23, 2019: Updates to the sample code and blog post to adapt to Apple’s new requirement of using the
response_mode=form_post
parameter, as well as details on getting the user’s name and email address. okta.github.io#3022. - Jun 7, 2019: Updates to the sample code based on some new implementation experience. Added screenshot of the permissions screen asking to hide the email address. You can see the changes to this article in okta.github.io#2924.
- Jun 10, 2019: Fixed typo in
client_secret.rb
Learn More about OAuth 2.0 and OpenID Connect
Apple Service Toolkit 2 0 Download
If you’d like to learn more about OAuth, check out the links below!
If you have any questions about this post, please add a comment below. For more awesome content, follow @oktadev or subscribe to our YouTube channel. If you’re interested in other security-focused articles like this, please check out our new security site as well.