Sessions is temporarily moving to YouTube, check out all our new videos here.

Identity Crisis

Andrew Gunn speaking at dotnetsheff in September, 2017
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

Imagine a scenario where you need to secure a web application. Sounds simple, right? But this web application is one of many bulkheads that sit behind a shared API. This API must have security measures to identify and ensure each authenticated user can only perform certain actions. And these authenticated users must be able to navigate seamlessly between each bulkhead without the need to re-authenticate. With the help of Identity Server, it is simple. Find out why.


This talk is on Identity Server. So, just to make sure you're all awake after a hard day's work, has anyone heard of Identity Server? Just give me a show of hands. Has anyone actually used Identity Server? And finally, has anyone used it in production? Who's the most daring? Congratulations, wow. Okay, so a few of you have heard of it. Can anyone remember the ASP.NET Membership? Way back when? It was horrendous. And before we go into what Identity Server is, I just want to take you back on a little history trip of how we got to where we are now. And feel free to ask questions, by the way, because I'd rather do them as we go, rather than at the end. So, before there was Identity Server, there was ASP.NET membership. But if you've ever used it, it was horrible. So, as a result, a guy called Brock Allen, in roughly 2012, created something called MembershipReboot. And he did that because he was frustrated with all of the problems with the ASP.NET membership provider. Some of the advantages of using MembershipReboot, which the ASP.NET membership provider didn't have, is flexible storage. So, if you remember, you were basically constrained to SQL, your tables, you know, they were basically predefined for you. With this one, you could have SQL, you could have no SQL, or you could have some other sort of storage engine, there. It supported claims, which are really popular today. It had proper password storage. So, your passwords were secure, which was one of the massive problems in the early days of ASP.NET membership. It also supported things like two-factor authentication, notifications, it was highly extensible and customizable, but unfortunately, it's no longer maintained. And the reason why it's no longer maintained... Just... Is because of ASP.NET Identity. So, ASP.NET Identity was created by Microsoft, and it was the successor to the MembershipReboot. It has all of the good things in MembershipReboot, so Brock Allen decided, there's no point in continuing doing this. Might as well let Microsoft do it. You know, they've got a lot more money behind it. So, again, it fixed a lot of the problems for the previous solutions. And in essence, it's a single identity system for all ASP.NET frameworks. Again, you've got full persistence control, so you can store everything, in whatever technology you want. It's unit testable, it's got claims, it's got social authentication, so you can do things like Facebook, Google, or... You can hook it up as your active directory, supports ON, and it's all available through Nougat packages. So that leads me on to Identity Server four, which I think that's the version that we're at, today. It's written by Brock Allen, who brought us MembershipReboot, and another guy called Dominic Baier. This one was... This version was created way back in 2015, but there's obviously versions one, two, and three. One and two, I don't really want to go into. They're pretty insignificant in comparison to this one. And Identity Server three is basically prior to .NET Core, so Identity Server four is .NET Core compatible. You can still... If you've got an Identity Server running Identity Server four, it's still compatible with clients that use Identity Server three, so you don't have to sort of upgrade all of your components. It's fully open sourced, as you can see, with the GitHub graph, there. So, if you spot any problems, you can raise them and get help. So, Identity Server. I've talked a bit about it, but what is it? So, it's an identity platform, basically. You need to identify your users. You need to know who I am, in order to do something with me. What can I do, what can I see? It's completely free, it's open sourced, like I said, and it's basically an identity token service. So, if I go see your website, and you've got Identity Server running, you basically get a token that represents me, and that token will have one or more claims that describe me. One of the main reasons for choosing to go with something like Identity Server is single sign-on. So, we'll cover this a bit more in a second, but, you know, you've got multiple websites that you all need to sign in on. The old way of doing it was, you sign on to each one, individually. It's quite cumbersome for the user, you've got repetitive code, et cetera. But that's definitely one of the biggest wins and selling points for Identity Server. It supports protocols like Open ID Connect, and OAuth 2. I'm no security expert, so I'm not going to go into the details of those. I'm not going to try and trick ya, and make up that I know the ins and outs of those, but that's the reason why you would use Identity Server. Unless you... I'm sure most of us are busy making our applications, and thinking about how to make those better, rather than the security concerns. It supports mobile web, SPAs, desktops. It's highly configurable. And it supports ASP.NET Identity, which we briefly mentioned. But you can also plug it into things like MongoDB, there's a MongoDB provider out there. You can write your own. If you want to plumb it into Table storage, because you want to save money, you don't want a MongoDB, that's fine. So it's really customizable, is what I'm trying to get at. So, some of the concepts that I just want to go over, before we dive any deeper. So, just list these off, real quick. So, user, that's the human. That would be me, in this case. We've got a claim, which is a piece of information about me, so that could be my name, my email, the thing that identifies me. Which is often referred to as a sub claim. You can create custom claims, but there's loads of predefined claims, so try and use those, if you can. The client. The client is the thing that's interacting with Identity Server, and you can have multiple clients, you're not restricted to how many. Scope. So, they're identifiers for resources that each client has, or wants to get access to. So, you know, if a client connects to Identity Server, is that client allowed to see my email address, for example? You can restrict that based on each client, depending on what that client does. And finally, the access token. So, that's basically a J-W-T, or a jot, which is assigned, I think it's Javascript Web Token. And that represents all of this stuff. And because it's signed, you can pass it on to all your different components, and you know if it's been tampered with. It doesn't contain too much sensitive information. It might just be, what identifies that user. Maybe the name, maybe their email address, it just depends. The flows. This is basically the different flows in which you can use to authenticate your user. So, we'll just go through them one by one. So, client credentials. This is basically server to server communication. So, if you've got like a Windows service, or something, and you want it to act on behalf of the client, then you can do. Or, you can get it to not act on behalf of the client, it depends what you want solved, there. So, just to clarify, the client credentials is not requested on behalf of the client, and the resource owner password is not... Sorry, other way around. Client credentials not as the user, resource owner as the user, sorry about that. The implicit, that's a browser based application. So, if you've got a SPA, you might have something in React or Angular, you can basically talk to Identity Server through the browser. You might just be talking to an API. So you need that browser to Identity Server communication. Authorization codes. This is a way to retrieve the identity token on a back channel, as opposed to the browser. It's not recommended to use on its own, which is why you'd probably want to use the hybrid model. So, that's a combination of implicit and authorization. So, you've got a bit of browser channel, back end channel, it offers the most security. And that's probably what you'd use for most web applications. That's what we use. And finally, refresh tokens. So, remember I talked about the access token, which is basically... It identifies me. Well, that has an expiry time, because if you authenticate someone, you don't want to authenticate them forever. So, you have a short expiration time. And the refresh token endpoint is a way of refreshing that access token. So, conceptually, you get an access token, and you get a refresh token, and you can use both of those to refresh your access token. It's basically a way of doing that behind the scenes, so the user isn't aware that any of this is going on. The alternative is that the user is kind of, oh, I've been logged out, now I'm logged back in, and it's a bit jerky. So, that's just a way round that. So, this is basically pre-Identity Server. Remember, I've talked about single sign on, which is the number one sign on point, for me, of using Identity Server. So, you've got users over here, they're interacting with your application through a number of different browsers. Let's assume that they're both desktop and mobile. It doesn't really matter. And let's say you've got three different web applications that all talk to the same user data store. Let's say SQL, for argument's sake. In this example, if you wanted to go to this URL, you know, and all of the resources are secure, these have to authenticate here. If you then went down to this application, which is on a different URL, you wouldn't be authenticated against this one, necessarily. So you'd have to re-authenticate using the same credentials. And the same for the third app. And if they're branded the same, the user is like, why are you asking me to re-authenticate? I'm just basically navigating your website, and you're making this more difficult for me. Remember that you basically get an auth cookie against each one of these. So, if we introduce Identity Server, it sits alongside your applications. It doesn't kind of go into them. So, what you get here, is the user comes in, let's say he goes to App one, goes to a secure page, he's immediately redirected to this Identity Server. So this would be a separate website, in itself, common URLs are the, for example. And that would take you to your Identity Server. You'd then enter your credentials. So, let's say we're just using a password, to keep it simple. And then that would talk to your UserStar. It would return you back... It would actually return it via the app, but return you the identity, the access token. And then, this app can basically create you an authentication cookie, and put that inside. So, you're now authenticated against this application. If the user navigates to this application, then they haven't got an authentication cookie against that one. And we've got an authentication cookie, here. So, what happens is you get bounced back to the Identity Server. But the Identity Server quickly knows that you're already authenticated because of that auth cookie. And it then gives us this identity, and we're in. So, to the user, it seems like you're being bounced to a different site, very quickly, and then you come back. But, I imagine that is very more convenient than re-authenticating against each app. So, if we expand that a little bit further, because that's great, we've got single sign on, but what about our APIs, and our desktop applications? So, we've still got our three apps, our three web apps, down here. And we're still getting our identity token. But we've also got a few APIs down here. So, if we were using a SPA application, we could talk straight to the API, directly. That's completely fine, that would be using the implicit flow. These apps all talk to this API behind here, and all they do to authenticate against this API is pass this access token. It's basically passed through the system, and it can keep going as deep as you want it. And each of these APIs and apps are configured to know to look for this access token, so that's how they know if you're authenticated. Similarly, if you've got a desktop application, or a server app... Sorry, a server application, that can talk to your Identity Server, as well, using the same username and password that we've used down here, and that can talk to an API. So it really opens up quite a lot of potential, there. So, this is the first demo. Now, where's me cursor gone? Conveniently, I've not had to create any of these demos. So, just bear with me. Like I said, Identity Server is open source, so this is their Get Help page. There's the source code for Identity Server, itself. They've got Identity Server four and three, and then you've got Identity Server four dock samples. So, if you want to look at any of the code that I'm going to demo, just clone this repo, and they're all in there. If we just shift... I'll just show you this, for a second. So, this is the cloned repo, and we can see that, in the quick starts, they've basically got all bases covered. They've got most of the flows, so you've got your client credentials, I can't really read that, but... You've got client credentials, ASP.NET Identity, Javascript client, which is just BAA, or your implicit flow, and then this last one is a way to use entity framework, as well, just as a different variant. So, what we're going to demo first, is how we can use ASP.NET Identity. 'Cause that's probably the one I'd recommend you starting with, is the easiest one. So, when you open up the solution, you'll see a number of projects. Now, just ignore the client and resource owner, for now. We're just not interested in those. What we are interested in is the Identity Server, one, which is this one here. The API, and the MVC client. So, remember, in one of the previous diagrams, we had a web app talking to an API, with an Identity Server. That's exactly what we've got in here. So, before I jump into the code, let's just see if this spins up, and we can have a look at what's going on. So, this is going to spin up the Identity Server, the API, and the MVC front end. Hopefully. Okay, got one. We got two. Yes. Yes, I think this one is the ID server, I think. So, let's try it, anyway. So, basically, we've got the MVC client over here, we've got a public home page, which is why we can get to it. And then we've got a secure page that requires us to authenticate. So, what should happen is we should be redirected. I don't really need this tab on the right, 'cause we can just do it all in here. I should get redirected to the Identity Server. So, I should be on port 5,000, I can't quite see, but-- Okay. Fantastic. So, now I've got to remember my credentials. So, it'll be mount server to Identity Server. I created these the other day, so let's try to logging this in. It's nice and fast, right, woohoo. So, we're authenticated. We're still on the Identity Server. We should still be on port 5,000. And this is basically where it's giving the user the opportunity to say, do you want this client to have permission to see this data about you? So, this goes back to the scopes. So, as a user, I could un-tick all of these, if I wanted. It's quite similar to when you log in to a website through Google, you know, you'll get this sort of screen. If you're using Identity Server on a project where all of the clients that are going to talk to Identity Server, they're all sort of internal, so... Microsoft, for example. There'd be very little point in showing this screen. So, you can sort of skip it. But if you open your Identity Server up to anyone, let's say you want to be a global Identity Server, identity server as a service, which doesn't exist yet, then you definitely want to show this. Because each client will be requesting different bits of information about you. But regardless, I just wanted to share, anyway. So, we're going to allow that, so we should get bounced back to 5,002, or something along those lines. 5,002, or one. So, we're on the secure page, now. We've basically got a cookie on the Identity Server and the MVC client, and all this is doing is it's listing out all of our claims. So, some of the ones to highlight are the sub-claim, which is the ID claim. So, this is the unique ID about the user. So, traditionally, you would have your user database within your main database, so you could have foreign key references, et cetera, et cetera. So you can still have a reference to that ID, but it wouldn't be like a proper foreign key, so to speak. But, this is what you should use to identify the user, it's unique. And some other things to note. The expiration. So, if we go... It's basically an Epoch time. If we put that into here, we should see the value come out. And I think it's set to an hour. But it's in UTC, so that's set to an hour. So, that's going back to the access token's expiry. That's basically when you'll stop being able to do things. Now, it's worth pointing out that the auth cookie expiry time is different to the access token expiry time. So, there's an auth cookie for this website that might expire tomorrow, might have a 24 hour expiry. But if that website is talking to an API, which it is, in just over an hour's time, you won't be able to talk to that API, because your access token will have expired. And that's where you can either refresh the access token, or you can just say to the user, hey, that access token's expired, basically, un-authenticate them in the MVC client, which deletes the cookie, which then bounces them back to the Identity Server, and bounces them back. Either which way, you can refresh this access token quite easily. So, the final one that I'm going to point out is the access token, itself. Now, that's not stored in a claim. You don't really want that in a claim. That's store inside the authentication cookie. And you can get it out using the ASP.NET SDK. I'll show you that in a second. But, if we have a look at this thing called, if any of you have heard of jots, you've probably been here. You can basically put your jot in there, and it deciphers it. And we can see exactly what's in it. So, some of the information, we've got that expiry time, so that's really important. Because that's assigned to access token, we need to know when it expires. If you were to change that at the other end, when it tries to read the access token, it would know it's been tampered with. Another really important thing is the sub. So, we need to know how to identify that user, and there's a few things about the scopes. So, basically, what can we do on each client, with this access token? So, in... As part of this demo, we can call the API that we've got sitting alongside it. So, if we do that, we're calling this on behalf of the user. So, basically, we're passing the identity... Right, that's probably the one that wasn't running, then. So, we've not got the API running, I'm guessing, probably. So, if we go back into visual studio, I might just duplicate my screen to make this easier. So, yeah, like I was saying, we've got the MVC client, so let's have a quick shifty through there. Nothing special, it's just a typical MVC client with a home controller. A lot of what we're interested in is done in the start up. And that's basically, how do we configure Identity Server. So, we're telling the MVC client that we want to use cookies. So, we want to create an authentication cookie. And then, we're using open ID connect. Remember, I talked about these protocols. This is just a standard protocol. It's not a Identity Server thing, as such. It's just using the things that already exist. But the things that are interesting is that the authority is the URL to our Identity Server, and the client ID and secret. So, in order to talk to that Identity Server, you need this ID and secret, which you create inside the Identity Server, and it persists those. And the scope's, basically, what do I want to do? So, this one, in particular, it wants to talk to this API, which I couldn't demo. But that's what it wants to do. So, Identity Server will say... Well, it will check to see if you can actually do that, or not. Other than that, it's pretty much what you'd expect from an MVC client. It's got the authorise attribute on that secure endpoint. If we... Let's have a look at the view that we saw. So, all that was happening was that we were going through the user claims. Really easy to do, we can get that through the view. And then, this was kind of like the secret sauce of how to get this access token. So, this is buried within the auth cookie, and they provide you with a way to get those tokens out, and those two tokens, the access token, and the refresh token. So, if we just move on to the API, before we get on to the main event, the start up is very similar. This time, it is an Identity Server-specific thing. But in this instance, we're basically just checking that any requests given to us, you know, count... Are we getting this access token? If we are, can we go to Identity Server and check that it's valid? Et cetera, et cetera. And the controller that we were trying to hit, again, it's got the authorise attribute. So, nothing Identity Server-specific, after that point. And it just returns you the claims and the response, just as attribute example. So, the Identity Server, itself. Again, I don't know if I mentioned it, but it basically... The way that you use it is by Nougat. If you're going to create yourself an identity server, I'd probably take one of these quick starts and trim it down, or at least take that, have it on the side, and kind of rebuild it, so it looks the same as that. But to be honest, they're pretty good out of the box. But, as you can see, it's an MVC website. You have to create the host for it, and then you pull in these Identity Server Nougat packages. And then, you configure it to be an Identity Server. So, in the start up, there's quite a bit going on. So, like I said before, this one's going to use ASP.NET identity. So, we're going to configure the database, there. Few bits and bobs go down around email and SMS, but nothing Identity Server specific, as such. And this is the main bit, where it's kind of saying, I want to make this MVC client an Identity Server. So, because it's a demo, it's using all of these in memory clients, resources. But you have to create all of these other controllers for basically logging the user in, registering the user, changing the user's password. It doesn't do any of that for you. But it provides the APIs, the SDKs behind that, if you like. So, if we have a look at the log in, the log in action, it's just taking us to a view. And if we go to that view, we'll just see some HTML. Very familiar stuff. So, what you should take away from that is that Identity Server's great, it gives you so much. But you have to do the rest. You have to colour it in. And if you want two-factor authentication, you can add it, but it doesn't come with this demo. If you want to do password resets, and you want to use SMS, yep, you can do it, but you do have to bake it in. So, like I say, it uses ASP.NET identity. So, it's basically created as an ASP.NET identity database. Now, for those that can remember the ASP.NET membership database, that was quite huge, quite a few number of tables in there. They've trimmed that down a lot. But for me, they could probably trim it down a little bit more, for me. The main one is the users. So, if we have a look in there, no surprise, there'll be a user in there, When it decides to load. And we've got all sorts of different things. We've got his email address, few other bits and bobs, password hash. You can configure the password hash to use whatever hashing algorithm you want. We recommend bcrypt, but I'm not entirely sure what that one uses. I know it's pretty secure, but I don't think it's bcrypt. But you can change it. It's fully customizable. So, why do we need Identity Server? What is it giving us? The main thing that I'll say about that is security's hard. Like I said before, I'm no security expert, and I know the basics, I know that you need to secure your passwords really well, but I'm not going to go into the depths of OpenID Connect, and OAuth. I'm not going to study those. If that's your job, then fine, but I'm more interested in making great products, and it'd be nice if this sort of stuff could just be, you know, done for me. So, don't roll your own. Sorry, don't roll your own solution, would be my recommendation. Use something like Identity Server. If you don't want to have Identity Server sit over here as like a microservice, if you're like... You can embed it into a monolith, that's completely fine. But I would recommend having it as its own thing. The idea behind that is you can deploy to your clients, if you like, without affecting your identity server, and vice versa. It's great to have that sort of split. But, effectively, it allows you to spend more time making your product better, rather than wasting time worrying about security, and how that works. So, we've got another demo. This time, we're going to do a Javascript sample. It's not using Angular, or anything like that. Or React. It's just plain old Javascript. So, this is the Javascript client sample. It's very similar to the other one. And we've got our Identity Server, we've got an API. For some reason, they still bundle in the MVC client, I don't know why. But the main thing that we're interested in is the Javascript client, I believe. Let's just check. Yeah, so we're going to start the API, the Javascript client, and the Identity Server. So, let's see if this one works. Now, this one isn't configured to talk to a database. This one's just pure, in memory. Okay, so that's pretty good. I've got the main one. Just wait for this one to load. Wow, so that is loading. Let's have a look at some of the code. How it differs to the other one. So, the identity server will be much the same. It's just not configured to go to ASP.NET identity for the users, it just says, I want it in memory collection for my users. Okay, I think that's loaded up, now, so we can go back and check. So, we've got our Identity Server, there, and we've got our... We've got our client, our Javascript client. Now, if we log in, we're on port 5,003, we get bounced back to port 5,000. This example, I'm not sure if the other one did have, but it's been configured to use Google auth. So you could basically add as many different social providers as you want, there. You can also link the two. So, if you start as a username and password kind of guy, and then think, I want to add my Google auth... Google account to that, so I can log in with either, and maybe Facebook, as well, you can do that. That's quite a common problem with some websites, that they don't allow you to link the two, and they just create you a different account. So, again, we get the consent screen. We'll just kind of whiz through that. And then, because it's a Javascript example, everything's done in the browser. But you can still see a very trimmed down version of what we had before. We don't get the access token. That's sort of obfuscated away from us, it would be pretty bad if that was to get out. But we can still call the API, I believe that should basically output our claims. So, this one did work, this time around. So, the API has responded with all of the claims for that user. So, we can see things like the sub, because we're using it in memory, store for the users. And the sub is one. Expiry time, again, it's an Epoch. And some of the sort of scopes. So, with this one, I just want to highlight, because it's a pure, in memory, sort of solution, I just want to highlight how you'd go about doing that. Cause this is by far the easiest configuration. Rather than having a database, or anything, you can just actually download this, spin it up, run it, off you go. So they've created this class called config, and inside there, you've got, basically, loads of different collections. So, we're defining our API resources, we've got one API. And then, we're defining all of our clients. So, remember I said that each client needs a client ID and a secret? This is basically where you'd find those, and where those IDs and secrets would live. So, we're going to ignore these first two, because I don't know why they're in this demo, and the same for that MVC client. The one that we're really interested in is the Javascript client. And you can see that we're using the implicit flow, which is the one that you chose the SPAs. You can configure what is the redirect URL back to your client, so you kind of authenticate with your Identity Server, where do you want to be bounced back. And this basically says, which sort of URLs can talk to the Identity Server using this client. Because you'll notice, this one doesn't have a client secret. Because if you were to put that into a SPA, that would be available in some way, anyway. So, they get around that security in a slightly different way. Again, the users is just literally a collection of users. Hard lesson, Bob. Couldn't be easier. So, the pizza's is here. Therefore, I'm going to try and whiz through this as quick as I can. So... Okay, we've gone through the configuration management, so we can kind of whiz through that. That was talking about, basically, the config.cs. But what if you don't want to stow your config in memory? Because, at the end of the day, if you're going to build this properly, you'd want to store that in a database. So, the final demo is basically using Entity Framework to store your configuration. So, if we look in the config.cs feed, for this one, we'll probably find that it's very similar. Wow. I wasn't expecting that. Yeah, so I was expecting that... These clients to be in the database. Moving on. But, basically, if... Assuming that's configured correctly, you end up with a database like this. And you get these sort of tables. So, you can see that we've got a client's table in here, if we have a look at that one. Perhaps I got the wrong demo for that one. We can see all of our clients in here, so we've got our server to server, server to server as a user, MVC client, and the Javascript client. Maybe that code was just an overhang. As you've seen in the other examples, they tend to just copy code from the previous one. Ah, that's it, I knew there was a reason. So, if you were to use a combination of ASP.NET Identity and that demo there to store your clients in the database, you kind of get the best of both worlds. But if you wanted to get off the ground pretty quickly, then just put as much in memory as you can, maybe you're only going to have one client, so it doesn't make sense just to punk it in memory. But definitely have the users in some sort of store. Okay. So, one thing that people get confused with, in terms of Identity Server, is roles. What I would say about those is they're more generic roles. So, are you a patient, or are you a doctor? And they're not, can I do this particular task, in this particular client? Try and decouple yourself from that sort of roles, and that sort of permissions. Because your identity server's meant to sit in the centre of all of your clients, and not really know much about them, other than how they interact with the Identity Server, itself. So, Identity Server is not a role server. Think of an identity as a passport, where a passport says, I am from the UK. But it doesn't tell me what I can do in each country. So, a quick example to highlight that. One of the ways that we've got around, well, how do I know what you can do in this particular app? Basically, if you go out to Identity Server authenticate, and we get the identity token and the access token back, this app can basically call a slash member endpoint, or a slash user endpoint, whatever. Just some endpoint where you pass the access token, and it returns you a response, and it tells you all about that user, in terms of that API. So, here, I've got a role of administrator, and I've got these different permissions. So then this app can cast those permissions in the auth cookie, as claimed, and then it can just use those. Just bear in mind that Identity Server does not, or should not, provide that information. It's very much an anti-pattern if you fall into that trap. AdminUI is a new product. I think it's in beta. But basically, it's a way of managing your users. So, remember our Identity Server, you've got a lot of responsibility. So... Basically you need to do a lot of things. Which is why they've brought out AdminUI, because that takes away the management of these users. Now, this is a pay for service. Don't ask me how much. But it is relatively cheap compared to other offerings out there. And it comes bundled in a docker container, so you just download it, spin it up, and then I'll be able to demo that in a sec. So, whilst that's loading... Another product that they're bringing out is IdentityExpress. And that's just a way of you getting Identity Server up and running without having to do anything. So, they're almost going to like, an Identity Server as a service, sort of model. Again, this is pay for. I think this one includes AdminUI, but you can find... You can definitely find out more information on their website. But they're two great products to keep in mind, keep an eye out for. If... Has anyone heard of Auth0? So, Auth0's been around for ages. And it pretty much does what Identity Server does, but it does it in the cloud. So, you configure your app to talk to Auth0, and all your users are stored in Auth0, and you can manage them there, and it's really slick, and you can get it up and running in about hour. It's great, I've tried it, love it. The only problem with it is, you know, you're lacking on certain customizations. And it's expensive. So, if you get to a point where you're like, 10,000-ish users, you're going to get a big bill. And this is why we started looking into Identity Server with one of our clients, because we anticipated a massive growth in users. By all means have a look at it. It might work for your application, if you anticipate quite low users. But for us, it didn't work. So, that's the end of the slides. Let me see if I can just show you this AdminUI, real quick, and then you can have pizza, I promise. So, we've got basically a load of apps listening on different parts. Let me just find out which one I need to be looking at. I think it's 5,000. So, again, you've got an identity server, there. As you'd expect, so we went to port 5,003, we've gone back to port 5,000. We're back in AdminUI. And here, you will eventually see your users, very slow, very slow. So, there is our user, the one we've just logged in, as. And it just adds this sort of UI, so you can... You might be able to add different claims, you can make this user active, inactive. Again, roles, doctor or patient, not can I do this particular thing? You can also configure the clients within this, which is quite nice. So, if you anticipated this being used by a lot of clients. You know, you're onboarding clients like, every day, and you don't want to be doing that manually, and this kind of automates it. If you want to make your own APIs to do that for you, it's completely customizable. So, it offers that sort of functionality. So, we've done it in a roundabout way, but we've got to the end, and the pizza's here. So, yeah, thanks for listening.