Authentication Part 1 - Individual User Accounts

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
We've been in looking at a number of recent  videos at Web Api applications. And something I've been promising is that we're going to   look at securing those with authentication  and authorization. Authentication is the   process of verifying that a user is who  they claim to be and then authorization is,   once we know who they are, deciding what  they're allowed to do, because different   areas of our Web Api application may have  different levels of access. And now there   are so many ways of doing this - so many  different variations. The one I'm going   to focus on over these videos is using things  called JWTs - JSON Web Tokens. But even then,   there's a lot of variation in how we can do those  things. So I'm going to start off in a number of   videos - 'cause it'll take a little while to get it  working - going through the most basic way,   and then we can see some of the variations. And  so what we're going to do in this first video,   is see how we can get a hold of these JSON  Web Tokens that we can then use to access the   site. Now, if we take a look at Visual Studio,  and if we just go for now for a new project,   and go for a Web Api application, click on Next  there, click on Next there. And then you can see   that we have this Authentication Type. And we've  got a couple of options in there, apart from None.   We've got Microsoft Identity Platform and Windows.  And those will generate for you your application   with some of the basic code required for those in  there. But I'm actually going to use a different   approach, I'm going to use probably the simplest,  a thing called Individual Accounts. And that's   where we actually store the user information -  with their password, their login - in the local   database, just like you'd store any data for the  rest of the application. And I'm going to do this   without doing code generation when I create the  application. I'm going to add it to an existing   application that doesn't have any authorization  or authentication code in there. So that's what   we'll do. So if we just close that down, and  then here, I've got an application - basically,   what we've been looking at for quite a while. So  I've got this idea of BookReviews. So there we've   got a BookReview, we've got a ReviewContext  - Entity Framework. We're connecting to a   SQL Server database. I've already populated  that database with some data. And then we've   got a BookReviewController that gives us  access to that. And so when we run that up,   we just got the Swagger interface, and we can use  that to test the various methods. And there we've   got some books coming out. And if we just take a  quick look at the database for that, then we can   see in here we've got this AuthenticatedLibrary  and in there, if we look at the data, we can see   we've just got the one table, and there were the  BookReviews that we were looking at. So let's   actually turn this into a secured application. And  the very first step I'm going to take is simply   on that BookReviewController, I'm going to put  an attribute ‘[Authorize]’ … and just get hold   of the namespace. And so that now means that this  controller, none of the methods in the controller   can be accessed unless the user is authorised.  Unless they've been logged in. So we're going   to look at some variations on that in a later  video. For now, it's just all or nothing - they   can either use it or they can't. And as soon as I  put that in, we're going to see that when we now   run it up with Swagger, or with anything else,  that now when I try to access one of these,   what I get back is, in this case, a 500. We've  not actually activated authentication at all,   so we're not allowed in there. But it's  impossible to get in there, because we've got no   authorization mechanism. So what we need to do is  start introducing that. And so, as I say, what we're   going to do is we're going to introduce this using  our own database. So I'll pop in another folder in   here and I'll call this ‘Authentication’. And then  the first thing I'm going to do is put in here a   class to represent the actual user. So we'll  have a class and we'll call this ‘LibraryUser’.   And then in there, I'm going to derive that  from a base class called ‘IdentityUser’.   And we'll get that and what you'll see  there - if we just take a look at that,   and then look at that one - we can see that that  has lots of properties, like it's got an Id, a   Username and EmailAddress, some other things like  that. So the basic information we're going to have   for our user. And when we derive from that, what  we're doing is adding any additional information   that is specific to our application, which you  may not have any at all, but in this case, just to   have something, let's pop in there a ‘bool’ which  we're going to say is ‘RatingsAllowed’. So is this   user allowed to add new ratings to the system?  And then also let's have another property,   which is going to be a collection of those  BookReviews. And we'll just call that ‘Reviews’.   So that's going to be the set of reviews that  this particular user has created. And because   that's a reference type, it's given us a warning,  so I'll just put ‘required’ on there. And as we   saw in an earlier video, that's a way of avoiding  getting the warning about nullable references   when you're in a class like this. So that's our  user. And the next thing we're going to have to   do is we're going to have to now take that user  and include it in our DbContext. So you will   sometimes see a completely separate DbContext done  for the authentication compared with the regular   data in the application, but I'm going to put them  together. And so I need to do a couple of things.   One is this ReviewContext, I need to change the  base class so that it's no longer a DbContext,   it's a thing called an IdentityDbContext. And  then we've got to install a new package for that.   And we want to go for that first one there, the  ASP.NET Core, Entity Framework Core identity.   So we'll install that and that's all happy. And  then also, we need to add another DbSet to deal   with our LibraryUser that we just created. So  we'll say just ‘LibraryUser’ and we'll call that   ‘LibraryUsers’. And then just  get hold of the namespace.   And so that's our DbContext. And we're now in  a position where we can actually add that to   the database. So the usual way of doing this,  we just need in here to do a ‘add-migration’   and we'll call that ‘AddUsers’. So you can  see we've already got the migration that I   did to create the initial database with  the reviews. But now we can do this one.   And then we'll do an update database   and that will generate the new tables in the  database as we can see. And then once it's done that, if we go back and take another look and  just refresh that, we can see that it's given us   a whole load of new tables that are related to  this, I'm not going to go through all of these,   but the key one to look at is the AspNetUsers. So  if we take a look at the structure for that one,   we can see that it's given us a lot of those  standard columns that we had from the base class,   that's where we're getting Discriminator and  Email, NormalizedEmail. But you can see it's also   got that RatingsAllowed, which remember was the  extra property that I put in there. And also, if   we now take another look at the BookReviews table  and look at the designer for that, it's got a   nullable LibraryUserId. So remember, I put in the  LibraryUser, it had a collection of BookReviews,   therefore the BookReview has that in and it will  set up the foreign key that hooks those together.   So the tables it's generated are integrated into  the existing tables. And you could do that in all   sorts of ways - we'll make some use of that  in later videos. So that's us generated the   database. Now what we have to do is allow users  to register with all of that. So that's what we're   going to do next. And first thing we need to do  is a little bit in the Program file. So if we just   go to Program, and then in here, I'm going to put  just after that - doesn't really matter where, but   there we've created the DbContext. So I'm going to  say ‘builder.Services’ and then we're going to say   ‘.AddIdentity’. And then that identity is going to  be generic, it's going to take the ‘LibraryUser’   that we generated, that we've got there. And  then also a thing called an ‘IdentityRole’,   which we've got there. And then I'm going to add  into that one option. So you can do all sorts of   options here, but the one that I want to do is  simply the problem that by default, you're not   allowed to have spaces in user names. So what I'm  going to do is just change that. I'm going to say   ‘options’ and then ‘.User’. And then there's lots  of things we can have in here for configuring the   rules of what can go in here, but I'm going to  go for this ‘AllowedUsernameCharacters’, and I'm   just going to say ‘+= “ “’. So now we're going  to be allowed to have spaces in the username.   And then also going to chain onto the end of that,  I'm going to say ‘.AddEntityFrameworkStores’ and   that one's going to be using our ReviewContext.  And then also going to add on to the end of that   ‘AddDefaultTokenProviders’. And all of that  will just configure our middleware so that   we can use this identity in the system. Now  I need to add a couple of new data types. So   back in Authentication, I'm going to add a  class that I will call ‘RegistrationModel’   and so this is going to contain the data that we  need for a new user to register. And so we'll have   a ‘prop string’, and this one, I'm actually  going to call ‘Username’ rather than ‘Name’.   Again, make that ‘required’. And then I'm going  to have another string called ‘Password’. And   then we'll also have the ‘Email’ address. And  on that, I'm going to give that an attribute   of simply ‘[EmailAddress]’. And all that means is  that Swagger and tools like it can know that this   is supposed to be an email address. While we're  doing that, let's add another model, which we'll   be using a little bit later on. But this one is  going to be called our ‘LoginModel’. So we've got   one that we use to hand over the data when we are  registering, but we're going to have another one   who's to hand over the data when we're logging  in. And that one is going to be similar but   different. That's just going to have the Username  and Password; we don't need the email address to   log on every time, we just need that to register.  So we'll just pop those two in there, then we're   going to need to add another controller. So we're  going to add one, and we're going to call this   ‘AuthenticationController’. And then  the first thing we're going to do is   put in this idea of registration. So to do  this, we need to have a ‘private readonly’   a thing called ‘UserManager’, and that is generic  for the user type that we just created, so our   LibraryUser. And we'll call that ‘_userManager’.  And then we'll pop in a constructor that will have   that injected into it. So we can just generate  that constructor like that, then we'll have our   register. So this is going to be a POST method, so  we'll do ‘HttpPost’ and then because we're going   to have more than one POST method - actually,  the login is also going to be one - we need   to give it the name just to distinguish it. So  that's ‘Register’. And then that's going to be   ‘public async Task<IActionResult>’, so nothing  unusual there. And we'll call it ‘Register’.   And it's going to take from the body, this  RegistrationModel that I just created,   and we'll call that ‘model’. And then what we're going  to do is firstly check to see whether there's   already a user with that username, which won't  be very good. So we'll say ‘var existingUser =’   and then ‘await’. And then that's where this  _userManager comes in. Because we can say   ‘FindByNameAsync’ and then we'll simply pass in  the ‘model.Username’. And so that will check for   that. And then we can simply do ‘if (existingUser  != null), so if there is an existing user, and   then we can have a ‘return’. And the best error  for this is ‘Conflict’. And then just a message   ‘User already exists’. Want to give back the  minimum amount of information because a hacker   could start making use if you told them any more  than that. But you've at least got to tell them   the user exists so they know they've got to  go for a different name. And because we've   returned Conflict, we also need to be putting  on the ‘ProducesResponseType’ that we looked   at in earlier videos. And that's going to be  ‘StatusCodes.’ and then if we find Conflict in there,   that's going to be ‘409Conflict’. So we've bailed  out if there's already a user, but now we need to   create the new user. So we're going to say ‘var  newUser = new LibraryUser’. And then in there,   we're going to say simply ‘Reviews =’ and then  we'll just have an empty ‘List<BookReview>’.   And then the ‘Username’ is going to be  ‘model.Username’. And then the ‘Email’ is   going to be ‘model.Email’. And then something we  also put in there - and this is something from the   base class - we put in a ‘SecurityStamp’ that  basically just means that you've got something   unique for each one of these, so we do this  with a GUID. So ‘Guid.NewGuid’ and then turn   that into a string. Okay, so that's our new  user created, then we need to actually add   that in there. So we can say ‘var result =  await’ again on the ‘_userManager’ and then   ‘CreateAsync(newUser’ and then also we pass in  separately ‘model.Password’ in there. And that   will create the user with that password. And then  finally, we can just say ‘if (result.Succeeded)’   then we can simply return an  ‘Ok(“User successfully created”)’.   ‘else’ we're going to return a status code  of ‘InternalServerError500’ and then bit of   explanation on that. So we can say something  like ‘Failed to create user.’ and then we can   put all that together with a ‘string.Join’.  And then we'll just do a space separation   of from that ‘result.Errors.Select’ and then  ‘e => e.Description’ and then on the end of   that a semicolon. So that may again be giving out  too much information, but for now, when we're   developing, I think that's worth having. And  so we just need just to be complete on that,   we need to have the status of ‘200OK’ and then  we also need the status of ‘500InternalServerError’.   So those are the possibilities. So now we've got  the ability to register. So let's run that up,   and you can see we've got that new Authentication  controller with the Register method. And we can   see there - because remember, I put that email  attribute on the email property - then we can see   it's showing, as an example, the correct format.  So let's try that out. So I'll go for Username,   ‘Jasper Kent’ - so remember I allowed spaces when  I was setting it up in the Program. Password,   let's just go for ‘Pa$$w0rd’ - so a standard  test password. Leave the email the same. And   if I execute that, then we're getting back ‘User  successfully created’. And if we take a look back   in Visual Studio and look at the database  again, and if we look at this AspNetUsers,   then we can see we've now got that one in there.  And you can see the information that I've put in   there. And also just worth pointing out is the  password is encrypted. This uses a technique   of hashing - sometimes referred to as one-way  encryption - and that basically means that even   if somebody stole this database, they could not  look up their password information. I may do a   video later on explaining exactly how that works.  But it is a very secure method that we have in there. So   we've registered. Now having registered, we need  to log in. So that's something else we're going to   have to add to the authentication controller. And  another class I'm going to put in here, actually,   I'm going to add a new class, and this one I'm  going to call ‘LoginResponse’. And on there,   I'm going to have two things, I'm going to have  a ‘string’ and that string is going to be this   JWT - this JSON Web Token that I've been  talking about is the key to this whole thing.   And because it's a string, we'll make it ‘required’.  And then I'm also going to have another property,   which is going to be a ‘DateTime’ which is going  to be an ‘Expiration’ date, because as we'll see,   the thing about a JWT is we set it up with  an expiration that typically could be a few   hours ahead - whatever you like, depending on your security issues. But we can create the token and   that will then be a valid login without any  further reference to the database until you   go past that expiration. And we’ll see how that  works over this video and later videos as well. So   that's what we're going to return from our login.  And so if we go back to our AuthenticationController,   and so now we're going to have another POST  and this one, we're going to call ‘Login’   and so we'll have a ‘public async  Task<IActionResult>', call this   ‘Login’. And this one is going to take, again,  from the body, we will have the ‘LoginModel’.   And then once again, we're going to try and get  a hold of the user with that particular username.   Not going to call it ‘existingUser’ anymore,  because that's kind of obvious in this case, so   just call it ‘user’. And then we've got to verify  that they've got the correct password. So we can   say ‘if (user == null)’. So if we don't have a  user at all with that name ‘||’ and then ‘!await’   and then ‘_userManager’ once again, and then  ‘CheckPasswordAsync’ and we pass in there the   ‘user’ object and the password has been given  there. And so if either of those fails - so if we   haven't got a user or they haven't got the correct  password - then we simply return ‘Unauthorized’,   okay. Don't give them any information at all.  because you don't even want to tell them they've   guessed the username wrong or guessed the password  wrong. If it's a hacker, you don't want any of   that. And again, that's going to mean we need a  ‘ProducesResponse ‘on there. So if we grab that,   and put it in there, and that's going to be the  ‘Unauthorised’, which is the 401 that we have   there. Then if they've successfully logged in,  we need to give them some claims. So now, we're   going to say ‘var authClaims’ - so authorization  claims – ‘= new List<Claim>’. And then the main   claim that we need to give them is the claim that  they are who they say they are. So we do a ‘new   Claim’. And then ‘ClaimTypes’ - just get hold  of the namespace there – ‘ClaimTypes.Name’.   And that's going to be ‘model.UserName’.  And then also for security reasons, we   make sure that we add another claim. And this one  is we use ‘JwtRegisteredClaimNames’ - and again,   get hold of a namespace for that – ‘.Jti’ And that  one, we simply set to something unique. So this is   to prevent what's called a replay attack, so that  we know that every set of claims has this unique   stamp on it so it can't have this idea of a replay  attack. Again, details of that I may go into in   a later video if people are interested in that.  Okay, so now we've got to take that list of claims   and some other information and turn it into this  idea of a JWT - a JSON Web Token. And to do that,   we've got to put a couple of things into  the appsettings file. So if we go in here,   and in this we put in a new section called  ‘JWT’ - whatever we want to call it, really,   but that's the simplest one. And then in there, we  put a couple of things. We put a ‘ValidAudience’   and because we're only developing this, that's  going to be pretty simple. I'm just going to set   this up as the URL on localhost to this site.  So if we go for ‘https://localhost’ and then if   we just take a look in the launchsettings,  we can see that when we're launching this,   that's going to be on port 7203. So if I just grab  that, and go back in there. So that's that one   set up. And then I'm going to do exactly the same  thing, only this one is going to be ‘ValidIssuer’.   But then the really important one is we need to have  a ‘Secret’. And basically, this is the encryption   and decryption key for the JSON Web Token  we're creating. And that just has to be a long,   effectively random string of characters. And  although there's lots of ways you can generate   this one, quite an easy one we can do is if  we go to a site called ‘grc.com/passwords’.   Then when we go there, we can see that just each  time we go there, it generates a brand new set   of passwords. And the one that I'm going to go  for is this 63 digit, alphanumerical one. So if   I just copy that, and then that is going to be my  secret that we have in there. And so that's going   to be of use. And obviously, you want to keep  that secret, not let anyone know that because   that could break the entire of your security.  So let's now go back to the controller. And so   what we're going to do in there is we're going to  say ‘var key =’ and then we're going to say ‘new   SymmetricSecurityKey’, we're going to use ‘UTF8’  encoding - get hold of the namespace again -   and then we're going to use that to get as a  string of bytes. And then we're going to need   to get hold of our secret. Now to get out of our  secret, I'm going to need to have access to the   config file. So up here we'll put in another  injectable object. So I'll have a ‘private   readonly IConfiguration _configuration’. We'll  add that in to the existing constructor because   that'll get injected. And then down here, those  GetBytes, we're going to say are going to be on   ‘_configuration’ and then we're going to read  out that ‘JWT:Secret’ that I just put in there.   And that has a slight chance that could be  null if there was nothing in there, so we're   going to just put the null coalescing operator and  throw a ‘new InvalidOperationException’ and just   ‘Secret not configured’. Unlikely that that's  going to happen, but we've got to cover that   just so it's safe in there. So that's going to be  if we throw an exception, that will be a response   type of 500. So we'll pop that one in there just  for completeness. And need another close round   brackets. So that's basically just taken that  and turned it into a string of bytes. And then   we can say ‘var token = new JwtSecurityToken’. And  then in there, we're going to have the ‘issuer’.   And remember that issuer is something else  we put in our configuration. So we'll say   ‘_configuration[“JWT: Issuer”]’. We're going to  do the same sort of thing with the ‘audience’,   which we also put in there. And then we're  going to put on this idea of the expiry time.   So remember I said this token is only going to  be valid for whatever time after it's issued.   So that's easy enough ‘DateTime.UtcNow’ and then  ‘.Add’, and let's go for ‘Hours’. And let's give   that three hours. So once I've logged in, this  will be valid for three hours after that. We   then also put in those claims, so we say ‘claims:’  and then that set of claims that we just created,   so ‘authClaims’ there. And then finally, we  have ‘signingCredentials:’ and we put in a ‘new   SigningCredentials’ and that takes the key  that we just created based on our secret,   and then also the algorithm we're  going to use. And we're going to go for   SHA 256, which is a fairly standard one.  You could do it in different ways. As long   as you're consistent when you're encoding and  decoding, that should be fine. So that's all of   that. And then finally, we can return that token.  So we simply say ‘return’ and then it's an ‘Ok’   and then remember, we created that LoginResponse  precisely for this. So there's our LoginResponse   and what we're going to do with that is simply  say the ‘JwtToken =’ and then we've got to do a   ‘new JwtSecurityTokenHandler.WriteToken’ and then  passing that ‘token’. So that basically turns it   into a string, which is what we want to send back.  And then also, remember, we've got expiration   on there. And we can get hold of that just by  saying ‘token.ValidTo’. And so that's handing   back the information we want. So that's now our  login. And I just noticed I got that wrong, what   we have in the config file, were ‘ValidIssuer’  and ‘ValidAudience’. So let's run that one up.   And we've already registered, so if I just go  to login and try it out. And then remember,   my username was ‘Jasper Kent’ and my password was  ‘Pa$$w0rd’. And if we execute that, then you can   see there is response. So there is when it expires  in three hours time, but there is the token   itself. So that is information, which we'll see  in the next video - because we're running out of   time here - will allow us to get into that secure  controller for the book reviews or whatever it may   be that we want to. But just before we finish here, there's one way we can take a look at what's   actually going on in that token. Because if we  go to another useful website called ‘jwt.io’,   and that allows us to actually enter that JWT. So  you see there's a sample one there, but let's take   the one that we had there, paste it in, and it  will partially decode it for us. So there's three   sections hidden away in there, separated by those  dots. So the first section in red, that is simply   Base64 encoding of the information it's telling us  there, so the algorithm that was used and the fact   it's a JSON Web Token. Then the purple section,  again, is simply Base64 encoded, so there's no   secrecy about it, it's just a compact form. But  that's telling us the information that we passed   in there. So we can see in there the two claims  that we had of the name, that unique GUID that we   created. And also we can see the expiration that  we've got in there as well. And then we've got in   the blue bit, we've got the actual encrypted  part. And so what's in the encrypted part is   essentially a checksum of that payload data, then  encrypted using the secret that I'd got generated   and stored on the server. And so that means that  although it's perfectly possible to tamper with   any of the information in the red section, or the  magenta section, when we then verify the token,   the system would spot it because only it can  decrypt that secret section. And when it did,   it discovered that the checksums don't add up  and therefore somebody has tampered with it. So   what we've got here is a way that we can prove  that we are this person who logged in and what   our claims are. And we'll have that valid  until the expiration time. Now, obviously,   you as a user want to keep this token secret just  as much as you want to keep your password secret.   So there is a responsibility on that anyone who  has this token can now impersonate you, but only to   the extent that anyone with your username and  password can. So that's the security we've got.   But we still haven't got to a point where we  can now actually send that back and allow us   to get into the site. So we've still got  the situation that because I put that   ‘[Authorize]’ on the controller, that we're  getting this error when we tried to access   the data. And so that's what we're going to  be looking at next time. But in the meantime,   hope you enjoyed that. If you did do subscribe,  do click ‘like’ and wait for the next video.
Info
Channel: Coding Tutorials
Views: 4,313
Rating: undefined out of 5
Keywords: ASP.NET, JWT, JSON Web Tokens, Individual User Accounts, captioned
Id: ZubGYVHTI3Q
Channel Id: undefined
Length: 30min 54sec (1854 seconds)
Published: Fri Jun 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.