User Registration - BUILD A JWT AUTHENTICATION SERVER (ASP.NET CORE) #1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
authentication is a common requirement for applications and in today's world of client server applications microservices it helps to use jwt authentication so that you can authenticate across various servers that said you're going to need a server that's responsible for managing users identities and signing jwts so in this series we're going to be building an authentication server that supports user registration logging in to get a jwt access token and refresh token refreshing an expired access token and logging out to invalidate all refresh tokens what are we waiting for let's hop right into it so here in visual studio i have an empty solution and we're going to add a new project and we're going to do an asp.net core web application we're just going to call this authentication server dot api because it's going to be a rest api let's go ahead and create that for our asp.net template we are just going to do an empty template so go ahead and create that and here we go blank asp.net core web application doesn't get much better than that so even though we didn't choose the mvc template we are going to have controllers that are going to be the endpoints for our authentication action such as registering or logging in so for our project we are going to add a new folder for our controllers and our main controller we're going to create here just a new item we will call this the authentication controller so just a class and we're going to inherit from controller so import that and obviously inheriting from controller will tell the application that this is going to have some actions on it that we want to map to routes now in order to map those methods to routes we're first gonna have to come over here to our startup.cs and we're gonna have to configure our services to add controllers and down here and use endpoints we're gonna map our controllers with endpoints map controllers and now back in our authentication controller let's start setting up our first endpoint so the first thing we want to support is user registration so we are going to have a method here to register a new user so it's going to return an i action result as all controller routes will and we'll just call this register and this action is going to be a post so an http post to the register endpoint so the user is going to be sending some data as part of this post request from the http body so we need to create an object that will hold the data that the user sends so here in my project i'm going to add a new folder for models and inside here i'll have a new folder specifically for requests and we'll create a new class and this will be a register request so what data is the user going to send to register well in this case they are going to need an email of course this all will vary based on what your application requires but in this case we're going to have an email we'll have a username we'll have a password and we'll have another string to confirm the password now while i'm here i'm also going to add some data annotations to these properties to validate the data so an email is going to be required and it needs to be an email address and then for all these other properties i'm just going to mark those as required you can have additional annotations if you want for example for a password you might require it to have a minimum length but in this case we're just going to keep it simple so now we have our register request and a user is going to send a json object that matches this structure in the body of the post request so to get that information we are going to use from body and we are going to deserialize the body into a register request now we are not going to do that because asp.net does that for us but this is how we set that up so now we get all the details about the registration request and now that we have the register request information we can actually do the registration so first off we're going to validate those data annotations that we set and we can simply do that by checking if the model state is not valid so this will automatically get set based on our data annotations and if it's invalid we'll just return bad request and then another validation that we have to do manually is making sure the password matches the confirm password so if it doesn't then we're also going to return bad request so if we get past all that everything's valid and we can start registering the user except we need to actually make sure that a user doesn't already exist for the email that the user is trying to register with and in my case i also want to make sure the username hasn't already been taken by another user as well so who are these existing users that i speak of well we're going to need a way to get users and we're also going to need a way to create a new user when we actually submit the user's registration and to do all that we're going to create a repository so i'm going to create a new folder in my project and this is going to be services and inside here i'll create another folder for user repositories and i'm going to start off by creating a new interface for my user repository so and i use a repository and what kind of methods is our user repository going to need well we're going to need a get by email so that we can see if the email of the user registering has already been taken and we're also going to need a get by username to see if the username was taken and both of these are going to return a user object which we haven't created so we're going to need a user object that is going to represent the users that we have stored somewhere so let's create a new model for that user so just a class for a user and our stored users are going to have ids which are just going to be guides they're going to have emails they'll have a username and lastly they'll have a password but obviously this is going to be a hash password so we'll call it a password hash because we don't just want to store that in plain text got to be secure so there's our user back in our user repository we can import that and now back to our controller we can get one of those user repositories in our constructor so we'll create a field for that first and generate a constructor to get that and we're going to come back and implement this interface in just a second but back to our register method we're going to use our user repository to get an existing user by email so user repository get by email and we'll pass in the registering users email and now if this returns an actual user so it's not null then that means the email already exists and we're going to return conflict and i'm actually going to copy this and do the same exact thing for username so we'll get an existing user by username using get by username and if that does not equal null then we're going to return a conflict again but if all of this passes then it is finally time to create the user and store it in our repository so back on our user repository we're also going to have a method to create a user and that'll take in the user that we want to create and it'll also return a user and this user that gets returned is going to have the new id that gets generated which i don't think we're going to use but that's typically how i set up my repositories so now back in the controller let's create our new user object so i'll call this registration user and let's populate all these properties so the id that'll get populated by the repository when we create the user but the email that's going to come from our request email the username will come from our request username and then what about this password hash well to do this we're gonna have to hash the password so let's do that before we create this user we'll create a string for a password hash and we're going to need some kind of password hasher to hash the user's password and i'm not actually sure how i want to implement this but i am going to come over to my services and add a new folder for password hashers and i'll just create a i password hasher interface as a placeholder for now so i password hasher and we'll make that public and that's going to have a method that'll return a string and it's just going to hash the password and take in the user's password that they want to hash and now back in my authentication controller i can get an i password hasher up here so create a field for that and we'll add that to the constructor let's add a parameter and now down here i can use that password hasher and just hash the password that we get from the register request and then set that for this property down here so now that we have our user we can use our repository and simply create the user and then we are all done so we can just return okay so pretty simple for registration so we have this action mapped to the register route the user sends a post request to this route with a register request object that has the structure and then we validate our data annotations on this register request we make sure the password and the confirm password match we make sure the email and the username aren't already taken by existing users and then we just hash the password and create the new user and then we just return ok for success now i really don't like the way i did this because i want to test this how it is but in order to test it we have to implement these interfaces and then register those services in our configure services in our startup so let's implement these services the password hasher and the user repository now for the password hasher i'm not really like a security pro so i think i'm going to find a library that will do that for me so let me go to nuget packages manage nuget packages all right so i did some looking around and i'm going to go with bcrypt.net dash next i have used bcrypt before so i'm feeling pretty confident that this will get the job done so let's download that and install it obviously if you want to use a different hasher then you can just implement our password hash your interface differently so i'm going to create a new class here in the password hashers folder and i'll call this the decrypt password hasher and we'll implement our interface all right so the documentation is pretty good for bcrypt so to hash the password all we have to do is bcrypt.net.bcrypt.hash password and then we just pass in our password so our interface method signature actually matches the bcrip method signature word for word so that's pretty nice and now in our startup.cs we'll add this as a singleton the password hasher is going to be a bcrypt password hasher and now we need to implement our user repository so obviously in most cases you want to use a database to persist all this data but i really want this video and this whole series to really just focus on the authentication so i'm just going to implement this as just a placeholder in memory user repository and then maybe later down the road in this series i'll have like a bonus video where we implement a database using entity framework but for now just want to focus on authentication in the series and if you have any framework set up then you can just implement this interface for the i user repository using any framework but for now in memory user repository so let's implement that interface and i'm just going to store all my users in a list call that users and we'll just initialize that right up here so for git by email we're just going to take our users list and use first or default and get the first user that has a matching email so if the email equals the email that we're searching for and then if a user with that email doesn't exist then it'll just return null now i made these tasks so that these methods could be implemented asynchronously in case you wanted to interface with the database or file or wherever your users are stored so to return this user we're going to have to use task from result and that'll give us back a task for a user and now same thing for get by username except we're looking for the first user with the username and then for create we're going to add the new user to our list of users and before we do that we need to give the user an id so we'll just generate one of those and then return the user with task that from result cool so there's our repository let's register that in our services so an i user repository just an in-memory user repository and now we can test this out so let's go to our authentication controller and let's put some breakpoints here all right application is running now how the heck are we going to send the post request to this register route well to do that i would recommend downloading postman which we have here i'll put a link to that in the description and we can set up a request and it's going to be a post request to our api which is running on localhost port 5001 for https so let's copy that paste that in there and we are going to hit the register endpoint and we have to pass in our registration data in the body so i'll select raw here and we're gonna send this as json and we need an email so i'll do test.gmail.com username i'll do singleton sean password i'll do test and then just copy that and the confirm password for test as well all right here we go moment of truth let's send our request oh we hit the breakpoint okay so our model state is valid let's continue again so our password confirm password matched a user with our email doesn't already exist a user with our username does not already exist we create the user return ok there's our hash password so that worked out pretty well and we get our 200 status code nice and now if i try to register again so our model states valid passwords match but then we get our 409 conflict because our email is already taken and then if i change the email i get another 409 conflict because the username is already taken but then change the username now we're getting okay again because everything's different the passwords don't match get a bad request if our email is not an email we get a bad request because our model state fails and there we go our registration is working now one thing that i don't like is that our failure status codes don't really give us any data or messages back about the failure so to fix that we can actually pass an object into our bad request and conflict methods and it's going to serialize that object and send it back to the user so let's create a response object for our errors that we can send back to the user so create a new folder in my models for responses and i'll call this error response and i pretty much do this for every rest api i make so i have my error response that's going to have a list of error messages and i'll just have those as strings sometimes i create an actual object and i associate the error messages with error codes which is useful so the user can get that error code and then create whatever error message they want and then even though i have this property for a list of error messages i like to create a constructor that supports just one error message because it'll be a lot easier to use and then i have another constructor that takes the list of error messages as well and then this constructor is pretty straightforward just set the property and then for the single error message constructor i'll just call this constructor down here with a new list of string error messages and just pass in my single error message as the content of that list and then i can just use that pretty easily in this controller so for this conflict i'll return a new error response and just say the username already exists if the email already exists i'll say email already exists if the passwords don't match then i'll say password does not match confirm password and then model states a little bit tricky so we can have a list of error messages which we're going to pass in so to get those error messages we can use model state dot values and then select many and then each of those values has errors associated with it and we'll map those errors and get the error message and that pretty much just gives us back every single error message associated with our invalid model state and we can just pass that in so now if i register first time success second time email already exists different email username already exists non-matching passwords password does not match confirmed password and then bad email invalid email address so first step of our authentication server is complete we support user registration we simply validate the registration hash the user's password and create them in our user repository and now that the user has registered they're going to want to log in which we're going to take care of next time so if you have any questions criticisms or concerns be sure to leave them below in the comments section but other than that thank you for watching and stay tuned for more
Info
Channel: SingletonSean
Views: 12,665
Rating: undefined out of 5
Keywords: asp, net, core, jwt, auth, authentication, server, api, rest, controller, mvc, web, application, programming, tutorial, learn, register, login, logout, refresh, token, access, sign, verify, hash, password, username, email, data, annotation, validation, model, state, error, message, clean, code, service, .net, restful, design, patterns, architecture, software, development, security, require, visual, studio, c#, project, setup, solution, postman, body, http, request, response, object, client, micro, services, azure, cloud, microservice
Id: vNaDR2fPLKU
Channel Id: undefined
Length: 17min 22sec (1042 seconds)
Published: Sat Dec 05 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.