So in order to authenticate clearly we're going to need some new database functions, we're going to
need to be able to get a user, which means we probably need to insert, update and delete users.
We probably want to be able to get all users.
And finally, we'll need an actual routine to perform the authentication itself.
So let's get some of this stuff out of the way.
So I'm in my post Glasgow file, which is inside DB Repo, which is inside repository, which is inside
packages.
And I'm going to create a new function and the first one I'll do is to get a user func Amster Postgres
DB Repo.
So it has that receiver and I'll call this get user by ID
and I'm probably going to want to get user by email or something as well.
But we'll start with this one and I'll take one parameter to this, which will be ID, which is an end,
and I'll return to things models, dot user and potentially an error.
So as is always the case, we're going to have our context, which I'll copy from the function above
paste in here, and then I'll write my query and my query will be query is assigned the value of select
and then I want to look at my model's table or models just to find out exactly what fields are in there.
So I'd first name, last name, email.
Let's do that ID first name.
Last name that are spell name.
Right.
Email and what else is their first name, last name, password access level created an updated and we
don't need the password, but I'll grab it anyway.
Password.
Access level.
Created and updated at, created at, updated at and I go on to a new line.
From users where ID equals and I put a placeholder in, so that should give me my basic query.
Now, since I know I'm going to get at most one row, I can use Queretaro context.
So Roe is a sign the value of IMDB DOT Queretaro context, which takes the context X and requires the
query string query and it requires the placeholders, which in this case is just one ID.
And that gives me my value of Roe.
Now that I have that, I can actually check for an error and scan the ROE into a variable.
So let's create a variable var user.
I'll call it you because you shouldn't name it the name of the actual table.
You as a model's user and error is assigned the value of.
Robots scan and I'm going to scan it into the variable I just created, so a reference to you ID a reference
to you first name, and let's just duplicate this a few times.
Last name.
Email.
The committer.
Password.
And duplicator owing three more times.
Access level created at an updated at.
If our error is not equal to nil.
Return you error, otherwise return, you know.
OK, so there is one function that allows me to get a user ID, which I know I'm going to need at some
point returns a user ID.
OK, I'll just make sure we got all of the fields in their email password access level created updated
at.
Email, password access, yes, we have them all.
All right, so that one should work now that we've created this, of course, we have to add it to our
repository, otherwise we have no access to it.
So let's go to our repository and paste in that new one.
Good.
So that's our first function.
Our second function should be to modify a user func M Sturr Postgrads DB Repo Update user.
That will be the name of the function.
It will take one parameter which is a named you and will be of type models dot user and it will return
an error.
So context, as usual, copy and paste that read our query query is a sign the value of in tactics
update users set and I'm not going to change the ID, obviously, but it will be first name equal to
dollar sign.
One last name equal to dollar sign to email, equal to dollar sign three password.
I'm not going to do the password this way because there's a different way of handling passwords and
we'll leave that out entirely.
Right now.
Access level equals dollar sign for.
And the only other thing to worry about is updated at equal to dollar sign five.
OK, so there's our query and we can execute that query using our appropriate method from the database
pool.
So the method is called Emerg DB dot exact context.
An exact context returns.
A result and an error, so it leads to things we don't care about the result, but we do care about
the error.
And we're going to execute what in this context, while the parameters it requires first it requires
the context, then it requires the query, then it requires the arguments so we can do it this way.
This is our context.
Our query is query, and I'm going to put these on separate lines just so it's a little easier to read.
The first argument will be you dot first name, then you dot last name, then you dot email, then you
dot access level.
Then the last one is just the time that this was updated, which is just time dot now and that should
take care of that.
But we have to check for the error.
If error is not equal to nil return error, otherwise we're just returning null.
So there is our one for updating user, which we're not using yet, but we will at some point probably
update a user in the database.
Finally, let's look at this one where we actually have to authenticate.
This one's a little harder, really not that hard, but we'll be doing some things you haven't seen
yet, including hashed passwords.
When we store our information about the user in the database, we are never, ever going to store the
password as clear.
Text instead will store the password using a hash using some of the functionality built into the standard
library.
So that means we have to somehow when we're authenticating, compare what the user typed against a hash
in the database.
They're going to type a username and a password, and we're going to take that password and see if the
hash in our database actually corresponds to what it should if they've typed in the right password.
This might sound a little confusing, but it's not that tough.
So let's get started.
We'll create a new function, which I will call func.
It has to be in the receiver of Postgrads DB Repo again.
I will call it authenticate.
And what I want to authenticate are basically two things email and test to password, which will both
come in the form of strings.
And what is going to return are three things.
I'm going to return on into a string and potentially an error.
OK, so we have this function created, but now we need to populate it with meaningful data.
As always, I'm going to copy the context and paste it in here and let's get started.
So the first thing I'm going to do is create an empty variable called ID and this will hold the ID of
the authenticated user if things return as the way they should.
In other words, if they put the right information in there, then I have a typo up here.
So let's fix that.
That's better.
So I created a variable to hold the ID of the authenticated user, and I don't know that until they
successfully authenticate.
So let's try this, the next thing I'm going to want is ver it's going to be called hashed password,
and this will be a string as well.
So the hashed password, which is really hard to say, that's going to hold obviously a hashed password.
Now I want to query the database, first of all, to see if I can find this user.
OK, so I'm going to create a row variable because I'm using the DB query row context.
I mean, I know I'm going to get most one row from the query I'm about to type.
So it requires the context and it requires a query which I will go as follows.
Select ID password from users where email equals dollar sign what?
And the one substitution in that query is obviously the email, which I've already passed as a parameter
to this function, so that will potentially give me my row.
And then, of course, I have to say I have to scan that information into some variables.
I just created those.
I will scan those as follows.
Error is assigned the value of rodent scan and I'm going to scan into the ID variable and the hashed
password variable.
And then I got a check for an error.
If error is not equal to nil, I'll just return my ID, my string, which will be empty in this case.
And the error.
Otherwise, keep going, it's not called air, it's cold or otherwise keep going.
So at this point, if I've passed that test, if they've entered a valid email and that matches an email
in my system, now I need to compare their password with my password.
And we do that as follows, error equals and this is a built in package, right?
Part of the standard library, it's called Decrypt, which is one of the very useful encryption algorithms
or encryption projects out there.
And all I'm going to do is take advantage of a built in function, compare hash and password.
And that takes two arguments.
The HASP hashed password as a slice of bytes and the password I'm testing against, which is a slice
slice of bytes as well.
So I will test first of all, I need to cast to a slice of bytes.
And I'm going to cast the variable.
Hashed password.
Against.
As a slice of bite's.
The test password, so all I'm doing here is saying here is the hash password that I pulled out of the
database, does this hash match the hash that you test or you create by running it against test password,
which is whatever the user typed into the form?
If they did, then all is good and were successful and we can continue.
Otherwise it's not successful and we need to do something else.
So let's first of all, test that error if error is equal.
Exactly.
To be creped error and this has some errors built into it.
So be creped.
Better spell it right.
B creped.
Error, mismatched hash and password, that's a constant that's built into the package in the standard
library.
So if the error is I tried to do the comparison between these two and they don't match, then what I
want to do well, I want to return something.
Otherwise, if the error is something else, if error is not equal to nil, then some other error took
place and I needed to return something.
So I'll return zero and I'll have an empty string.
And whatever the error is in this case, I will return.
Zero.
And I'll return a new error errors, new
incorrect password, actually, that's has to start with the lowercase character in order to be formatted
the way the go wants things to be formatted.
So look at what we've done here.
First of all, we create our context as normal.
Then we declare two variables that we're going to use to store information that we get from the database.
We query the database and we store those two values in that we get from the database ID and password
in the variable ID and hash password.
Then we take advantage of the built in package decrypt to compare the hash that we grab from the database
against a hash created from the password that the user typed into the form.
If we have a mismatch, in other words, there are no errors, but they don't match.
We generate a new error called incorrect password and send that back with dummy information for the
first two parameters.
Otherwise, if there's another error that's not a mismatch password, we just return the error.
And now if we get past all of that, then we are ready to return the necessary information because the
user can be logged in.
So we'll return the ID will return the hashed password from the database, which we may use.
We may not, but I want to be able to do that if I need to, and which are nil because there was no
error.
So let's give this a comment.
Authenticate, authenticate a user.
Alice, do this one as well.
We already gave that one a comment, but we need to put that into our database repository.
So let's open the repository and paste that in there and go back to postgrads and grab the authenticate.
And pass that back into our repository.
Now let's try running the application to make sure that everything runs and compiles nicely and we have
something here.
I use test tube repo as database as written argument test.
She does not implement missing the authenticate method.
OK, so what did I get wrong?
There's the authenticate.
Let's go back to Postgres authenticators right there.
Here it is.
There are no errors, so let's copy that and go back to our repository.
Maybe I just didn't save it.
I'll paste it in to be sure.
Save let's try running this again.
Cannot use and untested literal type O testing.
We have to put these in our test tube repo as well, of course, silly me.
All right, so let's go open up our test tube repo.
And we need to have three new methods in there, so we need to have func M test DB repo.
And the first one we want is get user by ID and it takes a parameter ID event and it returns a model's
user and potentially an error and we'll just have that for you is models dot user return, you know?
So that's the first one taken care of.
Let's take care of the second one.
So that was get user by ID update user.
So here we are.
Let's just copy this to save some typing, go back to our test repo and paste that in there and change
it to a test DB repo and that returns an error return nil and finally authenticate.
So we'll create a dummy method for that as well.
And go back to our test repo, paste that in, change it to a test DB repo and return an international
return, won a string and empty string and an error.
No.
And that should do it.
So now I should be able to stop this.
Clear the screen, run it and without errors.
Perfect.
All right, so the next step is to complete the changes necessary on our login screen to have it point
to an actual form, to create a route for the handler, for the post of that form, and then to create
the necessary handler.
And at that point, we're almost ready to actually authenticate a user and we'll take care of that in
the next few lectures.