[MUSIC PLAYING]
PETER FRIESE: Hey, everyone.
This is Peter from the Firebase team.
In this video, I'm going to show you
how to implement Sign in with Apple using Firebase
Auth in your app on iOS.
This video assumes that the basics about Firebase
Authentication already.
If you'd like to get a refresher,
I recommend you watch my introductory video first
and then come back here.
At the end of this video, you will
know how Sign in with Apple works,
how you can connect Sign in with Apple to your Firebase project,
how to handle the sign in flow in your iOS app, what
a nonce is, and how it helps us to protect the sign in flow
against a replay attack, how to update the user's
account in Firebase with the information
you receive from Apple, and how to sign out.
That's quite a bit to get through, so let's get started.
To make it easier to understand how all of this
works I've created a sample app, which
you can download from GitHub to help you get started.
The app itself isn't too exciting.
It's just a simple app that lets users
choose their favorite number.
And at the moment, I store this information
locally in user defaults, but later I'd
like to store this data in the Cloud,
so users can retrieve this information
on all of their other devices or even
share it with their friends.
My goal is to use Cloud Filestore
to manage the user's data.
But before I can do that, users need
to sign in so I know who they are,
and so I can store the correct information
for each individual user.
Before you can use Firebase Authentication,
you need to add the Firebase SDK to your app.
I've already done this for my sample app,
but if you'd like to know how I did that,
check out this other video in which I walk you
through the steps of setting up a Firebase project using
Swift Package Manager.
The next step is to add the Firebase Auth library
to all the targets in which I want
to use Firebase Authentication.
However, since I already did that in the previous video
about Firebase Auth, I don't need to do this again.
And the same applies to the next step,
to use Firebase authentication, we
need to activate it in the Firebase Console.
But as you can see I already did this for my project,
so no need to do it again.
However, I do need to enable Sign
in with Apple in the list of authentication
providers for my app.
You can enable and use as many authentication providers
in your app as you. like.
By doing so, you allow users to sign
in using their preferred authentication mechanism.
Some users might prefer signing in with email and password,
while others might prefer Google sign in or sign in
with Apple as this gives them this really smooth
one-tap login experience.
This also allows you to let users link
multiple sign-in mechanisms.
For example, if your users signed
in using email and password, but later would
like to connect their Apple ID to your app,
that's possible if you implement account linking.
Account linking is an important topic
that we will look at in more detail in one
of the next videos.
In this video, though, I want to show you
how to implement Sign in with Apple, so let me go ahead
and activate the Sign in with Apple authentication provider
for my project.
One thing you need to keep in mind-- if you
use any other OAuth-based authentication mechanism,
such as Google sign in or Facebook login,
Apple's App Store review guidelines
require you to also implement Sign in with Apple.
There are some exceptions to this rule,
so I recommend you check out the current version of the App
Store review guidelines to be on the safe side.
By the way, if you forget to enable a provider before using
it, Firebase Auth will throw an error message
when you try to sign in.
That's one of the reasons why you
want to implement some error handling in your app.
Proper error handling will make it a lot easier
to figure out what's wrong when things
aren't working as expected.
To make development easier, I recommend
using the Firebase Emulator.
You can think of the Firebase Emulator Suite
as your own personal instance of Firebase
running on your local machine.
Since everything runs locally, you
don't run the risk of messing up your production data,
and you also avoid stepping on each other's toes
when you're sharing a development
project with your teammates.
Another benefit is that once you've installed everything,
the Emulator Suite also works when you're offline
or on a slow network.
I have already installed and configured
the Firebase Emulator Suite in the previous video,
so feel free to check this out if you'd
like to see how this is done.
I'll start the emulator using the following command line.
This tells the emulator to persist
any user accounts we create and later restore them
the next time I run the same command.
In fact, I've previously created a user account,
and as you can see, the emulator has restored this user
from disk.
This saves quite a bit of time, especially
if you have more than just a few test accounts.
To connect your app to the Emulator Suite,
add the following code to your Firebase setup code.
Now, with all this preliminary work out of the way,
I can start implementing Sign in with Apple in my app.
The first thing I want to do is add a Sign in with Apple button
to my login view.
In iOS 14, Apple introduced a SwiftUI version of the Sign
in with Apple button, which makes our lives a lot easier.
Under the hood, this button will use Apple's ASAuthorization
Apple ID provider to handle the sign in with Apple Flow.
The button has three parameters.
The first one is for setting the correct label,
and you can choose between Sign in with Apple,
Continue with Apple, and Sign up with Apple.
The default for the first parameter is sign in,
and since this is what we want to use,
I will just use the default value, which means
I can drop the first parameter.
The second parameter is a closure
that will be called for configuring
the request that the button sends
to Apple's ASAuthorization Apple ID provider.
And the third parameter is a callback
that Apple would call once the authentication request has
completed, either successfully or with an error.
To keep my view code nice and clean,
I will not implement these callbacks here.
Instead, I've created two additional methods on the view
model that I can call.
I have named them handleSignInWithAppleRequest
and handleSignInWithAppleCompletion.
And as a final step, I will tweak
the design of the button a bit.
So first, I will make sure that the button
looks great in both dark and light mode.
And I also think it's a bit small in comparison
to the Login button, so I will use the frame view modifier
to increase the height.
And finally, I will adjust the corner radius
so both buttons use the same corner radius.
With the UI in place, I can now navigate to the view model
to implement the two methods that the Sign in with Apple
button calls.
First, I will implement handleSignInWithAppleRequest.
It receives an instance of ASAuthorization Apple ID
requests that I can adjust to meet the needs of my app.
So in my application, I want to be
able to address the user with their full name,
and I also want to be able to send them emails.
You should be aware that Apple is
very specific about requesting as little information
about the user as possible.
If you don't need the user's email address,
you shouldn't request it.
Second, I will add a nonce to their request.
Now, you might be wondering what a nonce is.
Well, it is a random number that is
used to prevent replay attacks.
I'll demonstrate how this works once I've finished
implementing the entire flow.
To compute the nonce, I make use of a function called
randomNonceString that I copied from the Firebase documentation
for Sign in with Apple.
To make sure an attacker cannot steal a nonce,
I will then compute the SHA256 hash of the nonce.
SHA256 is a hash function that takes
a string of arbitrary length and outputs
a value that is 256 bits long.
So no matter if I hash a single character
or an entire Wikipedia article, the output
will always be 256 bits.
It is virtually impossible to invert the hash back
to the original form, and this is
why SHA256 is a popular algorithm
for storing passwords.
When using a hash function like SHA256,
the only way to verify two passwords
are the same is to compute the hash for both passwords,
then see if the hashes match.
Sign in with Apple button will take this request
and pass it to ASAuthorization Apple ID provider
to start the sign in flow.
Once the user has finished the flow by using Face ID or Touch
ID to authenticate themselves, we
will receive a result type in the
handleSignWithAppleCompletion callback.
So I'll first check if the request has failed.
This might happen, for example, when the user
aborts the sign-in process.
OK, now, let's see how we can handle this success case.
I'll use if case let to extract the result value, which
in this case is of type ASAuthorization.
ASAuthorization has two properties--
provider and credential.
We are interested in credentials of type ASAuthorization Apple
ID credential, so I'll perform a type check
using the conditional type cost operator.
By doing this, the compiler will perform a type cast
and assign the result to Apple ID credential.
This Apple ID credential contains the ID token
for the user that has just signed in.
Before I can go ahead and exchange this ID
token for a Firebase ID token, I will perform a quick check
to see if the current nonce is set.
This is to make sure that it was actually our app that
initiated the sign-in flow.
Once this is established, I will extract the ID token
and then turn it into a string.
In the next step, I use Firebase's or OAuth provider
to create a credential.
This credential contains the name of the provider, apple.com
in this case, the ID token we just received,
and the raw un-hashed nonce.
And now the moment has come to actually perform the sign in.
I will use async/await to call this API,
so I'll need to wrap the call inside a task.
Also, since the call can throw an error,
I will wrap it inside a do/try catch block.
And now I can call Auth.auth.signIn
with credential with the credential I've just created.
This call will create a Firebase user
based on the credential I provide.
So now that we've got everything in place, I will run the app
and show you how the nonce is being
used to protect the flow against replay attacks.
When I tap the Sign in with Apple button,
the handleSignInWithAppleRequest method is called.
And here we can see the nonce and the hash nonce.
So let's continue the flow.
Back in the simulator, I now need to sign in.
If you're surprised that I have to present my Apple ID
password, this is because the iOS simulator
doesn't support biometric authentication at the moment.
So once I've entered my password,
handleSignInwithAppleCompletion will be called.
Everything went well.
So we will jump into the success branch of this statement.
And now let's look at the ID token.
This is a JWT, so I can now copy the value and inspect it,
for example, using a tool like JWT.io.
On the decoded side, I can see a bunch of interesting fields.
For example, I can see that Apple actually
is the issuer of this ID token, and that my app is
the audience for it.
I can also see when the token was issued
and when it will expire.
And down here, we can see the hash nonce.
So let's make a mental note of this value.
And then, back in Xcode, the next instruction
will call Firebase's Auth provider
to create the credential that we will then
parse into the sign in method.
Note that we include the raw nonce in this credential.
Now, once we call the sign-in method,
Firebase will extract a nonce from the ID token.
It's essentially doing what we just did using the JWT.io tool.
Then Firebase will take the raw nonce, that is this value,
and compute its SHA256 hash.
It will then compare that value with the one from the ID token.
So let's quickly jump back into the browser and do that, too.
And there you have it.
Both values match.
Since the nonce has a different value each time the user
signs in, this process effectively
prevents replay attacks.
So let's resume execution, and voila, I'm signed in.
You might notice that Firebase doesn't automatically
transfer the user's full name from the Apple ID token
to the Firebase user.
The reason for this is that Apple
wants to make sure that developers take the user's
privacy seriously.
This is why they only sent the user's full name the first time
the user signs into your app.
For all following sign ins, Apple
will not fill out the full name field of the ID token.
At Firebase, we decided to not automatically transfer
the user's full name when they first
sign into your application, but instead leave it to developers
to implement this themselves.
This way you, as the developer of your app,
can make sure that you get the user's consent
to use their full name and link it to any other personally
identifiable information that you
might store about this user.
This approach also makes sure we don't accidentally
overwrite any display name that you previously set.
OK, so let me show you how you can
read the user's full name from the Apple ID token
and assign it to the Firebase user.
I am first going to call this method I defined earlier,
updateDisplayName.
It takes two parameters, the Firebase user we want to update
and the Apple ID credential that we want
to read the full name from.
Insert the method.
You can see that I will first make
sure the display name on the Firebase user
is empty to avoid accidentally overwriting it.
I will then create a profile change request
to modify the Firebase user.
Next, I will extract the first and last name
of the user from the Apple ID credential
by using this extension method I created on ASAuthorization
Apple ID credential.
I will then commit any changes to Firebase Auth, which will
write them back to Firebase.
And finally, I will update the display name on the view model.
This is because making a change to the user's display name
does not trigger the authorization state
change that we set up earlier.
And this is how you update the username
when you Sign in with Apple.
Signing the user out works just like for all the other Firebase
Authentication providers.
A call to Auth.auth.signOut is all
that it takes to sign the user locally.
Firebase will remove the cached ID token from the keychain,
so the next time the user starts the app,
they will have to sign in again.
Keep in mind that you will have to reset
your app into an unauthorized state after a call to sign out.
The easiest way to do this is to use the authentication state
listener and reset the UI when you received nil as the user.
As you can see in my implementation,
it will reset the UI and authentication
state when it receives nil.
And there you have it, signing in with Apple
using Firebase Authentication.
I think Sign in with Apple is a great way
to let users sign into your app.
It's much more convenient than email and password
authentication, as it makes use of the biometric features
of the user's phone.
All they have to do is to look at their phone
to unlock it with Face ID, or put
their finger on the fingerprint sensor to use Touch ID.
Using a Federated identity two provider, like Sign
in with Apple, relieves users of the burden
to come up with strong passwords for every single app
they want to sign into.
Plus, Sign in with Apple requires
that users turn on second-factor authentication for their Apple
ID, so it is much more secure.
If you'd like to take a look at the source code for the sample
app, you can do so, because it's available on GitHub.
If you find this video useful, give me a thumbs up.
And don't forget to subscribe to our YouTube channel,
as we've got a bunch of other videos coming up
about the other Firebase Authentication providers.
Stay safe.
And I will see you in the next one.
[MUSIC PLAYING]