در حال بارگذاری ...

Add Login Nav Link0:00

All right, so let's see. We have User accounts, we have a registration system, we now have password hashing. I think it's finally time to build the login form. Okay, so if I go to TablePlus, we have this account from the last episode, joe@joe.com, but right now Joe's not signed in. Okay, so let's add a login link to the nav bar. All right, let's go. Let's go into nav, and let's look for that register. All right, let's see. All right, so if we have a logged in User, we display their avatar, otherwise a register link.

All right, let's see. All right, so if we have a logged in User, we display their avatar, otherwise a register link. And actually, on that note, let's scroll up. I want the styling to be the same as what you see here. So maybe I can grab this and then update this right now. href or href goes to register, and then we can update the styling. All right, and again, just keep in mind, because this is a simple static HTML website, there's a lot of duplication when it comes to the HTML and the Tailwind classes. Just keep in mind, in real life, there are ways to componentize these things so that you're not repeating the same styles over and over and over.

Create Login Route1:39

Yeah, hopefully the spacing is okay. Good, I'm happy with that. All right, so now register to register new account, or login to sign in. Okay, but right now, of course, we get a 404 because we haven't yet set up a route. All right, so I'm going to go into my routes file, and I'll put it right down here. If I visit /login, that will hit a controllers, and what's the endpoint? How about sessions create? How about that? And then once again, we can use our middleware here. And actually, on this note, we added a couple middleware two episodes ago, but I probably, behind the scenes, need to go over all of these routes.

And actually, on this note, we added a couple middleware two episodes ago, but I probably, behind the scenes, need to go over all of these routes to apply the necessary middleware. Okay, but yeah, we'll get to that eventually. So now, listen for a request to /login, and if you're a guest, we should load a Sessions\CreateController. So I'll do that now. Sessions\Create.php, and as we often do, we'll say login. All right, let's give it a shot. Refresh, and there we go. It works.

Build Login View Form2:42

All right, let's give it a shot. Refresh, and there we go. It works. Okay, so now, as we've done before, I'm going to load a view. I will say sessions/create.blade.php. All right, let's create the view. And once again, like we talked about, I'm going to duplicate a bit more, but that's fine, and paste in the registration form here. Okay, so if I come back and refresh, let's go to login, and it looks the same, but now we can tweak it however we need to. Login. Let's see how that is. Is that the button? Yeah.

and it looks the same, but now we can tweak it however we need to. Login. Let's see how that is. Is that the button? Yeah. Next, let's update the heading. Register. There we go. Login. Good. You have to give us your email address as well as your password. Then you click the button. Okay, so now if I scroll up, where should the form send us? Well, it should make a POST request to sessions. Again, if we're following sort of a restful practice, sessions is our resource, so create displays a form to create a new session,

Again, if we're following sort of a restful practice, sessions is our resource, so create displays a form to create a new session, and then if we post to that endpoint, that should, well, store a new session. At least that's one way to think of it. Another option is if you just want to keep it simple, post a /login, and that would be fine too, whatever you want. All right, let's go back to my routes section, and now listen for a post request to /login, and that will hit our store action. Okay, once again, come on up.

Hmm, it's not working. What did I do wrong? Listen for a POST request. Oh, yeah, yeah, of course. We have sessions. Okay, so one more time. There we go. Okay, so let's come on up. Oh, and by the way, if you're wondering why I didn't do session / create, you can.

Oh, and by the way, if you're wondering why I didn't do session create, you can. That would be fine. In this case, login is just an alias, but, yeah, you could also have a redirect from /login to /session/create. Again, it's just you're in charge. You can do whatever you want here. There's no requirements. There's just guidelines that maybe you should follow if you want, and if you're on a team, it's good if you all adopt the same system.

Implement Session Login Logic5:18

There's just guidelines that maybe you should follow if you want, and if you're on a team, it's good if you all adopt the same system, but otherwise there's very few rules when it comes to this stuff. Okay, so let's come up into our controller, and now log in the User if the credentials match. Okay, so the first thing I want to do is figure out what it means to log in. Now, you'll remember when a User registers, well, we do log them in. So, remember, what we have here, we validate the form, and then right here this creates the new User account, and then right here this is basically what it means to log them in.

and then right here this creates the new User account, and then right here this is basically what it means to log them in. We add this user key to the session. Okay, so with that in mind, a first refactor might be, well, let's store that within a function called login. Okay, and we can go into core. Let's add a new function here. Later, we will probably extract the class, but yeah, for now, when I'm not sure what I'm doing, sometimes it's fine. Just create a function, a simple old function, and we'll paste that in.

when I'm not sure what I'm doing, sometimes it's fine. Just create a function, a simple old function, and we'll paste that in. Okay, so now maybe you'll give us a $user, which could be an array, and then we will update it like so. Okay, so now, actually, you know what? Why don't we, hmm, how do we do this? Why don't we only put the $user's email? So we'll do something like that. Yeah. Okay, so now if I switch back, we can log in a User.

Yeah. Okay, so now if I switch back, we can log in a User. Is that right? Yeah. So I'm doing that because maybe at some point you could say, log in this user array, and maybe that has lots of attributes on it, like a password, timestamps, and things like that. Well, I don't want all of that to be put into the session. So for now, I'm just putting the email address there, and that's it. Okay. So now I have this function that I can also call from my login form.

Okay. So now I have this function that I can also call from my login form. So let's go to session/store. And yeah, I know if the credentials match, I can call this login function and provide our User object. So I know it's going to be something like this. Okay, let's begin. So to sign in, if I switch back, very similar. You have to give us your email address and your password. So you'll notice that I can reuse a little bit of this.

and then also validate that you gave us a password. But in this case, I don't care about any minimum. All I care about is whether it matches up with what we have stored within the database. So we'll do something like that. Okay, then I can say if it's not empty, then once again, let's send them back to the login form. So return view session create. And then what? Also, we need to send through the errors.

And then what? Also, we need to send through the errors. So assuming that validation passes, what's our next step? Well, log in the User if the credentials match. So now we need to check match the credentials. All right, so it sounds like we need to use our database object to perform a new query. And we might write something like this. SELECT * FROM users where the email address equals the email that was provided through the login form.

Select * from the users table where the email address equals the $email that was provided through the login form. And then I'll pass that in here. Okay, so you know what? I've already forgotten what my API is. So we have find or findOrFail. Let's do find like so. All right, so we're doing this incrementally. Let's dd($user) and have a look. We log in Joe.

Let's die and dump the user and have a look. We log in Joe. And yeah, we did find a corresponding record in the database. However, if I switch back and we try to find some accounts that doesn't exist in the database, and we log them in, we get false, which is what we'd want. All right, so we might say if there's not a user found, well, think about it. We need to reload the view and then send through an error. And the message can be no matching account found.

We need to reload the view and then send through an error. And the message can be no matching account found for that email address. Okay, let's give it a shot. Refresh. Try to log in Jode or Jod. And there we go. No matching account found for that email address. Okay, so what's the next step? The next step, if we reach this point, we have a User,

Okay, so what's the next step? The next step, if we reach this point, we have a User, but we don't know if the password provided matches what we have in the database, right? All right, so how do we do that? Well, it's not as simple as just saying if user password equals the password that was provided from the form, because, of course, the password from the form is the real password, and the password in the database is the password that we hashed using the bcrypt algorithm.

and the password in the database is the password that we hashed using the bcrypt algorithm. Okay, so again, php makes this pretty easy for us. All right, so we can use password_verify. And notice if I command click through, you can see we want to give it the $userPassword, and then the second argument is the hashed version, and then it returns a Boolean that indicates whether the two match. Okay, so the $userPassword followed by the hashed password from the database, and if that's verified,

Okay, so the user's password followed by the hashed password from the database, and if that's verified, then we're all set to go. We can log the user in like this, and then we should probably redirect them. Okay, otherwise, and here's a quick little tip. I could do else for otherwise, but in this case, I returned or exited early, which means the else statement is a little superfluous. I don't need to do it, so I can just do this down here.

but has an incorrect password, and that also fails. Finally, let's use his actual password, which is password. Log in, and there we go. Everything works. We can see the cookie was created, the session file was created, and we do see hellojoe at joe.com. All right, let's switch back and see if there's any quick refactors we can do here. Right here, one thing I think we can do

but the password verification failed, in both of those cases, we will hit this point where we let them know, no matching account found. And this gets us around, or it allows us to skirt around the issue of letting the user check if there are certain email addresses in our database. Now, unless both of them match the email address and the password, we just let you know, hey, couldn't find anything, try again. All right, so that's a simple little refactor that removes some indentation.

All right, so that's a simple little refactor that removes some indentation. And generally, when it comes to cleaning up code, I'm often thinking about just the little things, not these drastic changes, not big architecture changes, but small things. How can I get rid of this indentation? How can I make this else statement redundant? How can I wrap this up so that the code is just a little more clear? And that's why I tell you with the validation,

Add Logout Form Route14:27

for your own projects. Okay, I think that's fine for now. So at this point, we're signed in. To wrap up this lesson, why don't we add a logout link, and then we'll be done. Okay, so let's go back to our nav, partial. All right, so let's see. If you're signed in, this shouldn't be an anchor tag. We'll talk about that in a minute. It should be a form. But I just want to see if the styling is okay.

Like that, and then put this in here. Yeah, okay. And then I'd have to do another wrapper. Yeah, we might want to restructure things a little bit. If session $user false, then close it out. I'm being kind of quick and dirty with this template, because it doesn't really matter for the subject material. But yeah, I think that would be okay. All right, so anyways, if I switch back, yeah, I noted that you probably shouldn't use an anchor tag for your logout links.

All right, so anyways, if I switch back, yeah, I noted that you probably shouldn't use an anchor tag for your logout links. And I say probably because, honestly, in the wild, you'll find hundreds and hundreds of examples where people do this very thing. But generally, many episodes we talked about idempotency, and that would apply here. When I click on this, we are changing things. We are adjusting the User. We are logging them out. We're changing the session. So I really shouldn't use a GET request in that scenario. Instead, why don't we use

We're changing the session. So I really shouldn't use a GET request in that scenario. Instead, why don't we use a form that will submit... well, let's think about it. If you want to destroy a user's current session, should that be a GET request? No. We just established. Should it be a POST request? Well, technically, it has to be one. But what do we really want? We want a DELETE request. So why don't we create a new hidden input.

We want a delete request. So why don't we create a new hidden input. And as we learned many episodes ago, we can set the name. And did we call it method? I think that's right. And this is our way to instruct our server how we actually wanted to treat this request in terms of routing to the proper controller. So then I can say... we'll have a button here. And this will be log out.

So then I can say... we'll have a button here. And this will be log out. And then that will hit sessions. Or really, you know what? Maybe I should just make it singular. Make a delete request to /session. So let's come back and give it a refresh. And we got to fix our colors. text-white. All right. And now when I click on it, it's still going to fail because we haven't yet set up the

All right. And now when I click on it, it's still going to fail because we haven't yet set up the proper route. All right. That's our next step. Let's go to our routes file. And a couple things. First, I decided I want this to be singular. Update that. And then within here, update the name. Or you can keep it plural if you want. Just like that. And I think that should update all of the references. Okay. Great.

Just like that. And I think that should update all of the references. Okay. Great. So anyways, if I come back to my routes file, we're now going to listen for a DELETE request to /session. And that will hit a destroy action. Okay. But now think about it. What should the middleware be in this case? In order to respond and log the user out, they of course first need to be logged in. Right? So why don't we set the middleware

Destroy Session and Cookie18:39

All right. Log the User out. All right. So think about it. What needs to be done in this case? Well, at the point we hit the script, we know that we have a signed in User because of the middleware. So all we would really need to do is destroy the session, maybe clear out the session file, maybe delete the cookie, and then finally just redirect the User. Right? So at the end, we'd know we would redirect them to a goodbye page.

user. Right? So at the end, we'd know we would redirect them to a goodbye page and exit. And then the first step would be something like this. Like, let's clear out our $_SESSION super global. What else? What about the session file that's stored on the server? Well, we can call a php function called session_destroy. Destroys all data registered to a session. So we want that as well. But now, a lot of people stick with just this,

cookie. And if we click through here, yeah, so when we set a cookie, we need to give it the name of the cookie, an optional value of the cookie when it expires. So you could create a cookie and say, well, this should expire in an hour, or at the end of the user session. Or we could put it in the past, which means expire it instantly, which is what we want in this case. But then also, it's a good practice to provide the path where the cookie is available, and then also

But then also, it's a good practice to provide the path where the cookie is available, and then also the corresponding domain. So to do that, we have to write a little bit of extra code, but it's not too bad. And you only write it once. So what is the name of the cookie? Well, we've been playing around with this, haven't we? If I go into storage, that's the name of the cookie, phpSessionId. All right, the value can just be an empty string. Next, the...

3600. You're just setting it to some point in the past. All right, next, I said the final arguments are the path where the cookie is, and the domain. So to grab that, there's this extra function called session_get_cookie_params. And once again, if I click through here, I know this is kind of nitty-gritty stuff, but again, it's like you only do it once or you use a framework where they handle it for you. So don't worry too much about all of this stuff.

False? None of this would apply in our case, but again, if you want, all of that would be included in the params, and you can just keep passing that through. In fact, let's just do it to be extra safe, but it's probably okay to omit that in this case. All right, so yeah, kind of a bunch of lower level steps we need to follow to log a User out. You clear out the super global just so it's not referenced anywhere else nearby.

people would recommend that you regenerate the session ID. So we learned about the session file that gets saved to the server, and that has a unique ID. It might be good for security purposes to regenerate that ID and update the cookie and the session file name as part of your login, just in case a malicious user has that key and is doing something with it. You never know. So it's a good practice. So we could say, session, what is it, regenerate ID.

So it's a good practice. So we could say, session, what is it, regenerateId. And I'll tell you, these are lower level things you don't do too often. So it doesn't have to be on the tip of your tongue, is what I mean. And then if we click through there, update the current session with a newly generated one, and then you can pass in a Boolean that indicates whether we should clear out the old session file or not. And again, excuse me, if I switch back, again, that's a good practice. Let's say yes.

and then finally it redirects us back home and says, hello, guest, you're signed out. Okay, great. So now if I log in again, and oh, warning, looks like we have a lingering plural session. Let's go into the routes file. There it is. Clear that out. Let's go into that controller, Session / create. Yeah, that's right. And then within that view, excuse me, Session / create, are we loading form session? Yeah.

view, excuse me, session/create, are we loading form session? Yeah. Okay, I think that's all the references. So if I come back and refresh, yeah, now that works. I can log Joe in. Validation. Looks like I can't type. There we go. Now we're signed in. We can access pages that are exclusive to logged in users, and then we're done. When we're done, we can log out. Okay, last thing I promise I'll

in users, and then we're done. When we're done, we can log out. Okay, last thing I promise I'll let you go. Notice this notes link is still available. It doesn't work because of the middleware, but maybe we shouldn't even display it if you're not signed in. Let's do that, and then I promise I'm going to let you go. And yet right here, once again, I could say if session user false, only on that condition do we display that particular link.

AuthenticationExpiring CookiesDestroying a Session

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟