Introduction to JSON Web Tokens (JWT)
What's going on guys? In this video we're going to take a look at JSON web tokens and then we are going to create an authorization middleware for our Gin application using this standard. JSON web tokens are an open standard; it's a secure mechanism to transfer claims between two parties. A claim could be user information or custom metadata. Those claims are encoded as a JSON object and digitally signed using a secret key, which implies that the information is verified and trusted.
These are some of the key characteristics of JSON web tokens. A single token can be used and verified by multiple backends and services. Typically, there is one authentication service that provides the token after the user logs in successfully, and then the rest of the applications and services can verify that the token is valid using the secret key that was applied by the authentication service to generate the token. There is no session management required; it's a hundred percent stateless. So you don't need a database and you don't need an in-memory key-value store to store these sessions. It's simpler to use and the development time is faster using the existing JSON web tokens libraries.
And this is the JSON web token structure. We have three parts: we have the header, we have the payload, and we have the signature, and all these fields are separated by dots. And here is an example of a JSON web token where we have the header that includes the signing algorithm and the token type, typically JWT. Then we have the payload where we can extend the standard claims with custom claims. In this case, 'name' is a custom claim and the token expiration time is a standard claim. And finally, we have the signature using the algorithm specified on the header that takes the header and the payload, both base64 URL encoded, and a secret key. The signature is used to verify that the token wasn't changed along the way. Remember to follow Pragmatic Reviews on Twitter and Instagram, and let's get started.
Authentication vs. Authorization Flow Overview
Okay, so we have two steps here. We have an authentication step and the authorization step. So the authentication step includes the login of the user and the JSON web token creation, and the authorization step includes basically the token validation.
So for the authentication step, I created this login service here. And here I'm using just this username and password, so I'm going to authenticate just this user. I'm keeping it really simple here. Typically you are going to make a query to a database or call an external API, but I'm just keeping it really, really simple. And the second part of the authentication step is the token creation. So here I created this JWT service and I created this method, this generateToken method. And for the authorization step, I created this validateToken method.
Implementing the Login and Authorization Endpoints
Now let's see how we use these services to authenticate users and authorize API requests from our Gin API. Okay, and then now let's go to the server file here where we have our API endpoints. So here I added this login endpoint. And basically, this endpoint is going to call this login controller. So let's go there.
And this login controller is doing two things: is basically calling this login service with the user credentials, and if the user is authenticated, it's going to call this JWT service to generate the token and retrieve it. And that's it for the authentication part. We are authenticating the user and if the user is actually authenticated, we generate the token and we retrieve it here. Let's go back. We are returning it here using this JSON format.
And for the authorization step, I created this middleware that I'm calling here for this API group to get existing videos and to create new videos. So let's go to the middleware. I'm sorry, here. And here I'm using this authorizeJSONWebToken function. And here what I'm using basically is this service again and this method, this validateToken method where we pass the string, and it's going to parse the token and using the secret key is going to validate that JSON web token.
Deep Dive into the JWT Service
Okay, and now let's move on to the service that is the most important part of the code regarding JSON web tokens. So here we have these two functions: this generateToken function and the validateToken function. Here I added these two custom claims, name and admin, and here are the standard claims, basically the audience, when the token is expiring, an identifier, and a couple of other claims here. Here I'm assigning the secret key. I'm assigning the issuer. Remember that the issuer is who is signing the token. And here I get this function where I'm using this environment variable to get the secret signing key, and if I don't get any values from there, I'm just hardcoding this secret key.
And here we have the two functions. So first, we have the generateToken function. I am using this library here. So if we go to jwt.io, here we can go to libraries, and here we have different languages. So if you go to Go, I'm using actually this one. Here we can see what are the signing algorithms supported by each implementation. There are a bunch of implementations. I'm just using the first one. This is the most popular library. So let's go back.
And here we have this 'issue a token' function where I am creating these custom claims. Here I receive the username, I receive if the user is admin or not. And here I created the standard claims including the token expiration, the issuer, and the time when this token is created. Here I call this NewWithClaims function. This function is part of this JWT library that I'm using. I'm assigning what is the signing method and here I'm passing the claims that I created here. And here I'm signing the token using the secret key.
And then we have the validateToken function here that is going to use this Parse function from the library and it's going to parse and validate the token using the secret key that we are passing here. And if we go back to the middleware here, basically if the token is not valid, I am returning a 401. So using this, I'm preventing from calling the API endpoint that is using this middleware.
Demonstration with Postman
So let's run the server. Okay, and now let's go to Postman. So first we need to call this login endpoint. So I'm going to execute this request passing the user and the password. These are the only values that we are allowing to authenticate. Basically here we get the JWT token. We can paste this here. We can go for example here to this debugger and we can pass it here. And here we can see the values. We see the name that is 'pragmatic'. We are passing 'true' here to this admin attribute. And here we have the expiration time, we have the time when this token was created, and here is the issuer that is basically who is signing this token. Here we have the header, and here we get an invalid signature because here we are not passing the secret key that we are using to generate the token. So this makes total sense here.
Okay, so let's go back. And now we need to use this token to call our API. So let's use it here. Remember that previously we've been using a basic authorization middleware, so we need to replace this value by Bearer here like that, and we paste this token. We receive this empty array. It makes sense because we didn't create any videos yet. And here I'm going to create a new video. So here's the bearer, and we paste again the JWT token. Okay, now let's create a new video. Okay, and now this returns a 200. And what happens if we modify the token? Let's add a dash at the beginning. This is going to return a 401. Okay, let's go back. And now if I call this endpoint, I should get, yes, get this video that I just created. So that's pretty much all I have for today. Thank you guys for watching. Remember to subscribe to the channel and I see you guys in the next one. Take care, bye.