 If you're an iOS developer, sooner or later, you're going to have to build an app that uses authentication or more simply put, an app that requires users to log in and log out. Now, let me tell you, don't roll your own authentication. It's one of those features that often turns into its own project and there are so many security considerations involved that it's often better to go with the service like Auth0. So in this video, you'll build an app in SwiftUI that implements basic login and log out using Auth0. Now, I've purposely kept the app really, really simple so we can focus on the important part, and that is integrating Auth0 authentication within a SwiftUI app. Before we begin building the app, let's look at the final result. Now, I'm running the final version of the app on an iPhone 13 simulator, which you can see on my screen. As I've said before, I kept the app really simple. So it only has two screens, one for when the user is logged in and one for when the user is logged out. Right now, we're on the logged out screen. All it has is a title and the log in button. Clicking that button starts the login process, so I'm going to do that right now. The first thing that happens in the login process is this alert box. iOS SwiftUI login wants to use Auth0.com to sign in. Now, this is a security feature built into iOS. It's there to notify you when the app is sharing information about you, the user with a third party, which in this case is Auth0. Now, I want to log in, so I'm going to click Continue, which will take us to the Auth0 universal login screen. Now, this is a WebView-based screen, and it's a cross-platform page that supports logging in, signing up for an account, offering different authentication flows, all kinds of things beyond what we're basically showing right here. But since it's universal, it looks the same whether it's running on iOS here, or Android, or Web, or any other platform that we support. Don't worry about the default look and feel right now. We're just using it for this exercise, but the look and feel of the universal login page can be customized to match your organization's branding, and color scheme, and general graphic identity. So yes, you can make your login look like you. So let me log in right now. Let's see if I can type right without fumble fingering it. So I am using this account, randomuseratexample.com, and let's type in that password. Okay, and as you can see, I'm logged in now. Now, once logged in, the app takes you to the logged-in screen, which has a little more to look at. It's got the title that says you're logged in, plus the user's picture, name, and email address, which we've extracted from the user's account. Then finally, there's a Logout button at the bottom. Now, clicking Logout does what you'd expected to do. Start the logout process, which begins once again with the iOS security alert box. Now, if you choose to continue, the logout process finishes, so I'm going to do that. And now I'm logged out. And as you can see, we've returned back to the first screen, the logged out screen. Next thing we're gonna talk about is the starter project. Rather than make you start completely from scratch, we've provided a starter project for this video's exercise and you're going to need it. So you'll find the link for the project in the description below. Go ahead, download it, open it. I'll be waiting right here. All right, you're back. Anyway, I've got the starter project, as you can see, open in Xcode on my screen. Now, I'm a believer in making starter projects that actually compile and run, so let's do that. I have already selected my go-to simulator, iPhone 13, pick your favorite, and I am going to click the run button. And in a moment, we get the app. Now, we haven't yet integrated Auth0, so the login button simply just takes you to the logged in screen. And as you can see, since we haven't integrated Auth0 yet, we have no information to display about the user, so we're simply showing some default information right now, a blank, a blank name field, a blank email field, and a person placeholder graphic for the user's picture. Clicking log out basically takes you back to the first screen, so it's this app that we're going to start adding Auth0 too. Let's take a look at the app's code. The really interesting stuff, it just lives in two places. The first one's content view, and that defines the appearance and behavior of the app's one and only view. Now, there's also another file, profile, and it decodes and stores data about the logged in user that the app gets from Auth0. We'll look at that in a bit. Let's look at content view first, and as you can see, content view has two state variables. The first one is authenticated. It's a Boolean that's true if the user's logged in and false if the user's logged out. Makes sense. The second one is user profile, and this contains information about the logged in user. We use it to get the user's name and email address as well as the URL for their photo. Now, content view's body is basically a big if statement. Now, remember, I'm trying to keep this start app simple. So this is an if statement that returns one of two vstacks depending on the value of is authenticated. So let's start with if is authenticated is true. So I'm going to change its value here and update the preview in the canvas. And there we have it. We are now at the logged in screen, and as you can see, it's made up of a vstack containing a text object followed by a view that I defined called user image. What it does is it takes a string containing a URL and asynchronously downloads the image and then displays it. And while it's downloading, or if it doesn't have a chance to download, it shows this placeholder person kind of graphic there. Now, below the user image is another vstack and this vstack contains two text views, one for the user's name, one for the user's email. And then finally, we have the log out button. Now, on the other hand, if is authenticated is false, which is the initial state of the application. So I'm going to change is authenticated back and refresh the preview in the canvas. We should see, there it is right now, the logged out screen. So anyways, if is authenticated as false, we get the logged out screen. Once again, made up of a vstack containing a text view, this time displaying the title of the app, Swift UI log in demo and the log in button. All right. Now, at the bottom little farther down in content view, I've got the code for the user image view, the thing that asynchronously downloads the user image and then a couple of view modifiers to style the on-screen controls. Let's scroll a little further down the file. Now, I'm going along with a practice that a lot of iOS developers I know have adopted and that is dividing structs and classes into state and behavior with the state part going in the main part of the struct or class and the behavior part going into an extension. So I've done that here. I'm highlighting it so you can see it. Yes, in an extension for content view, where the code for log in and log out live. Now, these methods are called by pressing the log in and log out buttons and right now all they do is they simply change the value of the state variable is authenticated. As you can see from the to do comments, we'll eventually add more code to them. Let's take a look at the starter apps other file profile. So after the user successfully logs in Auth0 notifies the app saying, hey, this user's legit, we know them and here's proof. And that proof comes in the form of an ID token which Auth0 provides as a JSON web token or JWT or as some people like to call it a JAW. Now, the ID token in addition to being proof that we're dealing with a known user also contains some very basic information about the user. Profile extracts and stores these pieces of information and in this case profile is storing six pieces of information all in string form and they are ID which is an identifier that uniquely identifies the user name which is the user's full name, the one that you normally display on their profile page. Now, if no name is provided, this defaults to the user's email address and speaking of email stores the user's email address, email verify, this stores value either true or false. It's true if the user verified their email address when they were sent a confirmation email after registering for an account. Then there's picture and that is the URL of the user's profile picture. And finally updated at which is the date and time when the user profile was last updated. Now, if you look down a little further down here at the extension, you'll see that right now profile has only one computed property called empty. And what that does is it returns an instance of profile whose properties are all empty strings. You'll also see down here to do comment which indicates that we're gonna add a little more functionality to this struct. We've explored the starter project and it's now time to start adding things to it. And the first thing we're going to do is we are going to add Auth0.swift. Now, Auth0.swift is the Auth0 SDK for all Apple platforms, not just iOS, but also Mac OS, iPad OS, watch OS and TV OS. It's our third most used SDK and it accounts for about 11% of all the API requests made to our systems. If you are building an Apple device application that needs to authenticate or authorize its users, you want Auth0.swift. Now, Auth0.swift incorporates what we've learned from securing applications on Apple devices for the past few years. And if you've used earlier versions, you're going to appreciate the latest versions which include updated error handling, support for async, await and combine, improve default configuration, new documentation and best of all, we removed some deprecated methods and legacy features. So if you're new to Auth0, on the other hand, you'll be pleasantly surprised at how little code you need to write to add authentication to your apps. Let's go back to Xcode and the starter project and add Auth0.swift to our project. So I'm going to go to the file menu and select add packages. Apple makes a whole bunch of Swift packages available to us and the one we're looking for is Auth0.swift. So I'm going to enter it into the search bar and there it is, the only results. So I'm going to select it and I'm going to specify for the dependency rule here, up to next minor version. I try and keep reasonably up to date. And yes, add to project. It is the current project showing right here. So we're good. I'm going to click add package and Xcode does its thing. In the end, it's going to ask us to confirm that we want the Auth0.swift package. I'm going to say yes by clicking add package. And now if you look in the project navigator, you can see that there are now three package dependencies that have been included with the project. Auth0, JWT decode and simple keychain. For this project, we're just going to use Auth0 and JWT decode. If you like to do some exploring, feel free to start poking through the code that makes up these libraries. You can see what they do. There's also some documentation in the form of readme, but that's all beyond the scope of this video. It's time to visit the Auth0 dashboard. If you've made it this far and haven't signed up for an Auth0 account yet, you need to do it now. Now we know that as a developer, you need time to take your tools for a test drive and Auth0 is nothing if not developer centric. Luckily, you can sign up for a free account and it's a good free account. It lets you add authentication for up to 10 applications and it supports up to 7,000 users for all 10 of those apps. This should be enough for evaluating the platform as well as prototyping, development and testing. Oh, and one more thing. If you want to know how to sign up for an account, look in the description below. There's a link there. Log into your Auth0 account and you'll be presented with the Auth0 dashboard. It normally takes you to the Getting Started page, but we're gonna skip past that and go straight to creating an application within the dashboard. So in the left side menu, click on Applications and a submenu will appear and then select Applications in that submenu. In a moment, the page will appear and it lists all the applications in your tenant, right? Typically, if you're starting with a brand new account, you'll probably just get a single app there, the default app. We don't wanna use that. We wanna create an application specifically for our app. So I'm gonna click on the Create Application button right here. What this is going to do is it's gonna ask for some details about our application. In this case, it wants a name and it wants to know what kind of app it is. So I'm going to go with the name SwiftUI Login Demo and it's a native app. So I'm going to select native. Once I've done that, I click Create and there you have it. We now have an application registered in Auth0 named SwiftUI Login. It is native and we're immediately taken to the Quick Start page. The Quick Start page features a bunch of icons that you can click on to get Quick Starts, which are pre-made projects for various frameworks and operating systems as well. So you've got Android, you've got iOS, React, native, .NET and so on. But we already have a starter project. So what we're going to do is we're just going to go to Settings. So click on Settings near the top of the page and here is where we're going to do two very, very key things and that is, one, get information that the app needs to know about Auth0 and two, provide information that Auth0 needs to know about the app. And some of these key pieces of information include the domain, which I'm highlighting right here. The domain is the unique identifier for your Auth0 tenant. The other thing I'm going to highlight is the client ID. The client ID is the unique identifier for your application. So what we need to do is we need to take this information and bring it over to our iOS project. Now, the Auth0.swift library expects to find this key information, the domain and the client ID within a property list within the iOS project. So what we're going to do is we're going to create a brand new property list specifically for Auth0 values. The way to do that is I'm going to select the iOS Swift UI login in my project and I'm going to create a new file. It's going to be an iOS file and just to make sure I can find it, I'm just going to type in the word property in the search box and it gives me the file type that I want, property list. So I'm going to select that and click next. The last thing it's going to do is ask me for a name for this file. I'm going to give it the name Auth0. So it is very clear that this is a set of Auth0 properties and I will click create. If you look in the project navigator, there is now a new file Auth0 and it's a property list. What I need to do is I need to create two rows for once again, these pieces of information, the domain and client ID, both are string. So I'm going to create these two rows just as sub items of the root. There we go, two new items. One's going to be called domain. So I will enter that in the first key row and then for the second row, client, capital ID. Both are strings and now that I've got a place to hold them, I'm going to start copying them over. I'm going to copy the domain from the dashboard and into the corresponding value inside the property list. I'm going to do the same thing for the client ID. Copy, switch to Xcode and paste. Now the app knows about Auth0. With the property list, you now have given the app the information it needs to communicate with Auth0. Now we need to do the inverse. In other words, we need to give Auth0 the information it needs to communicate with the app and we'll do it on the settings page. It's just a little farther down. So I'm going to scroll down to this section here, application URIs and the information we need to provide, in particular, are allowed callback URLs and allowed logout URLs. Now a callback URL is a URL that Auth0 redirects to after the user successfully logs in and there can be more than one of these. There is also allowed logout URLs and a logout URL is a URL that Auth0 will redirect to after the user logs out. And like callback URL, there can also be more than one of these. Now, at this point, you're probably thinking, hey, wait a minute, wait a minute. I'm writing an iOS app using SwiftUI. Doesn't have web pages that you navigate to using URLs, but you've got views and you're right. For native applications, the callback and logout URLs are the same string. And Auth0 sends that string to the app to inform it whether a user has logged in or logged out. Now, the string that native iOS apps use for both the callback URL and the logout URL follow this format. And I'm going to drag it onto the screen here. So it is the apps bundle identifier colon slash slash, your domain, in this case, the domain of your tenant, slash iOS slash the bundle identifier again, followed by slash callback. So what we need to do is we need to get your app's bundle identifier. That's easy to get. I'll show you how right here. Go back to Xcode and select the project in the project navigator and then select targets and make sure general is selected up top. The identity section contains the bundle identifier. It is right here and it is, of course, made out of your organization identifier and your app identifier. So what I'm going to do is I'm going to highlight it and copy it. Then I'm going to jump back to my text editor here and simply replace bundle identifier, the bundle identifier placeholder that is with my actual bundle identifier. Okay, so far so good. Then what I need to do is I need to get the domain once again that comes from the Auth0 dashboard. There it is. I'm going to copy my domain and then replace the placeholder with the actual domain. This is my actual callback logout URL for my application. So I'm going to take the whole thing and copy it, switch back to the dashboard and scroll down to the application URII section and paste it into both allowed callback URLs and allowed logout URLs. Well, guess what? We have provided Auth0 all the information it needs, but we need to save this. So scroll down to the bottom of the page where there's a save changes button and click it. I myself have forgotten to do that and then wondered why my app didn't work. So make sure you do it. If you just signed up for and created an Auth0 account, your tenant won't have any user accounts and you're going to need at least one to be able to log into the app. So follow these steps to create a user. So going to the dashboard, find user management on the left side menu and click it. Select users from the sub menu and the user screen will appear. We're going to create a brand new user. So I'm going to click on the create user button and I need to provide just a couple of pieces of information, basically email address and a password. So I'm just going to go with rando at example.com. So rando for random person and then I'm just going to pick a reasonably strong password. Let's hope these match. The last option to pick is connection, but there's only one. So just leave it at username password authentication. Click create and we are taken to the details page for our brand new user. With that, we now have a user that we can use to log in to the app once we've actually installed authentication into it. And we're going to do that right now. So let's get coding. It's time to implement login and log out. We'll start with login. So in its current state, the login method, which is in the content view extension. So I'm searching to content view and I'm scrolling down to the bottom of that file to the extension portion. Yeah, right now it only simulates login. All it does is simply you click the login button and we set is authenticated to true. We actually want proper login. So what I'm going to do is completely erase the contents of the login method and start. Now, since we have now imported the Auth0 package and all its dependencies, I now have an Auth0 object. And what I'm going to do is I'm going to create an instance of the Web Auth object. Web Auth is a client for doing web based authentication using universal login. Or in other words, when you call upon it and tell it to do its thing, it will put on the screen the universal login screen where the user can enter their username and password or authenticate themselves in all kinds of ways. So we're creating that and then we are basically saying start, do your thing. So we will use its start method and the start method takes a closure. And that closure takes a parameter called result. And we are going to do something based on that result. The result, it basically signifies either successful login or failed login. So I'm going to use a switch statement here. This is typically what we do. Switch based on result. And I'm going to just mark this ending brace because we get braces messes really quickly in Swift UI. And we need to handle two different kinds of cases. There is the failure case. I like to handle that one first. So case, failure. And it does give us a parameter called error that we can handle. And there is also the success case. Case, success. And we get a parameter out of that called credentials. And I'll explain what those are when we get to it. So let's handle the failure case and it's fairly simple. With the failure case, we're dealing with either, the user either pressed cancel on universal login. So the universal login has a canceled button if somebody decides, no, I don't want to log in, I'm fine. In that case, that's considered a failed login or something unusual happened. So we'll make a note of that. And in the failure case, what we're simply going to do is we're just gonna, for the purposes of this demo app, we're just gonna print a failure message to the console. Basically say, hey, oh, something unusual or unexpected happened, failed with, and I'll just put the error there. The more interesting case, of course, is the success case. And what I'm gonna do here is if the user has successfully logged in, they are authenticated. So I'm just gonna go self.isauthenticated equals true. Now, in the earlier version of the login method, we basically just said is authenticated equals true, but we're inside a closure now. So we have to specify self. The other thing I'm gonna do for debugging purposes, and just to get a better idea of what's going on, is I'm gonna print what this, I'm gonna print this credentials object to the console. So credentials, credentials. And one of the credentials that we get is the ID token. So let's print that out as well. All right, Xcode is screaming at me. And it's saying, I can't find odd zero in scope. And that's because I forgot to import it. Let's import that right now. Scroll back down and Xcode is now happy. That's good. Let's take care of log out next. Log out, you'll be pleased to know, follows the same pattern that login does. We create an instance of the odd zero object and use it to create an instance of the web auth object. And we call a method from the web auth object to do something for us. In this case, it's log out. So once again, I'm gonna go to the log out method, our fake log out method, and we are going to replace its contents with a real properly functioning log out method. So we'll start again with the odd zero object. And we will say yes. We want a web auth object again, because yes, this is even logging out, despite the fact you don't see universal login, is a universal login thing. Web auth. And this time, what we're going to do is we're going to use clear session instead. The clear session method also expects some kind of closure and that closure provides a parameter called result. And that result symbolizes either success or failure. So we're going to do that again, just like we did with login. So I'm gonna use a switch statement, switch result and handle the failure case. So case failure and failure does provide us with a error parameter. And then there's case success. This time in login, there's no parameter. Success just means we're logged out. So once again, with failure, it's kind of a weird case. So I'm just going to print the error to the console. And then in the case of success, I'm just going to set is authenticated and I have to use self because we're inside a closure. So we need to tell Swift which is authenticated we're talking about is false. And there we have it, the logout method. So we've implemented login, we've implemented logout. So I'm grabbing my lucky Einstein duck. Feel free to grab your own lucky charm, move your hand over the run button, say the magic incantation, this should work. And run the app and let's see what happens. Oh, so far so good, Swift UI login demo. I'm going to click on the login button. All right, the security alert box, that's fine. I'm going to click continue. Oh, okay, this time instead of being taken immediately to the logged in screen, I'm presented with universal login. So let's use that user we just created. In my case, I created one called rando at example.com and my fiendishly clever password. Oh, and it worked. Okay, and we're logged in. Good, but no, we're still showing the placeholder image and we've got blank name and email and the logout button. Let's first confirm that the logout process worked. So I'm going to click logout. Okay, security alert, that is a good sign. I'm going to click continue and yes, we are back on the logged out page. So it works, but we don't have that user information yet. There's a reason for that and that is we haven't extracted that information from the ID token. Now, remember, if you look at the code for login, I had a couple of print statements here. Print the credentials and print the ID token. Let me pull up the debug console here and here they are. We've got credentials, which is a collection of some kind of information, which I'll explain in a moment. And then we've got all this gibberish here, the ID token. So the credentials are a big package of information that Auth0 sends to your app upon successful login. There's all kinds of stuff, but the thing we're most interested in is the ID token. Now, if you ask to print out the ID token by printing out the credentials, as a security measure, Auth0 redacts it. And it just basically says the ID token is redacted. But if you print out the ID token itself by printing out credentials.id token, access the ID token directly, you can get its value. So the ID token, once again, is this thing that Auth0 gives you and it's proof that the user is known. And within the ID token, once we decode it, is some identifying information about the user. What we need to do is extract it and we do that using the profile struct. Earlier in this video, I talked about the profile struct and its job was to decode and store information about the user that we received from Auth0. Now, what is this information about the user that we got from Auth0? Well, the key piece of it is the ID token. So look back at Xcode here. Remember when we ran the app after implementing login and logout, I printed out ID token to the console. So this is the ID token right here. So somehow somewhere in here is information about the user but it's encoded and right now looks like gobbledygook. Well, let me copy this and then I will take us to a particularly useful web page jwt.io and it's all about JSON web tokens, aka JWTs, aka Jots. And one thing it has, if you scroll down here, is a Jot decoder. So I'm gonna take that ID token that I just copied from the Xcode console and paste it into this field right here, the encoded field. Boom and boom, what have we got here? Nickname rando rando at example.com. In other words, this is the user that we created so that we could log into an our app in the first place and all the information is here. We have a picture. We have their name which is, which defaults to the user's email address. We have the last updated date. We've got an email address. We've got email verified. We have this information. We just need to decode it and that's what we're going to do with the profile object. So let's set about to give our app the ability to decode these Jots and we'll do it in profile. So open profile and one of the first things you'll see near the top of the file is a to-do comment telling you to uncomment this import statement, import JWT decode. So I'm gonna uncomment it. We didn't need it in the first version of this application because we weren't doing anything real, no real login, no real log out. We were just pretending but now that we actually are logging in and logging out using auth zero we're now receiving ID tokens which are encoded JSON web tokens that we need to decode. And luckily when we imported the auth zero package we imported the JWT decode library which gives us the capability of doing that. So we've imported it and now I'll scroll down to the bottom of the struct. We have another to-do comment, implement the from method. The from method is going to take an ID token and convert it into a profile object with all the properties ID, name, email, email verified because it's going to take the data encoded within the token and decode it and then just fill out these profile properties. Now, just to make life easier because it's a lot of tedious typing what I'm going to do is I'm going to copy and paste this from method and then I'll walk you through it. So I'm going to replace this to-do comments with the from method. Now, we'll walk through. The first thing you should notice is the guard statement. The guard statement is the nightclub bouncer of Swift. Certain conditions have to be met and if they evaluate to true you'll be allowed to stay in the club or in this case the method. Otherwise, you will be kicked out. You will have to return but you can also return a value. So the key to what's going on is in this line right here. What we're trying to do is decode the token that we've been given. Now, there's a possibility however slim that whatever token we got is corrupted or perhaps somebody's just passing this method some junk that we can't tell from the gobbledygook that is an ID token. So we try to decode it here and if that doesn't work, we fail and we simply return an empty profile. And don't forget an empty profile is defined right here. It's basically a profile struct whose properties are all empty strings. If we are successful in decoding the ID token we can extract the information from it including name, ID, email, email verified picture and updated that. Once we've managed to extract this information we simply use it to create a new profile instance with those values right here. All right, we now have a fully functioning profile struct. It's time to put it to use. It's time to make it decode the information that we received from Auth0 and use that information to display useful stuff on screen. So in order to do that we need to go to content view. Now, quick review, content view has two state variables one of which is user profile and in the very beginning when the app starts up we set it to profile.empty which creates an instance of a profile all of whose properties are just simply empty strings. And if you scroll down to the body of content view which defines the structure and behavior of the view you can see that we are displaying user image and user's name and email address based on the contents of user profile and until now we have only been using an empty user profile so it currently displays nothing. It's time to fix that and the place to fix that is in the login and log out methods. So let's go to the login method down here in the extension and just immediately after we set is authenticated to true in other words we know that the user is logged in therefore we have their information because we have their ID token. We can now update the user profile and say, okay it's no longer the empty profile but we are going to use the from method to pull the information out of the ID token that we received as part of the credentials object that Auth0 gave to us when the user successfully logged in, perfect. Now, not absolutely necessary but I do like cleaning up after log out. The success case for log out, what I'm going to do is I'm going to set the user profile to profile.empty. In other words, once again, the profile can compose an entirely of empty strings. All right, that looks good. I am clutching my lucky duck and I'm gonna click the run button and let's see if this works. Simulator's firing up, so far so good. I'm gonna click the login button. There's our security alert, that's fine. All right, universal login, that's good. Once again, I'm going to use the user that we created earlier, randoadexample.com. User's password, aha! And there we go. The picture that's automatically assigned to a user you create within Auth0 is basically just made out of the initials. We've got the name and email and remember we only provided a email address and a password, so the name defaults to the email address but yes, we've got that user information right there, randoadexample.com. Need to confirm that log out works properly. Click the button, there's our alert and we are back at the logged out screen. So guess what? We have the app. All right, well, high fives and handshakes all around. Guess what? You've completed the exercise. You have a working SwiftUI application that uses Auth0 to log in, log out and display information about the logged in user. That's great, so hey, congratulations and now music.