Auth Setup Overview0:00
Okay, let's start working on the authentication part of our application. On our Laravel backend, we already have Breeze and Sanctum set up, so let's go ahead and wire it up to our Nuxt frontend. So I've gone ahead and added login and register pages here. There's some styling so the forms look decent. And I also added this my info page, where I want to show the information of the User who logged in. So let's go back to the login page. And if I hit login, it just shows an alert that says logging in. So let's start with this. So if you're not familiar with Sanctum, make sure to check out the documentation here. And for us, we're going to be doing SPA authentication. So you can see Sanctum makes use of Laravel's built-in cookie-based session auth services. And there's a bunch of setup here, which we'll go through. First, we have to make sure to add this header for each request. And we'll do that in a second. Next, you have to configure your domains. And in our case,
bunch of setup here, which we'll go through. First, we have to make sure to add this header for each request. And we'll do that in a second. Next, you have to configure your domains. And in our case, both of our apps are running on the same domain. And in this case, it's localhost. So our frontend is running in localhost:3000. And I'm using php artisan serve to run our backend on localhost:8000. And since they're on the same domain, we should not have any CORS issues. Next up, we have some middleware. And if you remember, in the last video, I commented out this middleware here, just so we can get creating posts working. So let's put that back in. Since we're working with Sanctum now. So let's go back to our backend. And let's comment this back in. Okay. Next, we have CORS and cookies. I already talked about CORS. And a lot of this stuff has to do with configuration for your HTTP client. So in this case, it mentions Axios. And I'm not using Axios here. But there
Initial Login Request1:32
cores and cookies. I already talked about cores. And a lot of this stuff has to do with configuration for your HTTP client. So in this case, it mentions Axios. And I'm not using Axios here. But there should be similar options for our fetch library. So let's go down here. And you can see before we make any requests, we have to hit this endpoint to grab the cookie that stores the CSRF token. So actually, before I do that, let's just hit the actual login endpoint and see the error that we get. So back to our frontend. Go here. Let's remove this. Let's make this async. And we'll just hit the login endpoint, which should exist because we're making use of breeze. So let's say await API.fetch. And the endpoint is /login. And it is a POST request. So let's add that in the options. So the method is POST. And we have to send through the body here. So we can say body, the email is emailValue. And the password is passwordValue. Okay, so let's try this out.
options. So the method is post. And we have to send through the body here. So we can say body, the email is email value. And the password is password value. Okay, so let's try this out. So let me save this. Let's go back to our app here. And let me open up DevTools here. Go to our network tab. Go ahead and hit login. And you can see that we're getting an error here. So if I click login, you'll see that we have CSRF token mismatch. Again, if you go back to the docs, in order to get the correct CSRF token, we have to hit this endpoint first. So let's go ahead and do that. And you can see after we hit this endpoint, we should get this XSRF token cookie set in our browser. So let's see if we can do that. Let's comment this out for now. Let's go ahead and make an endpoint call to that endpoint. So let's say await, say API fetch. And it's going to be /sanctum/csrf-cookie. And let's see what we get here. So let me say this.
Include Credentials Cookie3:18
an endpoint call to that endpoint. So let's say await, say API fetch. And it's going to be /sanctum/csrf-cookie. And let's see what we get here. So let me say this. Let's go back here. Let's refresh this. And let's hit login. Okay, so we do get a 204 status, which is successful. And let's check if we get the correct cookie. So let's go to application, cookies. And you'll see that we don't have that XSRF token or cookie set, XSRF token. And that's because we have to add an option in our fetch library. So like I showed you before, for Axios, you have to add Axios.defaults.withCredentials = true. But for our fetch library, let's go to API fetch. So in here, we can say credentials. And we can say include. Save that. And now let's see if we get that cookie within our browser. So back here, let me refresh this. Let's hit login. And now we do get the XSRF token cookie.
And we can say include. Save that. And now let's see if we get that cookie within our browser. So back here, let me refresh this. Let's hit login. And now we do get the XSRF token cookie. So now whenever we make other requests, we have to send through this cookie as well. You can see that information here. So you have to pass it as an X-XSRF-TOKEN on subsequent requests. So in Axios and Angular, this is done automatically for you. But in our case, it doesn't. So let me just show you that first. So back to login. I'm actually going to extract this to a method so it's a bit cleaner. So let me cut this. Let's make a new method here. Let's just call it CSRF. And let's go ahead and paste that in. Make sure to return it. And we can just await it in here. So basically the same thing so far. So await CSRF. And now we should be able to hit this login.
And let's go ahead and paste that in. Make sure to return it. And we can just await it in here. So basically the same thing so far. So await CSRF. And now we should be able to hit this login endpoint. But remember, we have to send through this cookie that we just received. So this one right here. So if I save that, we get an error here. And there should be no await here. Okay, save that. Let me refresh. Go back to the network tab. Let's hit login. So again, this endpoint is successful. But the login endpoint is still an error here. And we're still getting the same CSRF error. And that's because we're not sending the header correctly. So if you look at the payload, I'm sorry, not the payload, the headers here, let me just make this a bit bigger. You'll see that the x-xsrf-header is not sent here. So we have to do that manually. So let's update our API fetch plugin here. And it's passed through.
Send XSRF Header5:51
let me just make this a bit bigger. You'll see that the x-xSRF header is not sent here. So we have to do that manually. So let's update our API fetch plugin here. And it's passed through the headers. So we can say headers. And I'm also going to add the header for Accept JSON. So Accept: application/json. Okay. And we have to send through that x-xSRF token. So again, this cookie should be set already. But how do we get that cookie? So it's a bit cumbersome to grab cookie information from vanilla JavaScript. So I'm just going to make use of a package here. So I'm going to make use of this package here, which allows you to easily grab cookie information. So let's go ahead and npm install it. Let's stop our server. Let's npm install it. Okay, and to grab cookie information, it's pretty straightforward. We can just say cookies.get, and then the cookie name. Actually, we have to import it first. So let's make sure to do that.
Okay, and to grab cookie information, it's pretty straightforward. We can just say cookies.get, and then the cookie name. Actually, we have to import it first. So let's make sure to do that in our code. So up here, we can import it. So let's put it right here. And we can just say, and we can just say, cookies.get. And the cookie name, as you saw in the browser, we go back to application cookies. It's x-srf-token. Not to be confused with the header you're sending through, which is x-xsrf-token. So for the cookie name, it's x-srf-token. Okay, let's save that. And let's try that again. Make sure to run our server again. Let's go back to our app here. Let me just make this smaller. Let's go back to the network tab. Let's refresh this just in case. And let's hit login. Okay, so now we're still getting an error. But it's the error coming back from the validation. So if you
Handle Login Errors7:43
Let's go back to the network tab. Let's refresh this just in case. And let's hit login. Okay, so now we're still getting an error. But it's the error coming back from the validation. So if you take a look at this, it's no longer the CSRF error. It's now the validation errors coming back from Laravel. So let's take care of that. First, let's display the error messages in here. And we already did this in the last video. So let's just copy that code. So we did it in the create form. So let's grab it from there. So we have to use a try catch block here. So after we catch the error, we are setting the error state here. So let's do that in our login page. So we have to try and catch this. You can also try and catch this CSRF call, but I'm just going to leave that. So let's try this. Let's catch the error down here. So catch error, and we can paste that in. Save that so it reformats. Again, we have this error state here.
going to leave that. So let's try this. Let's catch the error down here. So catch error, and we can paste that in. Save that so it reformats. Again, we have this error state here. And we can just grab the template that displays the errors. So let's grab that from up here. It's this entire unordered list. Let's paste that into here where the error should go. And hopefully that displays the errors. Okay, does this work? Save that or refresh that, hit login. And there are the errors. Cool. Okay, now let's work on the case where we actually log in. So we'll provide the correct credentials here. And we should be able to log in. Actually, let's try incorrect credentials here to see if that works as well. Should work. And the error message should show. Okay, but let's handle the case where we should be logged in. So back to our code. Back down here. After we log in successfully. So after this, we can reset the form if we want,
message should show. Okay, but let's handle the case where we should be logged in. So back to our code. Back down here. After we log in successfully. So after this, we can reset the form if we want, but I'm just going to redirect to a different page. So usually you'd make use of the router here. So router.push. And I want to go to the my info page. So it would be my info, or /my info. But in my experience, whenever I'm redirecting, I'm getting unexpected results. But when I do a full page refresh, that solves most of the issues. So instead of this, I'm just gonna make use of the window here. So window.location.pathname. So I'm basically redirecting, but it's a full page refresh. And we'll do my info. Okay. And this should be outside of here. So let's put it here. Okay, save that. And I think I have state for loading as well. So I guess we can set that as well. So we'll do it right here. isLoading.value equals true.
of here. So let's put it here. Okay, save that. And I think I have state for loading as well. So I guess we can set that as well. So we'll do it right here. isLoading.value equals true. And then we'll set it to false after we're done. So after all of this, we can set it to false. Okay. Let's save that. Let's see if it redirects to the my info page after we log in successfully. So let me just refresh this. And it looks like that loading state is true for some reason. Oh, yes, this should be down here. Sorry. Okay. Let's try that one more time. Okay. So let's go ahead and log in with the correct credentials. And let's see if we are redirected to the my info page. So let me just get rid of DevTools for now. Let's log in with this user. Let's log in. And we are redirected to the my info page. So now the correct cookies should be set. So any endpoint that we hit behind the sanctum middleware should work at this point.
Fetch User Info11:16
this User. Let's log in. And we are redirected to the my info page. So now the correct cookies should be set. So any endpoint that we hit behind the sanctum middleware should work at this point. So let's go ahead and try that out. So if you don't know what I'm talking about, back to our back end to our routes/api, this route, for example, returns the logged in user's information. And it's behind this auth:sanctum middleware. And we should be able to hit this and get this user's information, which is what I want to display on this page here. So let's go ahead and do that. So back to our front end, let's go to my info. Let's grab the import for API fetch. Okay, let's put that right here. And I want to call fetch client side here. So we'll just do it within a mounted hook. So say onMounted. And we actually need a piece of state for it as well. So let's just put it here.
So we'll just do it within a mounted hook. So say on mounted. And we actually need a piece of state for it as well. So let's just put it here. const user equals ref(). And by default, let's make it null. And when the component is mounted, let's go ahead and make a fetch call to that endpoint. And again, we should be logged in and our cookies should be set. So hopefully we get the correct information back. So say const response equals await, and we have to make this async. And we'll call API fetch here. And the endpoint is /api/user. And I'm not gonna bother with error checking here. But after that's done, we can just set that piece of state. So user.value equals the response. So let's see if this works. So let me save this. Go back to our page here. Again, all the cookies are set, they should be sending correctly to the server. So I'm going to refresh.
So let's see if this works. So let me save this. Go back to our page here. Again, all the cookies are set, they should be sending correctly to the server. So I'm going to refresh this, I'm going to open up DevTools. And hopefully that user variable should have the login users information. So let's go to view DevTools here. Let's go to that component. And you can see we are getting this user's information here. So we can make use of it in the template, as expected. So let's get rid of this. Let's say user.name. And same for the email. Let's save that. And refresh. And we are getting an error here. It says cannot read property. No, that's because initially the user is null until the information comes back from the server. So let's use optional chaining here with a question mark. Let's save that. Let's try this again. And now it works. Cool. So say for example, we also wanted to show this user's posts in here,
Add User Posts Endpoint13:43
let's use optional chaining here with a question mark. Let's save that. Let's try this again. And now it works. Cool. So say for example, we also wanted to show this User's posts in here, we can do that as well, because we now have access to the logged in User. So let's do that quickly. Let's set up our back end first. So back to our Laravel app, I'm going to make a new route group here with the auth sanctum middleware. It's already on this route, but let's add it again. So let me get rid of this, it's going to be a route group. It's going to be a callback. Okay. And now everything in here should have this sanctum middleware. So let's make a new one that grabs the User's posts. You can make a controller here, but I'm just going to make a callback just to see it working around the call. For the endpoint, we'll make it /user/posts. So this will get the logged in User's posts.
controller here, but I'm just going to make a callback just to see it working around the call. For the endpoint, we'll make it /user/posts. So this will get the logged in user's posts. And we can say, return auth user posts, because we have that relationship set up. So let's save that. And let's try this out in our front end. So back here, or back to our front end, I mean, let's add another piece of state for our posts. So let's duplicate this. Let's name it posts. Let's make it an empty array to start. And it's pretty much the same as this. So let's duplicate this. Let's rename this variable to responsePosts. The endpoint is API/user/posts. And let's change this to postValue equals responsePosts. I think it'll work if I just set it directly. So we don't need two lines. Let me try that. Let's get rid of this. And let's see if our posts are set in view dev tools. So let me save this.
I just set it directly. So we don't need two lines. Let me try that. Let's get rid of this. And let's see if our posts are set in view dev tools. So let me save this. Let me refresh this. Let's go to view dev tools here. And you can see the posts are set correctly. And it shows all of this User's posts. Okay, so I guess we can do the same here. Let's get rid of this. Okay. Let's save this refresh. And this still works. Okay. And now we can display the User's posts in here as well. So I'm just going to paste in the marker for this pretty straightforward. We're just looping through our posts and displaying them here. Pretty much the same as what we did on the index page. I think I have an extra div here, or two. Let's remove this one. Okay. And there are all the posts by this User here. Cool. Now from the last video, we worked on creating posts. But if we go to our back end, so it was
Use Auth User for Posts16:14
or two. Let's remove this one. Okay. And there are all the posts by this User here. Cool. Now from the last video, we worked on creating Posts. But if we go to our back end, so it was PostController store, we were just hard coding one for the userId. But now we want to make use of the logged in user. So we can say id, save that, let's go back to our routes/api.php. And now this endpoint for storing the Post should be within the auth:sanctum middleware. So let's move this up here. These two are still public, so they should not be wrapped in middleware. So let's see if this works still save this. And let's go back here. Let me just refresh. Set a new Post here, create a new Post, new postBody. Let me just hide this. Okay, create Post works. There's a new Post. And if you go back to my info, it should show there as well. And there it is right there. And just to double check, let's go to our database. Let's
Implement Logout Flow17:13
Okay, create post works. There's a new post. And if you go back to my info, it should show there as well. And there it is right there. And just to double check, let's go to our database. Let's check out our posts here. And you can see one is added. But now it's not being hard coded, we are grabbing it from the logged in user. Okay, now let's work on logging out. So we should have an endpoint for that as well within Laravel. And all we have to do is call that endpoint and Laravel should invalidate that session. So right now we're still logged in. If I refresh this page, we still get the correct information here. So let's go ahead and work on logout. So I already have it set up. All it does is alert logout for now. So let's work on this. So back to our front end should be within the default layout, because that's where I have the menu. And here's the method for logging out. So we can pretty much do the same thing. So let
we do want to redirect as well. Let's redirect back to the homepage. Let's get rid of this. And let's get rid of this as well. And for the error case, you don't have state for it, which is fine for now. And I still want to redirect. So let me remove this. And we'll make a finally clause here. And it's going to redirect in both cases if it passes or if it fails. Hopefully this works. Wait, this needs to be async. Okay. And again, at this point, I'm still logged in. If I refresh this page, we are getting the information here. But if I log out, okay, we are redirected back to the homepage. And that should have invalidated our session. So if I try to go back here, we don't get that information, which is correct. And we'll work on protecting these routes using middleware in the next video. So let's just check out DevTools here to make sure we have the correct error. So if I refresh this,
Build Registration Flow20:26
We also need this await CSRF method. So let's grab that as well. So this is repeated code. Maybe later on, we'll clean this up and put it into a composable. But for now, this is fine. So we are going to repeat this. Let's put this here. The endpoint is now register. The body is going to be slightly different as well. Now we have name, we have an email, oops, spelled that wrong. We have an email here. We still have a password, but we also have a password_confirm. It's actually password_confirmation. And I named this password_confirm. Okay. And after that's done, let's just alert it here to make sure I can see it. So alert registered. We have to add the template code for our errors as well. Again, that's the same as login. So let's grab that up here. All of this, go to register. Let's replace this. And hopefully I did that correctly.
to add the template code for our errors as well. Again, that's the same as login. So let's grab that up here. All of this, go to register. Let's replace this. And hopefully I did that correctly. The loading state is there. Did I set that? I did. And hopefully that is correct. Let's refresh just in case. Let's check out the error messages. Okay, that does work. Let's make a new User here. Let's put different passwords to make sure that validation works. And it does. Let's match the passwords here. Let's hit register. Okay, there's the alert. It redirects here. And the correct information is shown here. So everything is working correctly. So yeah, that is our starting point for authentication. Again, we're making use of Breeze and Sanctum on the back end. And once you have the correct headers and cookies set up, then it shouldn't be too difficult. Let's go ahead and make commits here. We have some changes in our front end and back end. Start with the back.
Commit Authentication Changes22:14
you have the correct headers and cookies set up, then it shouldn't be too difficult. Let's go ahead and make commits here. We have some changes in our front end and back end. Start with the back end here. Let's say git add, git commit. Let's just say authentication on front end. And for our front end, git add, git commit, authentication, starting point.
