WEBVTT

00:00.440 --> 00:00.800
Okay.

00:00.800 --> 00:08.060
So now we're going to be handling the form submission which means we will try to authenticate the user.

00:08.780 --> 00:15.020
The first thing that will be done here is to verify the CSRF token.

00:16.070 --> 00:17.900
So we don't have this functionality yet.

00:17.900 --> 00:21.200
I'm just leaving a comment that we should do that.

00:21.230 --> 00:23.870
You can also say that it is a todo.

00:25.520 --> 00:33.230
Next up we should just grab the send data from the post super global.

00:33.950 --> 00:39.290
That's email and that's a password.

00:40.850 --> 00:46.730
And now that we have the data we should attempt authentication.

00:48.440 --> 00:55.280
So the logic of authenticating the user will be stored inside the auth service class.

00:56.240 --> 00:58.160
Now why service classes.

00:58.190 --> 01:07.170
Well in service classes we can create some bespoke business logic that is not directly coupled to any

01:07.170 --> 01:08.760
of the controllers.

01:09.090 --> 01:12.360
It makes such methods reusable.

01:12.420 --> 01:21.210
It makes them much easier to test, especially with automated tests, also called unit tests.

01:23.190 --> 01:28.230
And it's also helping us keep the controllers slim.

01:28.980 --> 01:33.960
That's actually a good idea, and I would suggest you try to do that.

01:34.200 --> 01:43.710
Keep the controller actions as slim as possible because as their name implies, they should be controlling

01:43.710 --> 01:44.490
stuff.

01:44.790 --> 01:48.630
Not really contain a lot of business logic.

01:48.900 --> 01:56.850
That's why we're going to create a new namespace inside app called services.

01:56.850 --> 02:01.260
And there I'm going to create our auth service.

02:01.860 --> 02:06.030
Let me add the necessary code like the namespace.

02:06.300 --> 02:11.880
This is up slash services.

02:12.750 --> 02:15.240
The class name is auth.

02:17.370 --> 02:25.050
So right away I'm going to add a static method that will be called attempt.

02:25.050 --> 02:28.500
It just means attempt authentication.

02:28.770 --> 02:34.320
It accepts the credentials like the email and the password.

02:36.210 --> 02:42.690
And it returns a boolean value which means whether we were successful or not.

02:42.720 --> 02:44.310
So the name is wrong.

02:44.310 --> 02:46.470
It is attempt.

02:48.180 --> 02:49.830
So let's check the flow.

02:50.340 --> 02:57.090
The first step is verifying the credentials by fetching user by email.

02:58.350 --> 02:58.680
Okay.

02:58.710 --> 03:00.540
So we need to get the user.

03:00.570 --> 03:04.150
See if the user is even inside the database.

03:04.480 --> 03:07.390
For that, I'm going to use the user model.

03:07.510 --> 03:11.830
And we need a method called find by email.

03:11.860 --> 03:19.420
As currently we can only find by id it's missing but I'm going to put it here anyway.

03:19.450 --> 03:21.100
We're just going to add it.

03:22.870 --> 03:28.900
And now if the user is not null which means user was found.

03:29.650 --> 03:37.720
And we can verify the password hash which is simply done by using the password verify function.

03:38.770 --> 03:45.850
So the password is the password we received as a parameter in the plaintext.

03:45.880 --> 03:49.480
We compare it to a hash which is the second parameter.

03:49.630 --> 03:54.130
We get it from the user model password.

03:55.540 --> 04:03.650
So if we are successful, we should just mark the user as being signed in.

04:03.680 --> 04:07.250
We're going to get to that a little later and return.

04:07.250 --> 04:07.970
True.

04:08.270 --> 04:10.040
So we were successful.

04:10.070 --> 04:13.220
Otherwise we return false.

04:15.050 --> 04:19.310
So as you see, this is pretty simple.

04:20.750 --> 04:25.760
Now let's fill in the gaps starting from the user model.

04:26.120 --> 04:32.780
Now I think that you might also attempt adding the missing stuff in here by yourself.

04:32.780 --> 04:35.150
And consider that an exercise.

04:35.690 --> 04:40.160
You can take a look at the other models that we have like the post.

04:41.570 --> 04:47.540
What do we have to add to the model to make it work to be able to fetch it?

04:51.380 --> 04:56.450
And I think you should manage to create this simple method.

04:56.450 --> 04:57.890
Find by email.

04:58.430 --> 05:03.140
If you want to do it yourself, then you can do it right now.

05:03.660 --> 05:08.340
If not, then you can just continue watching because I'm going to do that anyway.

05:08.340 --> 05:15.060
And I'm just going to point you to the time in the video where I'm done with these changes, so you

05:15.060 --> 05:20.220
can continue with the further code that we need to create.

05:22.110 --> 05:22.590
Okay.

05:22.590 --> 05:31.140
So if you decided you don't want to do it yourself, then we had to add the field, the column definitions.

05:31.770 --> 05:50.520
So we need the ID column, the name, the email the password the role and created at timestamp.

05:51.240 --> 06:02.640
Then we are adding the static method called find by email, which accepts an email which is string and

06:02.670 --> 06:06.270
it returns either user or null.

06:08.490 --> 06:16.380
So we need to get the database using the app Getmethod database.

06:19.260 --> 06:28.380
Then we are getting the result of calling db fetch, where we just want to fetch a single record.

06:28.410 --> 06:30.300
So let's write a query.

06:30.510 --> 06:43.350
We want all columns from users table where the email is of specific value and in the parameters array

06:43.440 --> 06:45.120
we pass the email.

06:45.990 --> 06:48.480
That's the only parameter that we need.

06:49.800 --> 06:55.500
So do you think I am missing anything in this fetch call?

06:56.850 --> 06:58.470
Couple of seconds to think.

06:59.520 --> 07:01.830
So yes, we are missing the class.

07:01.860 --> 07:02.940
It wouldn't know.

07:02.940 --> 07:04.900
What class do we expect?

07:06.160 --> 07:10.600
And we expect the current class, which would be the user.

07:12.670 --> 07:21.580
And if we jump to the database, we can see that the fetch would return a mixed result.

07:23.650 --> 07:31.810
But we can be certain that the false will be returned on failure if we can't find the record.

07:33.610 --> 07:35.260
So we can use that.

07:35.260 --> 07:39.130
So it is either user or null.

07:39.820 --> 07:41.860
That's why we can return.

07:45.220 --> 07:47.740
The user or null.

07:51.040 --> 07:53.650
Or actually the variable is called result.

07:55.480 --> 07:55.750
Okay.

07:55.780 --> 07:58.660
So we have this completed.

07:58.690 --> 08:02.980
Now how do we mark the user as authenticated in the session.

08:05.060 --> 08:11.870
So being authenticated simply means we're going to store the user ID in the session.

08:12.560 --> 08:20.450
The session variable can be called user underscore ID and we're going to get it from user ID field.

08:20.630 --> 08:26.150
So a quick reminder session is per user per client.

08:26.630 --> 08:31.460
The session ID is stored in the cookie in the browser.

08:31.580 --> 08:35.780
So everyone has his or hers own session.

08:35.780 --> 08:44.330
It's safe to store the user ID in the session, but some consideration has to be taken here.

08:44.330 --> 08:49.070
Like the sessions need some reasonable timeout.

08:49.070 --> 08:56.930
They need to expire after some time not to keep the user authenticated for too long.

08:57.170 --> 09:07.970
Additionally, we should regenerate the session ID by using session regenerate id function and also

09:07.970 --> 09:13.220
pass this parameter to true to delete the alt session.

09:14.840 --> 09:18.200
So why do we want to regenerate the session ID?

09:18.740 --> 09:24.380
Well, it is to prevent an attack known as the session fixation.

09:25.040 --> 09:27.050
It's actually a serious thing.

09:27.380 --> 09:36.590
An attacker might trick the user of your app, either by sending him a link, or somehow forcing him

09:36.590 --> 09:43.550
to set the cookie using his own the attacker's session ID.

09:44.570 --> 09:55.790
And when the user unaware of that will sign in, he's gonna authenticate the session that the attacker

09:55.820 --> 09:57.620
knows the ID of.

09:58.760 --> 10:09.900
And having this authenticated session ID, the attacker can now by setting a cookie locally, use the

10:09.900 --> 10:14.400
session and just take over the account of your user.

10:15.180 --> 10:25.680
It is a serious thing and if you regenerate the session ID once you authenticate then well, basically,

10:25.680 --> 10:35.190
even if the attacker has tricked your user to use his session, the attacker's session ID, we're gonna

10:35.190 --> 10:36.990
generate a new one anyway.

10:37.740 --> 10:45.720
So we are basically protecting our users, and now we're going to keep the user ID in the session.

10:45.720 --> 10:52.380
And every single time we would like to get the currently authenticated user, we're gonna fetch this

10:52.380 --> 10:57.570
user ID from the session first and the user model from the database.

10:59.520 --> 11:00.990
So we are not done here.

11:00.990 --> 11:11.320
We need to try and attempt the authentication here so we can add an if statement and use our newly created

11:11.350 --> 11:21.040
auth class attempt method, passing the email and password that we obtained through the form.

11:21.070 --> 11:25.600
If we are successful, we can redirect.

11:25.960 --> 11:28.090
So let's use router.

11:29.590 --> 11:32.260
We don't have a redirect method.

11:32.260 --> 11:33.460
Interesting.

11:33.490 --> 11:35.140
Let me jump to router

11:36.760 --> 11:41.230
and hmm okay.

11:41.230 --> 11:44.110
So we actually don't have it.

11:44.380 --> 11:45.640
Let's add it then.

11:45.670 --> 11:50.980
This is public static function redirect.

11:51.730 --> 12:00.190
Now we're gonna jump back to the past and get this redirect function from the contact form project.

12:00.310 --> 12:02.500
Now you don't need to go to the previous project.

12:02.500 --> 12:07.970
You can just either rewrite that or just grab the code from the link under the video.

12:08.360 --> 12:13.460
I'm just copying this because I want it to be essentially exactly the same.

12:14.000 --> 12:20.420
Now I'm magically back at the project and I'm going to paste that.

12:20.930 --> 12:27.800
It's just setting a header, nothing fancy and going back to the auth controller.

12:28.130 --> 12:35.540
Okay, so if we were successful we'd like to redirect, let's say to the home page.

12:35.810 --> 12:38.690
We might also add a flash message.

12:38.690 --> 12:48.080
But if we display the user status in the top menu and the logout button, it might be clear that the

12:48.080 --> 12:49.760
user is authenticated.

12:49.760 --> 12:51.410
So that's something for later.

12:51.410 --> 12:52.340
Definitely.

12:52.850 --> 12:58.190
But if we weren't successful this means well there was an issue.

12:58.400 --> 13:07.250
So if we weren't successful then well we can either redirect to this page to the create action that

13:07.250 --> 13:15.830
will display the form, and we really would need a flash messages for that to display an error.

13:15.860 --> 13:20.930
But alternatively we can just render the form right here.

13:22.730 --> 13:30.620
So the template here would be well the layout and the template would be the same as in this case.

13:30.620 --> 13:33.410
So I can even copy and paste that.

13:33.800 --> 13:38.120
So that can be temporary or permanent solution.

13:38.150 --> 13:49.190
And additionally we're going to add data here saying what the error was that we've got invalid credentials.

13:49.190 --> 13:53.810
So now we're gonna jump back to the form.

13:53.810 --> 13:58.310
And somewhere over here we're going to check if we have an error.

13:58.490 --> 14:02.840
So if the error is is set.

14:03.590 --> 14:11.610
So if this variable exists we're just going to display an error using a paragraph.

14:11.640 --> 14:16.560
Let's make sure we add a CSS style so it's clear it's an error.

14:16.620 --> 14:20.520
We're gonna use the color red.

14:20.700 --> 14:26.100
And then we're gonna simply output the error variable.

14:26.520 --> 14:30.390
And I think we are ready to try this out.

14:30.660 --> 14:32.610
Just make sure the project is running.

14:32.610 --> 14:36.960
And I'm gonna try the credentials that I know don't work.

14:37.800 --> 14:40.170
Okay, so we've got an issue with this.

14:40.170 --> 14:41.760
Find by email method.

14:41.760 --> 14:48.240
Returned value needs to be of type user and boolean was returned.

14:48.960 --> 14:51.810
Let's jump to the user model.

14:51.840 --> 14:56.760
Okay so we expect either user or null.

14:56.790 --> 15:05.550
By the way I think this type can be also described as nullable user by adding a question mark before

15:05.550 --> 15:06.690
the class name.

15:07.770 --> 15:10.810
Okay, so I think I've used the wrong operator here.

15:10.810 --> 15:19.720
It is a null coalescing operator and fetch returns either whatever you expected it to like a class or

15:19.720 --> 15:22.420
array, or it returns a boolean.

15:22.420 --> 15:25.510
So we need to use a ternary operator instead.

15:25.510 --> 15:34.990
So if the result is truthy so it's a class, then we return the result if it's falsy.

15:34.990 --> 15:38.440
So it's for example boolean false.

15:39.100 --> 15:41.110
Then we return null.

15:41.110 --> 15:43.630
And I think this would fix our problem.

15:43.630 --> 15:45.970
Let me resend the form.

15:48.070 --> 15:50.620
Okay I think that's this line.

15:50.620 --> 15:55.210
So redirect does not return anything.

15:56.230 --> 15:58.660
That's why we should not be returning anything.

15:58.660 --> 16:00.580
It will just stop the execution.

16:00.580 --> 16:03.670
Anyway, let's try again.

16:04.090 --> 16:07.330
We've got error, invalid credentials.

16:07.330 --> 16:09.100
How do we test that?

16:09.140 --> 16:12.980
How do we try and see if we were authenticated?

16:13.340 --> 16:22.910
Well, we've got the fixtures someplace and we have users like this one admin example.com with the password

16:22.910 --> 16:25.580
which should be admin 123.

16:25.610 --> 16:26.960
How can we verify that.

16:26.960 --> 16:33.380
Well if we don't see an error it means we are authenticated.

16:33.410 --> 16:35.210
So let me log in.

16:35.660 --> 16:41.270
Now the problem is that we don't have an active session, which means we should just make sure we are

16:41.270 --> 16:46.790
starting the session inside the front controller, which is the index.php file.

16:47.390 --> 16:49.580
So again add the contact form.

16:49.580 --> 16:54.710
We are starting the session right after we include the bootstrap file.

16:55.310 --> 17:00.020
So let's jump back to our current project and the front controller.

17:00.020 --> 17:03.830
That's this index.php file inside the public directory.

17:04.940 --> 17:10.730
And right after the bootstrap we should be starting the session.

17:10.730 --> 17:17.510
I can add this use statement maybe at the top and move the code right here.

17:18.050 --> 17:22.520
Now let me save the changes and try this again.

17:22.520 --> 17:27.800
So we were successful because we were redirected to the main page.

17:27.890 --> 17:41.180
And now if I would try to do vardump the session super global user ID and die just to see if it works,

17:42.050 --> 17:49.610
I should see one which just happens to be the ID of the user that I've just authenticated.

17:49.610 --> 17:53.210
This just confirms that authentication works fine.

17:53.210 --> 17:59.840
Now we are obviously not done because we somehow need to fetch this user data for all the subsequent

17:59.840 --> 18:02.810
requests, and we're going to do that next.

18:02.810 --> 18:10.670
For now, we were able to implement the authentication process and confirm that it works properly.
