Lesson Description

The "Token API Handlers" Lesson is part of the full, Complete Go for Professional Developers course featured in this preview video. Here's what you'd learn in this lesson:

Melkey returns to the API layer and creates the token API handler functions. The HandleCreateToken function gets the user and verifies that the passwords match. If there are no errors in the authentication process, a new token is created. Finally, a route is added, and the application is updated.

Preview
Close

Transcript from the "Token API Handlers" Lesson

[00:00:00]
>> Melkey: So we went SQL layer, database layer, we know where we're going API layer. Let's go into the API, we're gonna make a new file called token_handler.go, package API like so. Quickly create our handlers and these shrugs, we can do type TokenHandler struct. This one can be a little different, cuz not only we're gonna have the tokenStore of store.TokenStore, we're also gonna have a userStore.

[00:00:30]
UserStore, that's also gonna have our logger, which *logger., oops, not that. Like that, okay? Func NewTokenHandler, let's go ahead and create this, can take a tokenStore type store.TokenStore, userStore type store.UserStore, logger like so. This is going to return a pointer to TokenHandler. Go ahead and return TokenHandler and fill out those fields to tokenStore, tokenStore, userStore, userStore, and then logger is logger.

[00:01:15]
Oops.
>> Melkey: Cool.
>> Melkey: Okay, we'll basically create one function here, which going to be creating a token for authentication.
>> Melkey: But since this is gonna be create method, and we've now handled, I think, two different create methods, one for user, one for workouts and even updating. We have to always remember that we have the ability to dump the data from our client into a placeholder structure.

[00:01:52]
And that should be ringing the bells of a struck, we can use another struck, right? So we can do type, we can call this createTokenRequest, this gonna be a struct that we're gonna decode the information we get from our client, okay? So here we can put Username is gonna be a string,
>> Melkey: Like this, and then the password is gonna be a string 'json:"password"', like that, right?

[00:02:22]
Now that we have that, let's go ahead and create our method on the TokenHandler, which is gonna be HandleCreateToken, like so. It's gonna be http.ResponseWriter, and our pointer to http.Request, let's get ourselves more room here. So first, let's go ahead and parse the request. So we're gonna declare this variable request, which can be of type createTokenRequest, the struct we just created above.

[00:02:50]
Let's go ahead and decode everything in there, so json.NewDecoder(r.Body), I'm gonna decode this into the address of request like so. If error does not equal to nil, let's check those errors with h.logger.Printf, I can just say ERROR. Okay, and we can say, createTokenRequest, right, module o v, let's put err.

[00:03:25]
Then we can use our utils.WriteJSON package (w, http.StatusBadRequest, utils.envelope), let's put "error" in here. And over here, we can say invalid request payload, okay, and then let's make sure we return out of it.
>> Melkey: So this is pretty standard stuff we've done a bunch of times already. So first we're gonna retrieve if the user exists by username.

[00:03:55]
Then they usually we know exist. If they do, we're gonna check invalidate the passwords, if they match we will then issue a token, okay? So first, let's get the user, we can do this cuz we've already at the user store. We can already do this since we've created the function to do that, which was get user by username.

[00:04:13]
So we can do user, err, is gonna be h.userStore, that's our TokenHandler has the userstore within it. We can do GetUserByUsername(re.Username), okay? If err != nil || user is nil, okay, h.logger.Printf, we can say ("ERROR: GetuserByUsername: "%v") utils.writeJSON(w, http, and we can say StatusInternalServerError).
>> Melkey: Okay, let's go ahead and return.

[00:05:16]
>> Melkey: So at this point on that line, we should have two pieces of information. We have the request information from our decoding, and then we also have a user struck from getuser by username. Next thing we wanna do is validate those passwords so we can do passwordsDoMatch, or something like that, and that's obvious, error is going to be,
>> Melkey: User.PasswordHush, all right, .matches, so this is the second function we created a little bit ago, and we can do (req.Password), okay?

[00:05:54]
If err != nil, we definitely wanna handle this, we can h.logger.Printf("ERROR: PasswordMatch, you can just do PasswordHush_Mathes.
>> Melkey: Nope, utils.WriteJSON, okay, so w, http StatusInternalError,
>> Melkey: InternalServerError, utils.Envelope("error":). And then we can just put "internal server error" like so, let's make sure we return.
>> Melkey: Okay, and then here, if the passwords don't match, if the passwords do not match, right, we can use the exclamation point operator to evaluate boolean expression.

[00:06:56]
So this would be, if it's not true, or if it's false, we can do utils.WriteJSON(w), we can do http. It's not gonna be internal server errors, can be StatusUnauthorized, we can do utils.Envelope("error":) and then the error message this time, we could just say, "invalid credentials".
>> Melkey: Keep it broad, can make sure we add the return statement there.

[00:07:25]
And now at this point, we can say that we've validated a user with the right username that they exist. Now we've matched the password we sent with the password that we've hashed using bcrypt. Okay, so let's actually go ahead and issue this token file, let's do token, err is going to be h.tokenStore.

[00:07:46]
Let's create this new token, I'm gonna pass in user.ID. We're gonna give it a 24 hour lifespan, so 24*time.Hour, okay? And for the scope, we'll do tokens.scopeAuth like so, I'm gonna go ahead and handle, so here, if err != nil,
>> Melkey: Supposed to be error, I said password matches, we can just say couldn't create a token.

[00:08:18]
So Creating Token. So this is still gonna be an internal server error, we can keep it like so, we can return out and in here. We can do utils.WriteJSON(w, ) we can do http.StatusCreated, okay, utils.Envelope. We can say something like, you see something like "auth_token", and then the associate token like this.

[00:08:47]
Whatever you wanna call it, token, auth_token, secret_token, whatever, it's all up to you. But just to be mindful that your client will have to decode that JSON as auth_token. Hopefully this is making sense. Hopefully, at this point the pattern is very obvious, like handler, database, SQL, app, route.go, it's very defined.

[00:09:07]
And I think it scales really nicely, right, it's very containerized. TokenHandler, you know exactly what's gonna happen here, and then you can match it with tokenStore and be like, okay, I see what's going on here. And then if tokenHandler has a userStore, you know exactly where that is, see, look, okay, I can see what's happening here.

[00:09:26]
But with that being said, let's go back to our app.go, since we do need to hook up the tokenhandler here. So, TokenHandler and api.TokenHandler, like so. And actually this is a TokenStore, we'll just do store.NewPostgresTokenStore, this is gonna take our DB, so pgDB. Can get mad at us, and here we do TokenHandler, it's gonna be api.NewTokenHandler, like so.

[00:10:06]
It's gonna take the TokenStore, the userStore and logger. And then put TokenHandler as TokenHandler in the actual return application struct. All right, let's head on over to the last place we can modify, which is this location here, the routes location. So here, this is for users when they want to authenticate.

[00:10:34]
So you can use them like ("/token/authentication"), like that, and we can do app.TokenHandler.HandleCreateToken, again remember, we're passing it as first class citizens.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now