Flutter Node Auth Tutorial with State Management - Email Password Login/Sign Up

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
this tutorial is divided into four sections first is the introduction of the app we have till now second is email and password sign in third state persistence which means to keep the logged in users logged in fourth is sign out we will be using provider for state management and mongodb to store the data so i have an application ready with me which consists of all the ui files of the application which is the home screen this screen is only visible when we have surpassed the login and the sign up screen next is the login screen which will only be accessible when we click on this button and it shows up the screen a text two text fields which have their text controllers and the login button i also have a sign of screen which is similar to the login screen and it has three text fields because it also wants the name of the user and this login user button all the three text fields look similar to each other because of this custom text field widget this is a common widget used across all the screens to display a text field i also have the utils.dart file with me which has only one function which is show snack bar this function will display a snag bar whenever it's needed especially in case of errors i also have the server folder where i've run the npm init command if you're wondering how i did that here it is i first created a folder using this command then i migrated into that folder then i ran this command and there we have it our server is created here we need to create a file called as index.js and then install some of the dependencies that we need the dependencies that we need are bcrypt.js so that we can secure our password when storing it to the database express so that we can quickly create routes then we need json web token which is jwt and helps in securely transmitting information and then finally so that we can interact with mongodb quickly and if you're not using mongodb as your database then you can definitely remove this and add in your own dependencies that you might need i'll click on enter just to install them using npmi you can also use npm install it's just a shorter format no difference after the installation of all these dependencies are done we need another dependency but not as a dependency but as a dev dependency to save it as a dev dependency you need to run npm i then the name of the package which is nodemon which will help us continuously restart our server whenever there is any change in our file and then dash dash save dependency as a dev dependency all right then click on enter after that process is completed we can go to the package.json file and have and see our dependencies and dev dependencies correctly in the scripts file we need to remove the existing script and instead add this line which is dev nodemon dot slash index.js this means whenever npm run dev command is executed in the terminal we want this command to get run another script that we need is start whenever npm start is executed we want this command to get executed in our index.js file we'll first of all import all the dependencies that were there for now we just need express and mongoose because we need to connect the database in our index.js file and also so that we can convert all the incoming requests to json you'll see that in the next lines another thing that we need is a port so if there is any dot env variable where there is port we need to use that if that's undefined or it's not there anywhere in our application then we need 3000 this comes in help especially when we are deploying our app to heroku or any other website then we need to initialize this express into an into a variable so let's have app and this will initialize the express package that we created after that we will use express.json as middleware this basically makes sure that whatever requests that are coming to our server gets passed and are in json format after that we need to make sure that we listen to our application and start our server so we need app.listen then we need to pass in the port we are at and this is from the port variable then we need the ip which means 0.0.0 which means accessible from anywhere and then a callback function so whenever the listen is set to true or that means our app has started listening what do we want what do we want to get executed so here we are just going to print out that yeah it has been connected to this port and it will just print it out in the terminal let's try to run our app so i'll press ctrl j to get my terminal up and running then i am going to run the command npm run def as you can see nodemon got executed because of our package.json and here we also have connected at port 3000 next step is to connect our app to mongodb if you're not using mongodb make sure to click this step look at the time stamps given in the description below and you can follow when i get to the other authentication part so i'm going to be using mongodb that's available on the net if you want you can go ahead and install mongodb locally on your app machine and then use it whatever you want but after you reach this website which is mongodb.com just click over here on the sign in and then you'll get to this login part if you don't already have an account click on sign up if you do have then log into your account using email address google or whatever you have with it i'm going to sign in and then see you in the next step after you've logged in and if you see a screen like this then great if you don't then you'll have to follow the incoming processes that means you've created a new account so you'll have to follow those processes just manually enter whatever you're doing and then you land on page something looking like this where you need to click over here click on new project and here you need to enter your project name i'll just name it node auth tutorial after that click on next then just click on create a project and here as it instructs you we need to create a database so we'll build a database we're going to go with the free option but if you want you can just look into this after that i'm going to see all the default options and keep the same and then click on create cluster here you need to enter a username and password which are very crucial for the connection purposes so whatever you enter make sure that they're correct and you can use it later on so i'm going to pass in driven as a username and then a password and then click on create user after that i need to add an ip address which means 0.0.0 so that i can allow access from anywhere and then click on finish and close then i'll go to the databases again right now they're deploying the changes so i can wait having the database built i'll click on this connect option so that i can take the database and connect it to my application i'm going to connect the app using mongodb's native drivers because i already have the node.js then i'm going to copy this go where create a variable called db and then pass in the url after that we need to take this username which mongodb has already put in and then replace the password now you need to include these angle brackets make sure to include them disclude them sorry and then add in the password i've already added in here don't try to take my url i'll be deleting this project after this tutorial so now to connect the node.js application to mongodb i'm going to use mongoose that i've imported i'm going to use mongoose so let's have mongoose here and it provides us with an option of dot connect which will help us connect to our database after that is done then we just since this is a promise which is basically a future in dot we need to use dot then and if the connection is successful then what do we need to do well i'll just type in some connection successful and if there's an error we'll catch it using catch and then print that the connection or the error what caused that error and then i'll just save this much and then here you can see connected at port 3000 and connection was successful after that is done we need to dive into the auth part so for authentication how we are going to handle this is create a new folder here called routers and here create a file called auth.js in here first we will import express so that we can create our apis here then we will create auth router variable which is express dot router here we are basically creating our own router that we can use instead of app dot get we'll use auth router dot get or auth router dot post you'll get to know in just a second and then we'll just export this auth router so that we can use it in the index.js file here instead of writing our ports or our routes manually over here like app dot get what we're going to use is use and then pass in auth router here we are also going to import it from route slash auth and then just save it it just makes our code neater so that everything is not just stuck in just one file auth will be done in auth file and other functionality of your app can be added in different other files and used similarly like this after this we are going to create our first route which is sign up route so here i'm going to use auth router dot post as i said here we would have used express dot router express dot get where we are using auth router dot post then we need to pass in the url which is slash api slash sign up this url basically means what way do we need to connect if we pass in slash api sign up that means we'll have to use localhost 3000 slash api slash sign up to get to this route if we pass in slash sign up then that would mean we just need to pass in localhost 3000 sign up to get to this route and access the information then we want this function or the callback function that we're going to have asynchronous so basically whenever a post request is sent to this url what do we need to do well this callback function is going to decide that and it's going to be asynchronous because here we're going to talk to our mongodb and post some data over there then we are just going to get a request and a response there and now our main part will start what do we need to do let's analyze that first of all we need to get the name email and username of the user that the user has typed in over here so we have to destructure it first second step is to take the email and find any other user that's there in our database with that email if that email is there that means that the user is not signing up the user is already existing so that means that the user should sign in third step is to hash the password whatever password the user types in over here is going to be in plain english but we can't store plain english in our database what if a database had to get hijacked or you know hacked so what would you do in that case so we need to secure that password by turning it into gibberish of course we could turn it into gibberish but then what's the point of asking for password and how would the user know what their password is so for that we are going to use bcrypt.js we look into that when we get to it fourth step is to create the user model which you are going to create in just a while after getting done with the signup route we'll create that user model which stores all the user data which is basically the structure of how our user is going to look like and we're going to use mongoose for that because mongodb and mongoose have a very clean way of doing it final task is just to throw any error in case it's there and then we need to connect our signup route to our client side so these are the six steps that we need to follow so let's get started with it first was destructuring so let's first of all have a try catch block because that's the error handling part and here we're going to showcase any error which is our sixth step now the first step is destructuring we need to destructure from rec.body because when we are on the client side we are going to extract data from request.body here we are going to get the name email and password make sure that these names are there as it is because these are going to play a very vital role when we get to the client side of doing this because we need to pass in the same stuff as this in our client side now the second step we need to find an existing user we'll do that using existing user await user dot find one and if you're wondering what user is we're going to create that user model right now we're going to create that user model in just a second but basically what we're doing is searching in our whole mongodb database if there is any user with this email and here we don't need to pass an email email this is a shorthand syntax when the value and the key both are the same thing so in our entire database if there is a user with this email then it will store that in the existing user for any other database you can search it up on the net how to find it and then use that logic it might not be one line but of course you can write all of that logic and then you'll have that existing user with you now of course if that existing user is there then we need to check that so if there is an existing user we need to throw a message saying that this user already exists so we'll just use return and make sure to use return over here because we don't want to execute any further in our function this callback function we don't want to go ahead and here we're going to return rest dot status 400 400 means bad request and if we just do res dot message or rest.json to send a message it will have a status code of 200. we don't want that we want 400 to be shown up because that's a bad error and this status code is also going to play a very important role in the client side then we just want dot json and here we are going to pass in a message this is how we are going to read the message in our client side using this property and then we are going to have user with the same email already exist instead of message we are using json if you want you can also use message it wouldn't really matter i just like using json because it returns a json response so after having returned keyword mentioned here it means if the existing user is there then we are just going to send this message and then return from this function not moving ahead but in case there is no user that means that the user is trying to sign up for the first time on our application then we want to create then we want to first of all hash the password to hash the password we have a variable hashed password equal to await bcrypt.js this is the package that we have we need to import that which we will do after all of this is done so this will basically take this bcrypt.js and hash it we need to pass in the password and the salt which is eight after getting the hashed password with us we need to create a user model instance so we have let user equal to new user and here we need to pass in all the properties of the user our user is going to have three properties the email the password and the password in our user is going to be named as in password but here we have hash password that we need to store make sure to not store this password otherwise there will be a big problem later on because our password will get manually stored in our database and we wouldn't want that ever and finally we need the name and as i mentioned we are not using email as email name as name because well why do we want to use that when we have a shortcut present with us which is just this after that we have created this instance but our mongodb doesn't know that we want to save it so we'll just use user equal to await user.save we are saving it and then mongodb will add two more properties which is id which is the randomly generated id and a version which it doesn't really matter that just tells how many times this user has been updated and then we need to return this user so that we can save it in our state management tool as we move ahead and that's why i've saved it as user equals await user dot save if we just use it as user then in our client side we won't be able to access the id of the user and it's very important to you ads to have them and then the final step is just to return the error here we are using 500 as error because there's some server error because of which we found this error here it was a bad request because user with the same email already existed but here we have 500 that means there's some server error and it's not therefore it's our fault basically and then we will just send in the error with e dot message here we had message because it was a bad request here it is an error so this is our sign up route now the next step is first of all to add bcrypt.js at the top of this so import it here then we also need the user model here so for the user model we are going to create a new folder in the server called models and here call it user dot js so for the user model again we are going to import mongoose here no need of express because we are not creating any routes here then we need to create a user schema user schema is basically the structure of how the user is going to look like and we're going to use mongoose.schema of course if you're using any other database you'll have to use the model supplied by there otherwise you'll have to manually create a user model just by using plain javascript now in the mongoose dot schema we after that we are also going to return this user which is using module dot exports because we don't want this user which is our model which we're going to create in a bit accessible to this file only we want every other file to communicate with it that's why we are using dot exports now here in the mongoose dot schema we're going to have our first property called name and this is how we're going to create it the name of the property which is the key and then the value the value is going to be an object and here we are going to have required true basically the user will have to enter the name of the user it can't go empty then we want the type which is string the name is always going to be a string and then we need to trim it we are going to trim it in our server side not in our client side of course if you want you can trim it in the client side as well it doesn't really matter then we need email even here we are going to have required true type string and trim true similar to the name part nothing really different then we will have a validate property which is the different thing so in validate we are going to validate if this is a proper email or not so if so how are we going to do that well we're going to pass in the validator argument this validator argument will provide us with the value that the email has and then here we're going to check if it's a valid email or not to check if it's a valid email or not of course you can put in your own configuration but i'm going to use regex for it so i'm going to save it in the variable this is the rejects that i'm going to use if you don't want to understand it i'll mention it in the description below and then i'm going to return value which is this value dot match which will help us compare this value to this regex so this value is going to be of the type string then it will match it with the regex to tell us if the email is a valid email or if it's not a valid email after that we need the message so in case there's an error that means the validator returns false then what message do we need well this is a message please enter a valid email address don't give us one two three at the 8456.com please then we need password which is going to have required true and type string of course you can post button validate here as well if you want the password to be at least six characters long but that's not my requirement after that we are going to take this user this is what we are returning right we are going to create this as of now this is just a schema we need to convert this to a model so it's going to be using mongoose.model this is how we are going to convert and this model accepts two parameters now first is the schema so the first is the name of the schema then the schema itself so this is the user schema and this is the user schema object or the user schema schema i hope you got that basically our collection is going to be named user now we can go to our.js and import this user so if i type in user again you can see it asks me to auto import it and here i get it i'm going to save this much now if you want we can directly get started with the client side but before that if you want to test it and not directly jump into the ui side of the thing you can test it using this vs code extension which is thunder client with underclient we can click on new request and here we will it will help us create a new request i already have one created so that it doesn't waste much of a time here i have it a post request with http localhost 3000 which is our url to connect then slash api sign up which is the u-path of the url that we need this part and in the body we are going to use the json format we are going to pass in name which is numan email as nam112 at the dategmail.com and password as test123 then i'm going to click on send and here it is our name is the same email is the same but here the password you can see it's hashed now now you might be wondering how are we going to well check with our sign in password whenever the user just signs in right i mean this is a password and how will we connect plain text with this but we look that look into that in the sign in process here we also have our id which is randomly generated by mongodb and the version it is zero right now because we haven't updated yet now let's try checking if this part is working basically if there's an existing user we need to say user with the same email already exists so i'm going to take the same thing and send it across but you can see user with the same email already exists so now that our signup route is working let's get started with the sign in route let's write a server sided code first and then we'll dive into the client side what do you think right all right so now let's get started with the sign in part so here we're going to similarly have authrouter.post pass in the url as api slash sign in instead of sign up here we're going to have sign in then the callback function is going to be asynchronous with the request and the response then we're going to have our trap and a catch block in the try block we're going to have our constant you know the name email password instead of name we are just going to have email password because in our login user we just have email and password no need to store name there then we need to check if the user with the same email already exists or not so if the user with the same email already exists then we can move further otherwise we'll just have to tell that the user needs to register first so here we're going to find that user again using user.find1 and now you can see user labeled because we have already imported user year so after having that we just need to check if there is no user so if there's no user we need to send in an error message saying user with this email does not exist again the status is 400 and this is the message then we need to check if the password that the user has typed in is matching to the one that is there in our database now how we're going to check that because that password over here is in english or in a text format but that one is an encrypted password which is obviously in a text format but it's not encrypted all right so we're just going to use bcrypt dot compare this is another function that's provided by bcrypt.js package or dependency here we need to first pass in the password that we need to compare it with i mean this is the password that we need to compare and the password we need to compare it with so it will be this user's password so we can just have user dot password because here we have the user's details so if there is no user here we've already returned this code will not get executed but if the user has some data then we need to get user dot password and we can access that because we are finding user and user.password is what we are using if we just go to the user model you can see user.password now if it is a match then we can execute further but if it's not a match then we need to tell that the password is incorrect so we'll just return res dot status400 dot json incorrect password note here we have used message in case of a bad request we are always going to send in a message but whenever there's a server error we're going to send in an error similar to this so if it is not a match we are telling it's an incorrect password now we need to get a token and to get a token which we will save in our database we need to use jwt what is jwt json web token the dependency that we installed so we are just going to have constant token equal to jwt dot sign then we will pass in the id of the user which can be found using user dot underscore id and we have underscore here because here if you come and let's say i type in number one two three and send in this id is underscore underscore id if you can't see it properly i'll copy this and paste it here you can see it's underscore id this is how mongodb stores its id so i'm using underscore id and we are signing and creating a jwt based on this id and then we need to also mention the password key this token is going to be of a lot of use this token is going to get stored in our database or in a local storage database just to be very clear we are going to use shared preferences to store this token to our local storage database so that we can use that just to know if you're logged in or not and if you're logged in then we need to get all the users detail and everything correctly this is how important this token is going to be this is going to be a differentiating factor to know if the user is logged in or not basically if the user is authenticated or not basically if your app has a feature of chatting for only authenticated user this token is going to help us with that because this token will help us in creating a middleware which we will create so after having that token we need to return that to the user so that we can save it in our you know provider and in our application throughout so we are just going to return rest.json now here comes the problem we need to store token as well as the user so that everything goes correctly in our database or in our provider sorry so i'm going to pass in the token first and then all the user data so if i just pass in user it will send all the data which we don't want we want only certain data which can be found using user dot underscore doc if we just pass an user it will send a load of data and it won't be able to map and pass through the data to get the correct data but user dot underscore doc will get give it all the necessary data like email password name user id all the necessary stuff great having that in place we just need a catch block and here it is rest.status500.json error e dot message so this is our sign in route now let's go to the client side of course there are certain more routes that we can add and we are going to add like we want to check if the token is valid or not but first let's get into the client side just to know what we're doing and understand it better so i'm going to close all the save files here and now add in the dependency called provider because that's how we are going to manage the state in our application and before we forget go in the main.dart file and wrap it with the widget known as multiprovider we also need to import provider now we need to add the required argument of providers now our first provider we need to create which is user provider so here i'm going to create a folder called providers and our very first provider is user provider dot dot if you are using any other state management like river pod or get x even just try to understand the concept and implement it there so here we're just going to have a user provider created that extends a change notifier i'm not going to explain provider here a detailed video on provider is going to come in as the next video you can find that but basically here i want to edit the user data user data to edit the user data i'm not going to simply store it in a map or something it might be very inconvenient so i'm going to create a model for it and the model is going to be user dot dot i'm going to call it user class and then define all the required arguments that are there for the user so these are the arguments that we need an id of the user the name of the user email token and password password is optional if you want to store it you can store it and if you don't want to don't store it because this password is anyways a hashed password storing it in user provided doesn't or wouldn't make sense to me but anyways it's fine if you want to store it it's harsh password if you want to display it to the user you can display i don't know why you would but anyways now i can click on this bell icon and generate a constructor first so that all of this error disappears in case you're wondering why i got this option you can just go to this extensions tab and find dart data class generator this extension allows me to do it after that i will generate see adjacent serialization and that's all that i need but make sure and from map you add map underscore id because whenever you use it just come back to our thunder client and your in our sign up if you see id is underscore id so if we convert the user mo we convert the map that we get into a user model we have to use this map which is underscore id we need to use this property so if we just pass an id it will give us an error so i'm going to use map underscore id also here we can ignore the id part of course we are not going to pass in the id every time and anyways the id is going to be empty whenever we want to send data to the server side if you want you can also eliminate token from here but i'm just going to keep it just to show you it's fine even if you keep keep id but i'm just trying to make it as neat as possible for me i'll just come in here and the user and create the user model which is user import it here we have it user user equal to user and then pass in all the required arguments id is going to be empty everything in fact is going to be empty also make sure to make this user a private variable because i want the instance of this user to not be called whenever user provider gets there because for that i'm going to create a getter which is this user so whenever we want the instance of this user we're going to use this user not this one so i'm making this private but whenever user is called we're going to return this private it just looks as a great way of writing code and also it will prevent certain bugs in the future now we need to create a variable so that we can set the user to whatever value we want so here i'm going to create a function called set user it's going to be a string and user basically whatever data we get from the parameter is going to be in a json format but it will be in a string in dot and here what i'm going to do is set this variable equal to user dot from json so whatever json that is there this part we are going to take this json and from that create the user model and set it to this user variable and then of course notify all the listeners that's why we are using change notifier right and similarly we are going to create the user model as well so we are going to have set user from model here as you can understand we are going to have a user model so from this model we're going to set this user and we did that now we'll just notify the listener and we'll just notify the listener and we're done that's how simple it gets now we just go to the main dot dot file and add the provider called change notifier provider and here this is of no use to us the build context that we have so i can just mark it as underscore and pass in the user provider here after saving it and wrapping all of that we can use this user provider everywhere now the next part is in the lib folder we are going to create another folder called services and save our first service which is auth underscore services dot dot so for auth services you are going to need a dependency called http if you want you can also use do which is another package just like http but you can say it has more features in this tutorial i'm going to use http and make sure to import it i've also imported other things that we might need for example material dot dot the user model and the utility file so that i can display errors using a snag bar i'm going to create the class of auth service right now then have a function called sign up user in this signup user function first of all we need a build context so that we can display a snag bar because to display smart bar we need scaffold messenger dot of context so i'm importing context usually to unit test as well and make your code neater you definitely don't need build context here but just to simplify my life and you know just to give you a brief knowledge of what to do just to authenticate the user this is how we're going to do it then we are going to have all the necessary things like email password and name then this is going to be an asynchronous function because we are going to use http to communicate with our server then we need a try and a catch block here in the try block we are going to have our user model created which is going to be user and make sure to import it i have imported it here the id is going to be empty for now and even as i said we're not going to use it even if you use user dot 2 map anytime then give name email and password and then the token also empty then we are going to use http.response if you can see i have used the suffix over here http.response so that i can use http before it and http.post and then in the http dot post first thing we need to pass in is uri so for that i'm going to use uri.parse and then pass in the uri here we are going to create a class with the static instance of uri where we are going to store localhost 3000 we'll get to it but for now let's complete the process then we have slash api signup exactly like we had in the server file so if we just go there in the auth dot js route here we have our slash api slash sign up then we need to pass in a body so that here it can access all three objects so we have used user dot to json if we just come where to json is basically encoding the two map function and two map is this we've removed the id if you want you can remove the token doesn't really matter then we need to pass in these headers these are very essential while using http and node.js otherwise it won't work then we need to handle this error handling part so this rest can be either status quo 200 400 or 500 that's how we configured our whole process right if it's a success then 200 if there's a bad request 400 and an internal server error 500 so i'm going to use http error handle but we don't have that method and why do we even have a method well http error handling method is going to be very similar you know we are continuously going to use it in every function that we are going to have not just for sign up user even sign in user or anything else whenever we communicate with the server we need this http higher error handling so why do i you know manually copy and paste it every time instead i'll just create a function for it in the utils dot dot package so here i've added two packages and imported them dot convert i mean i'm not added it it's just there with the dart dot language then http again using the suffix http here i'm going to create the next function which is http error handling function so i want http response required argument then the build context then the on success function so what to do when there is a success right i mean if there's an error we know we need to show the snack bar but in case of success it really differs from each function sign in we might have to do something else sign up we might have to do something else that's why so here again we are going to have a switch block then we are going to check the status code as i said status goods are going to play a major role if you mess up in one of this you'll get a different blank a different error and different functionality altogether so we're going to use a switch saying if the status code is 200 that means everything is all right okay then we need to execute this on success function and then break it so that it doesn't go to the next case and the next case is 400 in case of 400 we need to show a snag bar and then we pass in the context here it's in a string format so basically if i just remove this json d code it will not get executed you can see it's giving us an error so first we need to decode this and then we can use message tag on this and we're using message over here because every time there's a status 400 we are passing in message now you can see everything is coming together and then again we'll break it then we have case 500 and as you can understand we're going to show a similar snag bar but instead of message we're going to have error great because every time there's a 500 status we are displaying an error awesome now we also need default so because if we mess up somewhere basically if you have case 200 400 and 500 then we have no other cases left and we've not handled it over here we need to show this response.body that yeah you have not handled me please handle me over here great now we can come over here and have http error handling you can see the named parameter context is now required it's asking us because we have imported utils dot dot so here we are going to pass in the response because that's what it requires this is the response then the context and then the on success so whenever there is a success what do we need to do well now let's understand what we need to do in case of success we'll just tell the user that yeah your account has been created log in again because if the user can't log in again that means they've forgotten their password so this is the kind of verification we are going to add so here i'm simply displaying account created login with the same credentials and then i'm going to show ag bar in case this part gets failed because this is the asynchronous part if this gets failed we are going to show a snack bar showing this error now we need to create a constant class with uri in utils.i am going to create constants dot dot create a class constant here and then a static instance of uri with this passed in http don't pass in https otherwise it won't work for you it's not secured it's http only then we will pass in localhost 3000. this is a very convenient way of doing it because if we upload this to node.js or any other website to deploy our server then we will have to change this uri so instead of manually changing it everywhere in every route we just have one route where we need to change great now we can import this so we have constants.imported and this is our signup user function so just to test it i'm going to go to the sign up screen scroll up we have our function created now we need to create the instance of this auth service class and then i'm going to take this auth service and have auth service dot sign up user then we will pass in the build context the email which is basically email controller dot text even if you don't trim it it's fine because we're going to trim it in the server side then we have password as password controller.txt and the name similarly namecontroller.txt after saving this much let's try to bind first of all let's see if it's bound to this function on pressed it is so let's try to sign up if i come over here enter my name rivan enter my email naman 1 2 and i'm using 912 for a reason that's because i've already stored it in the database when i was testing it using thunderclient if you remember it correctly and then the password which is test123 then click on signup and you can see user with the same email already exist then i can have three four as well and then click on sign up account created login with the same credential if you want to check you can go to your database here it is i'm going to click over here go to the collections and as you can see we have our users collection over here and if we scroll down num112 is there when we first tested then number one one two three when we second tested in the third test again we used one two other gmail.com and then finally num1234 and this is our password properly hashed if even if the user gets our data from our database he has no clue what they're looking at the next part is the login part let's get started with it so here i'm going to create the function so i'm going to similarly create another function called sign in user where i'm going to have a build context again then the email and password no name here because sign in doesn't have an name then we have asynchronous function obviously http then we have a try block and a catch block catch block directly we are showing the error no explanation there then we have the user provider instance surveyor we are declaring it first because later on we are going to have async await calls there we are dealing with futures there when we are dealing with futures we definitely want don't want context there otherwise we'll get the warning of do not use build context across async gaps similar to this we are going to have navigator as well because again we are using that ahead when we have async await calls then we have the http post request again where are we going to post it the local host 3000 slash api slash sign in this time so we have slash api sign in then we are going to pass in this body which is json in code and then pass in the map which is email and password if you want you can create an instance of the user class something like this so we have final user user equal to user pass and then pass in all of this as empty but the email and password is going to be like this and then use user.2map instead of json instead of this we can have user.2.json and we're done but i just wanted to show you an alternate way of doing this that's why i took this method now we need to handle the error because we have the stress and we want to know if it's success or a failure so of course we also need the headers my bad because as i mentioned you won't be able to go ahead without this then we need http error handler we'll pass in all the required stuff like response context and an on success in the on success we need to realize now what we do when we are logging in we need to make sure that we store this data in our local storage because we are going to get a token if you remember if we scroll down here we have token that is being passed and this token is going to help us process the state and the persistence we'll get to it in a while but the main part is we now need to store this token to a local storage for that we are going to use the dependency called shared preferences this is the thing that we are going to use so make sure to install that and import it here i have shared preferences already installed here so we are going to have shared preferences easy equal to abate sharedpreferences.getinstance we got the instance of this shared preferences now we need to store this token so we'll have well first of all before even storing the token we want to make sure that the user is getting the correct data so we are setting the user from this res dot body address dot body contains all the data like token and name email password everything so we are setting it here we are not using set user from model because we don't have a model yet so after having stored all of this now we will store this token to our local storage so we have preferences dot set string we'll pass an x auth token this is a token string that we have if you want you can save this in constants because we are going to use this a bit in our application and then we will pass in the token token will be found using json d code will decode this body and then get access to this token otherwise you can also use provider dot of context listen false dot user dot token but you can't use this user provider because this user provider is set listen to false that means even if this updated this part doesn't know that token is something it will return an empty string having this done we just need to use navigator.push and you're passing the route that we need to push to which is the home screen after saving all of this i think we're done looks good enough so we can just take the sign in user go to the sign up user copy this final launch service line go to the login screen paste it in here import the odd service and here have auth service dot sign in user pass in the context then the email which is email controller.txt again no need to trim it and if you want you can even trip it because we are not trimming this in the server side we are streaming this only in the client side but maybe the user wants their password to have the space maybe we don't know so we have password controller dot text and there we are this is all that we need now if i come here and i'll take this email so i'm going to select this all copy password is test1234 and i login here you can see we are getting this error and we are getting this error because we need to restart actually i'll restart the whole application because i installed two dependencies provider and shared preferences when there's changes in prospect.aml file you always need to restart your app alright so our app is launched i'm going to paste the email again then i have the password test123 then click on login and you can see jwt is not defined this is the server error and we are showing the data quite well and the reason for that is jwt here is not defined we need to import that we have not imported it anywhere so we will have constant jwt equal to require json web token since therefore this was an error over here basically we have not defined it over here it went in the catch block and in the catch block we passed in the error here now since there was an error here when we communicated we got the error here and the status code was 500 so it sent this snag bar this is how the whole process worked now let's try to login and you can see we are getting to the next page now if you want to see how we got to the next page or you want to see if the provided data is getting stored correctly or not you can go to this home screen and in the home screen first of all have that user provider user so this is how we are going to have it final user equal to provider dot of user provider context dot user because this was a getter now let's import these two things we need user provider and we need provider having both of them let's print out the user's email instead of the yes and remove constant because now it's not a constant anymore now you can see number one two three four other gmail.com showing up if you want you can show the user's name and here we have the name the user's id as well and here we have it and there we have it the hashed password is showing up i don't know why you would show it but that's great now let me show you the next thing that we're going to work on which is if i restart this here i am well i went back to the sign of screen why is that happening in the main.art file i've defined the home as a constant signup screen i don't want that i wanted to know if i'm logged in i want to show the login home screen otherwise the sign up screen so how are we going to achieve that simple for us in the odd services we are going to create a new function and even before odd services we need to go to the odd.js route because in auth.js we need to create a new route which is to get all the user data so here basically i'm going to get all the user data what does that mean well whatever user i have based on them i want to get all the users data and how are we going to know that well that's where this line is going to come in place which is authorrouter.getslash so whenever there's localhost 3000 slash i want to get the user data but we can't get user data for uh all the users right i mean why would anyone want all the users data unless their admin or we want the search screen or something like that so here we're going to have our middleware which is auth this is going to extract the token and get the and let us know if the user is authenticated or not so if the user is authenticated then only we can move further otherwise it will send an error over there as well here we we will have to create the auth middleware but before that let's get all the user data so we have constant user equal to await user dot find by id request dot user you won't understand this id if you're wondering user dot find by ids request dot user how you'll understand only after we work on the auth middleware and then we are going to have rest.json we are going to pass in this user document just like this one and then the token which is request.token again you wouldn't understand this unless we work on auth middleware so let's get started with it in the server folder i'm going to create a new folder called middleware and then have auth.js so over here what we are going to do is have the instance of this json web token and before we forget about it and then i'm going to create this auth function this auth function is going to be asynchronous and it will have three properties request response and next next means when we get to it i'll tell you otherwise you wouldn't understand it but basically it means we can move to the next function because if we see here we have auth here but in the auth part this is the callback function so if we pass in next that means we can move over here otherwise it won't move to the callback function it will just remain there and the function will get stopped so this is why we have the middleware as well if we don't call the next function we won't give it to this callback and it's particularly very useful because you just think about it if the authentication part fails why would we move to this part this part basically tells without any verification get me the user's data so that would be an exception because we wouldn't have the user's data so coming back to this here first of all let's export it even before we forget it then we have the try and catch block then we have the token now what is the token request dot header x token in the client side we are going to attach the token as a header not as a body it's the particular convention you're going to pass in as a header and later on when you want to do authenticated work only like chatting for example you want only authenticated users to chat or add product to a cart then you will have a header and in the body you would have all other data like if you're chatting then you want the body to be the name of the user the text that they have sent so why would you pass in token in the body the token will be in the header so we are getting access to the token using request.header and in the client side we will pass in the header then we are going to check if there is no token that is passed in that means that the there is no token so access has been denied that's a very useful message for us because us as a developer if we forget to pass in the auth token then it will tell us that we have not passed in any token access has been denied then we want to check so basically if there is no token then we have returned it that means that this function won't get executed and we have not called next so this part will also not get executed then we have verified so here basically we are verifying the jwt json web token if the token that we have and the password key we have to pass this in if i pass in password key it will not verify because here the password key is this and your if the password key is this they won't match so you have to pass in the exact password key as this one then here we have password key and it will verify this is jwt dependencies task so after it has verified and this verification is important why because what if the user got access to the local storage database or the shared preferences and they go inside manipulate the token and then give it to us well that's very weird and it might be a sort of hacking into so you want to check if the jwt is verified or not if it's verified then only we want to move further otherwise we will check if it's not verified then we can say token verification failed authorization denied simple enough and here we've passed in 401 even year 401 400 is bad request 401 means authorization is required that's a very useful status then we will set request.user equal to verified.id if you remember if we go back we have signed jwt using this id so to get that id of the user we can have request dot user equal to verified dot id if we don't attach this to request.user it will think here it will get us a problem because we don't know the user's id that's why we need to store it away if we just set it to request that means all the other data is gone and that's very important for us right so we will set it to request.user and equal to verify.id then we will also set request.token equal to token just like this then we are calling next after we call next this function will get run if it's not verified it will return and this next will not get called so we won't get ahead and it will not get us the user's data after this part we just need to display an error in case of a catch block simple enough now we can come here and add the auth middleware so import it and we have it having that we can create another route now which is if we want to check if the token is valid or not so we need to check the token is valid or not for that we are going to create a separate route as well so whenever we get the user's data before getting that we need to check if the token is valid or not and it's going to be very similar to this part right here also we have added that logic basically but now we need to create this over here as well because auth middleware will be used every single place wherever you need to perform authenticated task but what if you want a separate route only for once that will tell you if the token is valid or not and then you won't move ahead so here we have our route created slash token is valid which is asynchronous then we have request response very similar the same try catch block the same status error code here we are going to get access to the token using request.header similar to this one then we are going to check if there is no token then we are going to return rest.json false and we can also pass in the boolean value you might already know so you have passed in the boolean value here so if there is no token the token is not valid so we return false but if there is a token then we are checking if it's verified so we are using jwt.verify similar to this again and if it's not verified then we can pass in dress.json false again finally if all of these get passed through we are going to get the user's id so user is equal to user dot find by id then we are going to check if there is no user with that id so we are using user dot find by id verify dot id if the user with that id does not exist then we are going to return rest.json false but if the user exists then we can send in race dot json true of course if you want you can uh pass in the status codes here as well but i don't find the need to do it so i'll just pass in rest.json false perfect now i'm going to close all the save files i think authentication part from the client side is all done now here in the auth services part we need to get started with the get user data functionality so for the get user data we'll create a function it is asynchronous then a try and then we'll import the build context just to display the snag bars then we have a try and catch block here we are showing the snag bar then we have the user provider again just to avoid the warning of don't use build context across async apps then we have shared preferences here where we are getting the instance of the shared preference then we are having the string token which can be nullable and here we are basically using shared preferences to get the string so we have stored xor token here you can see set string but here you are getting it and it can be nullable because what if the user hasn't stored anything at all in the database till now so this is going to be nullable so if the token is nullable what do we want to do the token is null then we'll just set it to an empty string and then we're going to have token response http.post because if the token is not null then we want to tell if the token is not null then we want to check that the token that we've gotten is valid or not so here i'm going to pass in uri.parse slash token is valid then of course in the headers we want the normal default header like this one so we'll pass that in and then as i mentioned whenever we use token is valid or authentication middleware we need to pass in the headers our token and here we pass an xor token please spell all of them properly they're very essential otherwise it won't work for you and you'll be wondering why it's not working great now we have the token response so now after getting the token response we want the response response which is tokenresponse.body then we are checking if the response is true because token response dot body will just reply as with the true or false you can see it in the odd.js route here it's just returning true or false so if the response is true then we want to get and get all the user data so that we can store it in the user provider great if it's not then we don't want to do anything so if response is true then we get that which is constant dot uri and make sure to put in slash because constants.uri is localhost3000 without a slash so make sure to put in the slash so that you get it and make sure to pass in get over here instead of a post because you're getting the data and this part right here has a get not a post now headers already you have the content type like this and of course the exoth token which is this token make sure to pass in both of them if you don't pass this it will tell you with this error which we have in the middleware no auth token access denied now if the response is true obviously we will get the data so we'll have user provider dot set user and set it definitely if you want you can just add http error handling as well it wouldn't matter but here i'm pretty sure nothing can really go wrong we're just getting the data great now we just need to take this get user data function go to the main.file convert this myapp widget to a stateful widget in the init state we are going to remove this have instance of auth service which is final auth service or service equal to auth service now that we have the instance we can take auth service dot get user data pass in the build context there we have it now our user date user provider has that data so if it's a success here you can see we are setting the user provider to this data so if it's a success then our user data providers token will not be empty but if it's a failure that means that the user has not signed in then we are going to get the token as empty so here in the home i'm going to have provider. of userprovider.user.token if it is empty then we want to show the signup screen and if it's not empty then the home screen good enough this is the code if you want to take a look at it and now you can see i'm on the main screen even if i refresh the application here i am it's working really well so that was the login sign up and all of the tasks with state persistence the last task is the sign out task but sign out we are going to do it a bit differently so how is it going to be different i'm not going to pre-write the sign out user function i'm going to write it in front of you so that you understand what the thought process is like i have not typed it at all so let's try to understand what we need to do well first of all we need the sign out button so for the sign out button i'm going to go to the home screen add my button so for the button i can just go to the sign up screen copy this elevated button and paste it over here now for the on press we are going to have the sign out user option so i'm going to copy the sign out user and have that created for you to sign out user and yep that's pretty much it now i want to see the sign out so i can change this to sign out now in the auth services i will have void sign out written here and it's again going to be it's not it's going to be asynchronous because i'm going to use shared preferences so i'm going to take this and paste it in here why do i need shared preferences pretty simple right because we are determining if the user is logged in or not based on this function which is get user data get user data is basically checking if the get string function this token is null or not if the token is null that means that the user is not logged in so here i i'm getting the preferences and again i also need this token so i'm just going to take this token actually i don't even need the token i'm just going to have preferences dot set string take this xor token and set it to an empty string that's pretty much it here we are checking if the user is logged in or not based on this token preferences xor token i've set it to empty again that means there's no token data in our local storage that means we have logged out so automatically of course it won't go to this screen so what i want to do here is use navigator and if i use navigator here you'll see that we get the warning again which is navigated or of context of course we need the build context here dot push and remove until and we get that now you can see if i hover over this do not use build context so what i'm going to do is final navigator equal this to navigator dot of context paste it here then use navigator then i have navigator dot push and remove until then we have material page route context and then we pass in the constant sign up screen that's where we want to go right so so we are just setting the preferences to an empty string and then going to the sign up screen and we are using push under move until so that even if we try to go back we can't go back you know there's no back option there and that's pretty much it if we didn't do this task we would just have this and it will just go to the next screen and it would work but what if we refreshed it afterwards then again we would come to this screen because again all of this would process and there would be user data left here we are just changing the route but here we are adding some functionality to tell that token is empty there's no more user signing sign in great now i can take this sign out go to the home screen and have auth service and here i'm directly calling it in i'm not created creating the instance of this auth service the reason for that is if i create an auth service thing here instance then this can't be a constant but i want it to be a constant so of course i can't do that and here we'll have auth sign and we'll pass in sign out we also need to build context so i'll pass in the context and that's pretty much it i'll take then the sign out user and since we need to pass in the build context we'll have to do it something like this so that we store the address basically we're calling it now if i click on sign out i go to the sign up screen if i try to go back i am not going back and if i refresh my screen you can see we are on the sign up so this was my thought process and this was the whole tutorial we've learned about signing in signing up perfecting the state of the application and obviously a lot about middleware now if you want to focus on your application or and if you want authenticated people to perform the task of the application you can use this auth middleware everywhere it's a very neat way of doing stuff in my opinion at least so this was it for the tutorial thank you so much for watching see you in the next video
Info
Channel: Rivaan Ranawat
Views: 16,453
Rating: undefined out of 5
Keywords: flutter, node, Nodejs, email, password, auth, email and password auth, no firebase, authentication, flutter node auth, flutter Nodejs auth, flutter Nodejs authentication, state persistence
Id: EuP3xycjiM4
Channel Id: undefined
Length: 76min 5sec (4565 seconds)
Published: Sat Aug 13 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.