Authentication using JWT with Golang [Fiber] 2023 🧬
¿Hablas español? Ver en español
Authentication can be a headache when you are just starting out in the world of programming, so I want to make your life a little easier and teach you how to implement it in a basic way using the Fiber JWT package in its version 3.
Let’s prepare the environment:
We create our 📁 folder where our project will be located and initialize our go project using the command:
go mod init github.com/solrac97gr/basic-jwt-auth
Don’t forget to replace solrac97gr with your github user.
Now we need to download 3 packages that we will use in this basic guide:
The Fiber JWT package
go get github.com/gofiber/jwt/v3
The Golang JWT package
go get github.com/golang-jwt/jwt/v4
And finally the Fiber package
go get github.com/gofiber/fiber/v2
Hands on code
Models:
We will start by creating our structures inside the models/models.go folder, you can create it in separate files or together, for the example I will put them in one but in the repository they will be separated:
package models
type LoginRequest struct {
Email string `json:"email"`
Password string `json:"password"`
}
type LoginResponse struct {
Token string `json:"token"`
}
type User struct {
ID int
Email string
Password string
FavoritePhrase string
}
Config
Within the config/config.go folder you have the task of extracting the signature for your token from the environment variables or from some configuration file, for the example I will use a constant, this should never be done since you would compromise the security of the Token.
package config
// The secret key used to sign the JWT, this must be a secure key and should not be stored in the code
const Secret = "secret"
Middlewares
Middlewares are programming tools that can serve as a prelude to actions carried out by our API, such as validating that the user is logged in or restricting access to certain countries.
Fiber provides us with a JWT middleware that we will initialize in the middlewares/auth.go folder:
package middlewares
import (
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/jwt/v3"
)
// Middleware JWT function
func NewAuthMiddleware(secret string) fiber.Handler {
return jwtware.New(jwtware.Config{
SigningKey: []byte(secret),
})
}
Repository
In order not to overcomplicate the project and deviate from the focus, we will use a function that will simulate a call to a database and will return the user when the password and email match or an error when they do not, this will be in the repository/FindByCredentials.go folder.
package repository
import (
"errors"
"github.com/solrac97gr/basic-jwt-auth/models"
)
// Simulate a database call
func FindByCredentials(email, password string) (*models.User, error) {
// Here you would query your database for the user with the given email
if email == "test@mail.com" && password == "test12345" {
return &models.User{
ID: 1,
Email: "test@mail.com",
Password: "test12345",
FavoritePhrase: "Hello, World!",
}, nil
}
return nil, errors.New("user not found")
}
Handlers
Now the important part, let’s assume our user has just signed up using the following credentials.
{
"email": "test@mail.com",
"password": "test12345"
}
We will create the endpoint to login and a protected endpoint where we extract the information stored in the token.
We will do this in the handlers/handlers.go folder.
package handlers
import (
"time"
"github.com/gofiber/fiber/v2"
jtoken "github.com/golang-jwt/jwt/v4"
"github.com/solrac97gr/basic-jwt-auth/config"
"github.com/solrac97gr/basic-jwt-auth/models"
"github.com/solrac97gr/basic-jwt-auth/repository"
)
// Login route
func Login(c *fiber.Ctx) error {
// Extract the credentials from the request body
loginRequest := new(models.LoginRequest)
if err := c.BodyParser(loginRequest); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": err.Error(),
})
}
// Find the user by credentials
user, err := repository.FindByCredentials(loginRequest.Email, loginRequest.Password)
if err != nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": err.Error(),
})
}
day := time.Hour * 24
// Create the JWT claims, which includes the user ID and expiry time
claims := jtoken.MapClaims{
"ID": user.ID,
"email": user.Email,
"fav": user.FavoritePhrase,
"exp": time.Now().Add(day * 1).Unix(),
}
// Create token
token := jtoken.NewWithClaims(jtoken.SigningMethodHS256, claims)
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte(config.Secret))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": err.Error(),
})
}
// Return the token
return c.JSON(models.LoginResponse{
Token: t,
})
}
// Protected route
func Protected(c *fiber.Ctx) error {
// Get the user from the context and return it
user := c.Locals("user").(*jtoken.Token)
claims := user.Claims.(jtoken.MapClaims)
email := claims["email"].(string)
favPhrase := claims["fav"].(string)
return c.SendString("Welcome 👋" + email + " " + favPhrase)
}
Time to unite everything
Now in our main.go file located at the root of the project we will put all the parts together and get our API up and running.
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/solrac97gr/basic-jwt-auth/config"
"github.com/solrac97gr/basic-jwt-auth/handlers"
"github.com/solrac97gr/basic-jwt-auth/middlewares"
)
func main() {
// Create a new Fiber instance
app := fiber.New()
// Create a new JWT middleware
// Note: This is just an example, please use a secure secret key
jwt := middlewares.NewAuthMiddleware(config.Secret)
// Create a Login route
app.Post("/login", handlers.Login)
// Create a protected route
app.Get("/protected", jwt, handlers.Protected)
// Listen on port 3000
app.Listen(":3000")
}
Running our application
To do this, once we finish creating the necessary files, we will execute the following command.
go run main.go
This will give us the following result in the console, that means that our app is already running on port 3000.
Now we will try using postman to execute our requests.
We execute the Login and we see that we obtain our Token 🌟
Now we will use our token in the next request to the protected route and that’s it our route identifies which user is logged in thanks to the token information.
You can download Postman petitions here
This is how our final project would look
You can review the repository at the following link github.com/basic-jwt-auth
This would be the file structure.
Closing
In this way we can authenticate users using JWT in Go Fiber, however the structure of this project has many improvement points that I cover in this article and that you can review to make something more robust using hexagonal architecture and DDD.