Setup User Registration In ASP.NET Core API | Ultimate ASP.NET Web API Tutorial For Beginners

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hey guys welcome back so we're going to continue our journey into setting up our authentication for our api and we'll be setting up this endpoint for user one registration and two login and well by extension authentication now i've already kind of cheated and gone ahead and set up the new controller so you can pause and go ahead and get up to speed right click controllers go ahead create a brand new controller that's blank and call it account controller and i already went ahead and kind of started doing my injections so two you're familiar with two you would be seeing for the very first time so you already know the logger and the mapper so i don't need to explain those in any great detail however you would be seeing user manager and sign in manager potentially for the first time these are two built-in libraries courtesy of identity core so when you type them you may want to include the missing reference and you'll see those start to appear up top now notice the context for the user manager and for the sign-in manager is api user or whatever the custom user class is that you would have used when you were setting up identity if you didn't use one then you continue to use identity users so whatever class it is that you set as your context in your configuration and the configuration would be here whatever class you set there you you continue to use that throughout the application all right so user manager gives us access to a bunch our suite of functions that allow us to manage sign in retrieve user information add users so we don't have to write any custom code to be adding or i don't have to go and put in any unit of work functions for user user table interaction or role table interaction all of those things come out of the box with the user manager sign-in manager and you have another one called the roles manager if we need to use it then we will but i'm just letting you know that most of those things are encapsulated in these services which can easily be injected into our application so now that we have set up our controller and injected all of what we need we need to start writing our endpoint so our first endpoint is going to be for registration so it's going to look something like this public async task i action result and the name is register for registration since we'll be requiring potentially sensitive data well not potentially will be requiring username password and so on i don't want to send that across the pipe in the parameters so we discussed that in the overview video where i was saying that using the parameter is quite fine when it's something like this just an id nothing too damning right but then when you get to asking a user to register and submit sensitive information you don't want to just start saying string email string password etc because then everything will actually come across in plain text over to the to the endpoint so what we want to do is make this one a post so we we have done work with the get verb we haven't done much work or any work with the post so a post is just like when you're submitting a form on the internet when you click submit to go to let's say sign up for facebook or sign up for something online if you look in the url up top you don't see any of the information being listed out you you just know that your information left one page and supposedly when somewhere and the other page could represent it that's what a post operation is right so the message is encapsulated and kind of hidden from prying eyes when we do a get it is sent across for in plain sight just like what we saw here so there's no reason to hide so of course when you're dealing with sensitive information you don't want to use a get or anything that will not hide the information that's being sent across so with that considered the parameter that we're going to use here is going to be a custom one so i'm going to put in an annotation to say get the information from body so the sender needs to send it in the body as opposed to in the url in the body of the request and then we can specify a data type that should take the information so i'm going to say user detailed so i haven't created user dto as yet but i'm just going to say user dto all right so let us go ahead and create this user dto and then i'll explain why it can work like that so in models we click add class we're calling user dto and then user dto needs to have at minimum the same fields that we one need to map back to our api user and two we need the user to provide so this is how i am designing my user details so you can pause replicate it but i'm going to walk you through it anyway so first name last name when registering you have the option to pass those two fields over notice i said option because i'm sitting neither as required we have the phone number so if you want you can send us your phone number when you're registering for the api once again not required however email is definitely required and password is definitely required and then for email we'll actually be using this as the username also so if we have this detail as usual we have to make sure that our mapper knows about the dto so mapper initializer i'm just going to go ahead and say that api user and user dto need to know about each other and reverse map all right and so that is what we will do so the purpose of the from body here is to say that when when a request hits this end point you will look in the body of the request don't look in the url so one i'm not requiring anything in the url and two if the user passes anything in the url i don't care i'm not looking for any information in the url however the body of the request should contain information with fields that match up to the user dto so they send more information than i need it will be ignored but at minimum they must send these two for me to process it so that's how everything kind of ties in when we talk about sanitizing the requests and making decisions based on what is coming across the pipe all right so let us go back to our account controller and then what we want to do at this point and we can do a number of things so i'm going to firstly try and log and say uh registration attempt came in for and this i copied and pasted that so i kind of printed myself but let's say registration attempt for user dto dot email all right so whatever email came in over the user dto that's what i'm putting there so i'm going to also check if the valid state if it is in a valid state so i can say if model state so this is how we verify forms right if you do if you have done mvc and they say you're just learning api it's pretty much the same process we're going to say if model state is valid right so at this point i would probably say if it is not valid then i'm going to return a bad request so that means you sent over a request to register but your validation failed so you didn't include the email it didn't include or you didn't meet whatever standards i would have laid out for you regarding the data you should have sent right and then i can return the model state so that it will kind of inform the sender what went wrong all right so if model state is not valid then we do that otherwise we can proceed to try so just try tap tab and i'll catch that exception so let me just deal with the exception part of it where i'm going to say log arrow something went wrong in that register section and then in previous times what did we do we returned with the status code 500 so another type of return that you can do when there was a problem is literally return problem and you would put in the same kind of message so i could put in something went wrong right and then tell it that it has a status code of 500 so i'm just showing you that there are different ways you know if you are looking at other resources you may see me do it this way and somebody else returning a problem somebody is returning the in the status code manually there are different ways to do this all right but then for the ones that are built in and i already give you the methods i generally encourage you to just use those so we've taken care of the bad situations i like to just say if it is not right do this now what do i do when it is right one i can say var user is equal to mapper dot map into api user and then i am mapping user dto so then it will say okay match all the fields which we know already we know how the mapper works by now and then now that i have the user i can say var result is equal to and i'm going to await a call to my user manager dot and then here's here's a bunch of functions like i said we can do a number of things i can say create async so user manager create async and then i'll just pass in the user object all right so go ahead and create that user it will automatically take it take the password hash it store it do everything it needs to do we didn't have to write any complex logic to really facilitate that right but then sometimes things fail so i'm going to say if not result dot succeeded right it will tell you did it succeed or not then i'm going to also return a bad request so you know i'm going to say something went wrong this time i'm not it's not a model state thing it's probably just an arrow so the thing is that the result object actually gives you the errors if you really need to list them out you can probably put them in a loop list them put them in one string and return them if you want if you want to give the user that much detail but sometimes those details can be too sensitive so you have to be careful what information you're sending back to the calling client right so at this point i'm just going to say bad request it was a 400 and it is because the user registration field that's all you need to know so try again later because this could have been my fault otherwise if it was something serious would have given them a 500 to know it is on our side that the problem is and if it is that you send incorrect data then i'm telling you what is incorrect because the model state will list that out all right and that's pretty much it for the register now since we've done the register i think we might as well just do the login because the only real way to test our register is to test the login right so i'm actually just going to copy and paste this code right below but what we need to do is change the action name so this is a login action we're using the same user detail we're taking from the same url from the same body sorry ignoring anything coming in the url and then what we're going to do is say login attempt for this user if it is invalid once again validation constraints email password now one thing that we may want to consider is do we need the same dto or should we use the same dto for login and register same principle with the other dtos right because for login i don't need your first name last name phone number i only want your email and password so what i'll do here is quickly just do a class to say login dto and i'm going to take these two fields put in just the login because it's absolutely necessary for login stuff but then i'll let this inherit from login so the fields are shared across the board regardless all right so i should call that login user dto there we go let my mapper know that it is do i even need to let the map or no i really don't need to let the mapper know about this one because the operation is going to be slightly different so i'll leave user detail in the mapper but i won't put this one and i'll explain why so login user detail so once again this is another part of security when we sanitize exactly what we want because if the user may have included malicious sorry the client or well the user sending the request the client sending the request could have included malicious information in the body of the request by doing this part here i am saying that i am only looking for fields that match what i have outlined in this dto so if they send more information that i need it's going to be ignored regardless so that's another part of the security considerations when creating apis so we get the login user dto and then for the login okay well we validate it okay good so we checked the model state and then what i'm going to try so nothing much has to change here except maybe the log messages just to make sure that we are accurately representing where what went wrongly all right so under the try what we're going to do i don't need to do any mapping so let me just remove what's in try and start from scratch i am going to say var result is equal to and then this is where we use the sign-in manager dot password sign in async so you see that and you can even do a check you can check to see if the password would be correct if the person was trying to sign in of course that doesn't help the situation at this point because we want to actually sign in right so signing async takes two overloads we can pass in the whole user object i just took all the mapping and the password or we can choose to just put in the username and password which is the one i'm going to use so you see i have both options so if we kept the mapping we would pass in the whole user object as well as the password in this case i took it out so i'll just say user dto dot and we're using the email address as username and userdto.password and then is persistent so let's read what is persistence is it says flag indicating whether the signing cookie should persist after the browser is closed now i this is an api i don't know what kind of application is calling the api it could be postman it could be the browser it could be a mobile app i don't need to persist anything so i'm going to say false all right and then the last one lock on failure i don't want to lock anybody when they fail to log in that would be too much of an administrative overhead once again context is everything but this is what our sign-in code looks like now the result is going to be a similar object to the register attempt so we can just say if result or sorry if not result dot successful oh sorry this is asynchronous so when we fail to put on the await then the result that we're going to get is of type task not of the type that we are expecting so let me make sure i put on that weight and then when i do that you'll see that the intellisense is not giving me what i would expect so if it is not successful let me just double check what we did when it wasn't successful here then we will say we say bad request up top but if it's an unsuccessful login attempt it's not necessarily a bad response what i would just say or bad request rather what i would say is unauthorized all right which is a 401 if i'm not mistaken right unauthorized and i can probably just return the user detail to see this is what you attempted to use it's unauthorized i'm sorry all right otherwise if it gets this far that means everything is okay so we'll just return i could return okay um i've used accepted in the past right so anything with a 200 in the 200 range would be seen as an okay response so okay is 200 you have created um which is i think 204 you have accepted which is 202 etc you don't have to worry about memorizing these codes necessarily just know that in particular situations you want to use this one and not the other right so then that is what our login function will look like and okay i thought i made a change just now all right so let us oh the register is saying that not all code paths return a value oh that is why i am so sorry i missed this one out so remember that you always have to have a final return so the same way that we said the if statement does this but we return accepted afterwards i have to do that here so i could return accepted up top here also right i have accepted your registration attempt and then that is done now before we move on to testing i want to bring something very important to your attention and that is the routing considerations for this endpoint we already established that the route would be api controller in this case api slash account in previous times let's say the hotel controller we would have determined that we can't have two identical get operations the name of the action is irrelevant but i can't have two operations having identical verbs the verb can be the same but then there must be some nuance to one implementation of the verb from the other in this situation i have both as post both of them are post once again the name here doesn't matter but they're both post which means that if i attempt to do any post operation it's going to not know which one to use they're both kind of identical in terms of what they're expecting the only way i can really differentiate between these two is to give them their own roots right so i can say the root for register is register right and then the root for login is login so that way at this point the only way to hit register is to say server slash api slash account slash register as a post request the only way to hit login is to say whatever slash api slash account slash login so i'm specifying that root for this endpoint is login and this one is register that way they can have identical verbs but because there are different locations they won't interfere with each other all right so let's take this first pin let's try and register a user real quick so when we check out what swagger has generated for us you notice it doesn't append anything to it's like oh for id here and id there they are now post not get right and then if i expand they will show me what the object needs to look like or can look like when it is coming across the pipe all right just for just the same for login so you see the difference remember that we have two different dtos this one has email password and the custom fields this one only takes email and password so that's what i was saying that when in a situation when you don't need all the information you create a dto to limit exactly what you need in that situation all right so let us try with the registration attempt so i'm going to leave that that email i'm going to put in some password that has characters and a number and i'm going to leave everything else blank because all i really need are email and password however by leaving these as they are they're literally going to go over as string string and phone number so let's see what happens when we click execute all right and this attempt is giving me a 500 error so this would have been one of those exceptions caught and it's saying cannot resolve a particular service for the signing manager all right all right so the sign-in manager is giving a problem and i would like to sit down and troubleshoot it but when i'm thinking about it i really don't need the sign-in manager for this api and i'm going to explain so when i put in sign in manager and injected it and all these things it was because that's like a knee-jerk reaction whenever we have authentication to do we want all the libraries that can help with authentication right however in the case of a web application where somebody would submit a login items through a form click submit and then be told they are logged in through some message or you know some indication that says hello username that is really the sending manager creating something like a session or a cookie based on the configuration setup for managing that user session in terms of an api we are not going to be maintaining a session for any user yes you register but we don't know when you're going to be calling the api how long and it's not like you're going to be lingering inside the api for one or few calls the api is going to accept your request give you a response and be finished that is why we're using tokens so i don't want to get too much ahead of myself and start discussing tokens but because of the tokens we really don't need the signing manager in the traditional sense of what the signing manager does so i'm going to remove all references to the sign-in manager really and i'm going to comment out the login endpoint for no so that's ctrl kc just to comment right i really don't need all of this just yet so we'll get there but i just got ahead of myself and i apologize sometimes these things happen but hey we're just going to comment out the code and then we'll revisit it later on now as it relates to some of the error messages that we want to send back what i'm going to do here is kind of add all the result errors so i did say you could find a way to put the result errors in a package and send off someone to say for each error in results dot errors i did say it could be sensitive information so i'm accepting that risk but i'm going to say model state dot and i can say add model error which is then going to ask me for a key i don't have to put in the key and then i'm going to say error dot i'm sorry error being the error message coming back from the result attempt uh code so all right here's what i'll do i put the code as the key right so the key there really means what's the name of the error and then the code error dot description would be right there all right and then we can say bad request user attempt failed or i'll just set the model state because it's already obvious that it failed so that's how we'll go proceed with the error messages for our registration item so let's try this one more time all right so we're back in swagger without the login endpoint but we're going to just try it out i'll put in my complicated password once again and then execute all right and this looks a bit better all right so first we're not getting any complaints about any user manager or any service not being registered that's one two we're seeing here that we're getting back up 400 and we're getting back some details as to what is wrong so invalid username username blank blank is is invalid and can only contain letters or digits all right so that is because we did a mapping but we're only asking for email the actual identity user has to have a username value also so what i need to do here is to specify that user.username is equal to the email address so email address is a required field username is a required field and password they're all required fields by the identity user so i was only providing email and password i'm just going to go ahead and provide the username also but it's the same as the email address alright so let's just try that one more time all right and when i do that and i put in the same details user example and password one look at what we get we get our 200 response all right and well it's just telling us everything is good to go so it has not failed there is no validation error or anything like that i noticed that it says undocumented for the tool too that's because we didn't let the controller know or let swagger know that 202 is a potential return type so remember that we did that with the other controllers where we listed out the potential return types so you can go ahead and replicate that in the account controller but for now we've set up our registration process uh yeah i know you might be disappointed that you're not getting to test the login one just yet but it's really not a login it's more it's going to be more like um an authentic an authorization to say okay you have a token and it's valid yes you can access the resource you want because once again a login really is to create a session and allow somebody access to your system for a period whereas with a with an api we don't know what period you may need access for we don't need to facilitate you for a longer period than it takes to process your request and give you your response so we just want to know that you are an acceptable accessor validate that give you what you want and then we're done with you so that is why we'll be doing the tokens so when we get there we will go ahead and hash out more how we restrict access hey guys one quick addendum to what was done in the previous activity setting of the registration endpoint and everything it worked however when i take the data i realized that my user did not have a password and that's because i did not put this part on so if you look at the create user function it has two overloads one word just creates the user and another one where it asks you for the user and the password to go with them so i'm not entirely sure why they feel the need to make two because for me a user has to have a password but i guess for different situations they may need to store something and not the other so user comma and then from the user dto we take the password so that we can ensure that when the user manager creates that user that password is also going to get hashed and stored on their record hey guys welcome back so one very important part of user authorization is knowing what role this user is in up until now we haven't done anything associated with roles or more preoccupied with getting the user information validating it and whether we create the user or not now when it comes to roles the roles that are allowed need to exist in the system prior to the system being used so the same way that we would have set up these seeding operations for hotels and countries they're of less importance really than roles would be so we want to make sure that the roles are there from the get-go so that when the users start registering the roles are already there so what we want to do is see them but instead of seeding them the way that we see that country and hotel i'm going to show you a way that we can abstract that operation from our db context and keep the db context as light as possible similar to how what i've shown you to do that with our services that we are installing so what i'm going to do is set up a new folder i have configurations already i'm just going to go ahead and add another folder inside that and i'm going to call it entities so configurations for anything that is entity related and then i'm going to add a class and then i'm going to call this class role configuration go ahead and add it and then this row configuration class is inheriting from i entity type configuration and it will be of the type identity role all right let's go ahead and include any missing references so we're just letting raw configuration know that it's going to be associated with that configuration type and of course we have to go ahead and implement whatever this is saying needs to be implemented which is our configure function all right so it's automatically going to pass over the same builder that is in use on model creating so then we can actually replicate the code that we have where we say builder dot has data and then inside of builder that has data we can go ahead and create new roles new role objects so let me just sort out these quotation marks all right so new identity role and we're just going to initialize this object i'm not going to give it an id it will sort out its own id but the name let's call this one user and we have to give it a normalized name which is really just a capitalized version of that of the name really and then i'm also going to give it admin mistrator let's put it out administrator and then the normalized is all caps version of administrator administrate so always make sure the spelling is right i've spent days debugging bad spelling so don't be like me all right so now that we have this configure method and as many rows as you think you may need you can go ahead and configure them but the most important part of all of that is when we're ready to put it into the system we need only say builder dot apply sorry builder dot apply configuration and then i'm just going to say new role configuration and then include the missing reference store newly created and there we go so you see that looks much to me that looks much neater than having all of this in this file remember that the reason we moved it from at the top here above the list was that it was so huge a function because of all these lines so if you want you can pause right now and attempt to build other configuration files for country and hotel all right so i hope you actually attempted it because i did it all right so i'm not going to ask you to do anything i'm not going to do so just in case you need some guidance we i went ahead and created configuration files per type all right so we have hotel and we have country and we have roles so we created a role together i did country same name basically you could copy and paste the code and just change the essential parts country configuration this i entity type configuration relative to country as a context class and then the same code that was there to create the countries i just cut and paste all right the only thing is that in the previous one we had builder dot entity something like that and i removed that dot entity part so it's just builder that has data so you can go ahead and replicate that across both country and hotel and if you're doing your own thing across any other one i know you're on model building looks much cleaner all right and i think i'll just put this one i don't really think it matters where in terms of the order but i'll just put it at the bottom since that's the order in which everything was being created anyway so country then hotel then we have the rules once again these two are really optional in terms of what's really needed to get the application up and running all right so now that we have the role configuration or seeding we can now just add migration and say added default roles and that that and the resulting migration file has two entries into our rows file so you see it generated its own id that's why i didn't bother to modify that but then we get user and we get administrator so when we do update database it will go ahead and do the insert so once that is done we have two other changes to make so one is in the user detail i've introduced this collection of type string and i'm calling it rows right so we're giving the user the opportunity to say which role or roles should this person have right and then in the account controller right after we have vetted if the registration attempt was successful or not or the user creation operation was successful or not remember if it gets beyond that if statement it means that it was successful then we take the extra step of saying user manager dot dot add roles async and we're adding to the user that was just created the role that came in our roles that came in so if you look at this it has a few overrides one override allows it to or not overlap overload sorry so the one overload allows you to just pass in a list of roles that it will just go ahead and add to this user so once the role exists then it will be associated with the user in the backend once again built-in function not much code or effort needed on our part so let us state this for a spin so in swagger you see that they're modifying the potential values right so they they're showing you that you can pass in an array called roles so the array is the square bracket whereas an object is the curly brace right so let's try it out and i i'm going to leave the same user that's the same user that we got success with last time right so that means i should get some validation message to say it shouldn't work but i'm going to pass in the role of user i'm going to execute and then here's what i'm getting so the result is that i have a 400 baht request duplicate email is the issue it's already taken so that validation is working so let's say user at uh hotels hotel listing dot com just something different and that's the user so i'll go ahead and execute and what i'm getting is not a bad response all right that looks good so let us see what happened in the database after those operations so i'm going to kill the execution go over to the server manager and firstly i'm going to look in the users table so i should have at least two users based on my tests so far and there we go we have user total listing and user at example all right so that's good the next one would be the rows so i just scaffolded the roles myself i just see that the rules in so these are the rules that we've created however the user role association is stored here because it's a many-to-many so they're saying that many users might have many roles right so that's why we allowed an array because maybe one user might be administrator and user maybe and supervisor etc etc based on your system it's all context right however in this table when we view data then we should see the user id associated with the role id all right so if we were to just go back and double check you see that this is the user id there we go ending 8 f seven so if i look at that same user id ending eight f seven and the role seven five five so you have to go over to this one to see which one is seven five five which is user so that is how we start integrating roles based authorization into our system so now our users when they get registered will get registered with their appropriate roles [Music] you
Info
Channel: Trevoir Williams
Views: 8,783
Rating: undefined out of 5
Keywords: asp.net core, asp.net core web api authentication, asp.net core web api authentication jwt, asp.net web api account controller, asp.net web api jquery post data, asp.net web api register user, authentication, authentication with bearer token, authorization, core web api user registration, dot net, json web token, registration user asp dot net core web api, signup user asp.net core web api, token based authentication, web api authentication
Id: kCcMgxeL44I
Channel Id: undefined
Length: 42min 4sec (2524 seconds)
Published: Mon Mar 21 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.