Okay.
So now we're going to be handling the form submission which means we will try to authenticate the user.
The first thing that will be done here is to verify the CSRF token.
So we don't have this functionality yet.
I'm just leaving a comment that we should do that.
You can also say that it is a todo.
Next up we should just grab the send data from the post super global.
That's email and that's a password.
And now that we have the data we should attempt authentication.
So the logic of authenticating the user will be stored inside the auth service class.
Now why service classes.
Well in service classes we can create some bespoke business logic that is not directly coupled to any
of the controllers.
It makes such methods reusable.
It makes them much easier to test, especially with automated tests, also called unit tests.
And it's also helping us keep the controllers slim.
That's actually a good idea, and I would suggest you try to do that.
Keep the controller actions as slim as possible because as their name implies, they should be controlling
stuff.
Not really contain a lot of business logic.
That's why we're going to create a new namespace inside app called services.
And there I'm going to create our auth service.
Let me add the necessary code like the namespace.
This is up slash services.
The class name is auth.
So right away I'm going to add a static method that will be called attempt.
It just means attempt authentication.
It accepts the credentials like the email and the password.
And it returns a boolean value which means whether we were successful or not.
So the name is wrong.
It is attempt.
So let's check the flow.
The first step is verifying the credentials by fetching user by email.
Okay.
So we need to get the user.
See if the user is even inside the database.
For that, I'm going to use the user model.
And we need a method called find by email.
As currently we can only find by id it's missing but I'm going to put it here anyway.
We're just going to add it.
And now if the user is not null which means user was found.
And we can verify the password hash which is simply done by using the password verify function.
So the password is the password we received as a parameter in the plaintext.
We compare it to a hash which is the second parameter.
We get it from the user model password.
So if we are successful, we should just mark the user as being signed in.
We're going to get to that a little later and return.
True.
So we were successful.
Otherwise we return false.
So as you see, this is pretty simple.
Now let's fill in the gaps starting from the user model.
Now I think that you might also attempt adding the missing stuff in here by yourself.
And consider that an exercise.
You can take a look at the other models that we have like the post.
What do we have to add to the model to make it work to be able to fetch it?
And I think you should manage to create this simple method.
Find by email.
If you want to do it yourself, then you can do it right now.
If not, then you can just continue watching because I'm going to do that anyway.
And I'm just going to point you to the time in the video where I'm done with these changes, so you
can continue with the further code that we need to create.
Okay.
So if you decided you don't want to do it yourself, then we had to add the field, the column definitions.
So we need the ID column, the name, the email the password the role and created at timestamp.
Then we are adding the static method called find by email, which accepts an email which is string and
it returns either user or null.
So we need to get the database using the app Getmethod database.
Then we are getting the result of calling db fetch, where we just want to fetch a single record.
So let's write a query.
We want all columns from users table where the email is of specific value and in the parameters array
we pass the email.
That's the only parameter that we need.
So do you think I am missing anything in this fetch call?
Couple of seconds to think.
So yes, we are missing the class.
It wouldn't know.
What class do we expect?
And we expect the current class, which would be the user.
And if we jump to the database, we can see that the fetch would return a mixed result.
But we can be certain that the false will be returned on failure if we can't find the record.
So we can use that.
So it is either user or null.
That's why we can return.
The user or null.
Or actually the variable is called result.
Okay.
So we have this completed.
Now how do we mark the user as authenticated in the session.
So being authenticated simply means we're going to store the user ID in the session.
The session variable can be called user underscore ID and we're going to get it from user ID field.
So a quick reminder session is per user per client.
The session ID is stored in the cookie in the browser.
So everyone has his or hers own session.
It's safe to store the user ID in the session, but some consideration has to be taken here.
Like the sessions need some reasonable timeout.
They need to expire after some time not to keep the user authenticated for too long.
Additionally, we should regenerate the session ID by using session regenerate id function and also
pass this parameter to true to delete the alt session.
So why do we want to regenerate the session ID?
Well, it is to prevent an attack known as the session fixation.
It's actually a serious thing.
An attacker might trick the user of your app, either by sending him a link, or somehow forcing him
to set the cookie using his own the attacker's session ID.
And when the user unaware of that will sign in, he's gonna authenticate the session that the attacker
knows the ID of.
And having this authenticated session ID, the attacker can now by setting a cookie locally, use the
session and just take over the account of your user.
It is a serious thing and if you regenerate the session ID once you authenticate then well, basically,
even if the attacker has tricked your user to use his session, the attacker's session ID, we're gonna
generate a new one anyway.
So we are basically protecting our users, and now we're going to keep the user ID in the session.
And every single time we would like to get the currently authenticated user, we're gonna fetch this
user ID from the session first and the user model from the database.
So we are not done here.
We need to try and attempt the authentication here so we can add an if statement and use our newly created
auth class attempt method, passing the email and password that we obtained through the form.
If we are successful, we can redirect.
So let's use router.
We don't have a redirect method.
Interesting.
Let me jump to router
and hmm okay.
So we actually don't have it.
Let's add it then.
This is public static function redirect.
Now we're gonna jump back to the past and get this redirect function from the contact form project.
Now you don't need to go to the previous project.
You can just either rewrite that or just grab the code from the link under the video.
I'm just copying this because I want it to be essentially exactly the same.
Now I'm magically back at the project and I'm going to paste that.
It's just setting a header, nothing fancy and going back to the auth controller.
Okay, so if we were successful we'd like to redirect, let's say to the home page.
We might also add a flash message.
But if we display the user status in the top menu and the logout button, it might be clear that the
user is authenticated.
So that's something for later.
Definitely.
But if we weren't successful this means well there was an issue.
So if we weren't successful then well we can either redirect to this page to the create action that
will display the form, and we really would need a flash messages for that to display an error.
But alternatively we can just render the form right here.
So the template here would be well the layout and the template would be the same as in this case.
So I can even copy and paste that.
So that can be temporary or permanent solution.
And additionally we're going to add data here saying what the error was that we've got invalid credentials.
So now we're gonna jump back to the form.
And somewhere over here we're going to check if we have an error.
So if the error is is set.
So if this variable exists we're just going to display an error using a paragraph.
Let's make sure we add a CSS style so it's clear it's an error.
We're gonna use the color red.
And then we're gonna simply output the error variable.
And I think we are ready to try this out.
Just make sure the project is running.
And I'm gonna try the credentials that I know don't work.
Okay, so we've got an issue with this.
Find by email method.
Returned value needs to be of type user and boolean was returned.
Let's jump to the user model.
Okay so we expect either user or null.
By the way I think this type can be also described as nullable user by adding a question mark before
the class name.
Okay, so I think I've used the wrong operator here.
It is a null coalescing operator and fetch returns either whatever you expected it to like a class or
array, or it returns a boolean.
So we need to use a ternary operator instead.
So if the result is truthy so it's a class, then we return the result if it's falsy.
So it's for example boolean false.
Then we return null.
And I think this would fix our problem.
Let me resend the form.
Okay I think that's this line.
So redirect does not return anything.
That's why we should not be returning anything.
It will just stop the execution.
Anyway, let's try again.
We've got error, invalid credentials.
How do we test that?
How do we try and see if we were authenticated?
Well, we've got the fixtures someplace and we have users like this one admin example.com with the password
which should be admin 123.
How can we verify that.
Well if we don't see an error it means we are authenticated.
So let me log in.
Now the problem is that we don't have an active session, which means we should just make sure we are
starting the session inside the front controller, which is the index.php file.
So again add the contact form.
We are starting the session right after we include the bootstrap file.
So let's jump back to our current project and the front controller.
That's this index.php file inside the public directory.
And right after the bootstrap we should be starting the session.
I can add this use statement maybe at the top and move the code right here.
Now let me save the changes and try this again.
So we were successful because we were redirected to the main page.
And now if I would try to do vardump the session super global user ID and die just to see if it works,
I should see one which just happens to be the ID of the user that I've just authenticated.
This just confirms that authentication works fine.
Now we are obviously not done because we somehow need to fetch this user data for all the subsequent
requests, and we're going to do that next.
For now, we were able to implement the authentication process and confirm that it works properly.