This blog post is the second part of a series that guides readers through the process of implementing JSON Web Token (JWT) authentication in a Go application using the Gin web framework. In this blog, we discuss storing JWT tokens in cookies for authentication purposes and defining the necessary authentication routes.
In the previous blog, we learned how to set up the project, including connecting to the database and migrating the user model. We also covered the creation and validation of JWT tokens, as well as the implementation of JWT middleware. In this second part of the series, we will explore how to store JWT in cookies using Go, and we will also discuss the authentication routes.
In the previous section, we learned how to use JWT authentication for our Go application. In this section, we will learn how to store JWT tokens in cookies for authentication.
An HTTP cookie is a small piece of data stored on the client’s computer by the web browser. The data is sent back to the server on every request to the server that the browser makes. Cookies are used to store user information or preferences for future use, and they are an essential part of web development.
To set cookies in Go, we can use the SetCookie function provided by the net/http package. We can use this function to set the cookie's name, value, expiration time, and other attributes.
To retrieve cookies in Go, we can use the c.Cookie function provided by the gin context. This function returns a pointer to an http.Cookie object, which contains the cookie's value and attributes.
We will introduce some helper functions that will help us to set and clear the cookies. Here’s what the cookieHelper.go file looks like:
In this example, we have buildCookie function that takes in the cookie's name, its value and its expiry time and returns an HTTP.Cookie. The SetCookie function is a helper function that sets an HTTP cookie for a given gin.Context. The function takes three arguments:
The function first calls the buildCookie function, passing in the name, value, and expiration arguments, and gets back a pointer to an http.Cookie instance. It then calls the http.SetCookie function to set the cookie on the response writer of the gin.Context.
The ClearCookie function is used to clear the value of an existing cookie by setting its value to an empty string and its MaxAge attribute to -1, indicating that the cookie has expired.
The function takes in two parameters:
The function calls the buildCookie helper function to create a new cookie object with the given name and an empty value and sets its MaxAge attribute to -1. The cookie is then written to the response header using the http.SetCookie function, which takes the c.Writer field of the gin.Context object as its first argument. This causes the client's browser to delete the specified cookie.
In this section, we will learn about the routes that we came across in the first part of the series. Let us have a look at the AuthRoutes route:
This code block defines the routes for the authentication functionality of the application. It utilizes the Gin web framework to create and manage the routes. The AuthRoutes function accepts an incomingRoutes parameter of the type *gin.Engine which is an instance of the Gin engine.
The POST method is used for the /users/signup and /users/login routes, which are responsible for creating a new user and logging in an existing user respectively. These routes are handled by the Signup and Login functions in the controllers package.
The /users/refresh-token route is a GET route that is responsible for generating a new access token and refresh token for an authenticated user. This route is handled by the RefreshToken function in the controllers package.
The /users/logout route is a POST route that is responsible for logging out an authenticated user by invalidating their refresh token. This route is handled by the Logout function in the controllers package.
By defining the routes in this way, the application can easily handle requests for user authentication and respond with the appropriate actions, such as creating a new user, logging in an existing user, refreshing access and refresh tokens, and logging out a user.
Now let’s have a look at the authController.go file. This file is a set of controller functions for handling user authentication and authorization in our web application.
The file starts by importing necessary packages and dependencies, including the Gin web framework, the Go validator package for data validation, and the bcrypt library for hashing passwords.
The UserResponse function returns a serialized user object with selected properties from the models.User struct.
Next, the Signup function handles user signup by first binding the request data to a models.User struct, validating the input using the validator package, and checking if a user already exists with the same email address. If the email is not already registered, the function hashes the password and creates a new user record in the database. It then generates an access token and a refresh token using the helpers.GenerateToken function and sets cookies with the tokens using the helpers.SetCookie function. Finally, the function returns a serialized user object in JSON format.
The Login function handles user login by first binding the request data to a map of strings and then checking if a user with the provided email exists in the database. If a user exists, the function uses the checkPasswordHash function to compare the provided password with the stored hashed password. If the passwords match, the function generates new access and refresh tokens, sets cookies with the tokens, and returns a serialized user object in JSON format.
The RefreshToken function handles refreshing the access token using the refresh token. The function first retrieves the refresh token from the cookie, validates the token using the helpers.ValidateToken function, retrieves the user ID from the token claims, and retrieves the user record from the database. It then generates a new access token and sets a new cookie with the token.
The Logout function handles user logout by clearing the cookies containing the access and refresh tokens.
Lastly, the hashPassword and checkPasswordHash functions are used to hash and compare passwords using the bcrypt library.
We now got to know how to authenticate a user with login and signup method. We will now learn about the protected routes UserRoutes. We authorize a user with the help of AuthMiddleware what we learned in the first part of this series.
This code defines two routes for handling HTTP GET requests to retrieve user data:
Overall, this code is responsible for handling user-related GET requests and directing them to the appropriate functions in the controllers package to generate the response data.
Now let us have a look at the userController.go file to have a better understanding of the routes.
The GetUsers function retrieves all users from the database using the Find method from the database package. It then maps each user to a UserSerializer struct defined in the serializers package using the UserResponse function, which formats the user data for JSON serialization. Finally, the list of serialized users is returned as a JSON response using the JSON method from the gin.Context object.
The GetUser function retrieves a single user from the database by ID using the Where and First methods from the database package. If no user is found, a 404 error is returned as a JSON response. Otherwise, the retrieved user is mapped to a UserSerializer struct using the UserResponse function, and returned as a JSON response using the JSON method from the gin.Context object.
To conclude, the series explained how to implement JWT authentication in a Go application using the Gin framework. It started by explaining the basics of JWT and its benefits. Then it moved on to explaining how to set up a basic Gin application and install the necessary packages.
After setting up the application, the blog explained how to create a user model, set up the database, and implement user authentication using JWT. It also explained how to create a middleware function to authenticate the JWT token on protected routes.
Finally, the blog demonstrated how to test the authentication functionality using Postman. By following the steps outlined in the blog, readers should have a solid understanding of how to implement JWT authentication in a Go application using the Gin framework.
Using JWT authentication in Go offers several benefits, including:
While JWT authentication provides a secure and efficient way to authenticate users, there are some considerations to keep in mind for future improvements. Some of these include:
Overall, JWT authentication is a great way to secure your web applications, and with the right considerations and best practices, it can be a reliable and efficient method of user authentication in Go.