JWT Authentication with Gin | Go
Published: 2022-12-16 14:45:24

---
title: JWT Authentication with Gin | Go
published: true
date: 2022-11-15 01:55:21 UTC
tags: 
canonical_url: https://carlosmv.hashnode.dev/jwt-authentication-with-gin-go
---

In this article, we will build an authentication API with two endpoints, a `register` endpoint to sign up users and a `login` endpoint. Then we will add two endpoints that will need authentication and authorization to get access. For this article will be an endpoint to get a list of groceries and an endpoint to add a grocery to the database.

We will use the Gin framework and GORM as ORM.

Prerequisites:

- Go basic knowledge
- JWT knowledge
- Gin
- CRUD operations with GORM

This will be our directory's structure:

```
handlers/
          auth.go
          grocery.go
models/
        database.go
        user.go
        grocery.go
middleware/
        jwtMiddleware.go
utils/
      token.go
main.go
.env

```

Briefly, I will explain these packages. In the `models` module, we have the setup for our database, the model for users with functions to verify and hash the password, and the model for groceries. Every user will be able to add groceries and retrieve all the groceries added.

In the `handlers` module, we have the handlers for registering and login users. Handlers for retrieving and adding groceries.

In the `middleware` module, we have the `jwtMiddleware` to handle incoming requests and ensure the users are authenticated.

We place all the functions related to generating JWT tokens, getting tokens, and parsing JWT in `utils`.

### Setup

First, let's create our module:

```
go mod init <your module name>

```

Then we install the packages we are going to use:

```
go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u github.com/golang-jwt/jwt/v4
go get -u github.com/joho/godotenv
go get -u golang.org/x/crypto

```

In our root directory, we create a package for our model and file to set up our database:

user.go

```
package models

import (

    "gorm.io/gorm"

)

type User struct {
    gorm.Model
    Username string `gorm:"size:255;not null;unique" json:"username"`
    Password string `gorm:"size:255;not null;" json:"-"`
}

```

In this file, we create a User struct for our model with two fields: Username and Password. But also `gorm.Model` adds the fields: ID, CreatedAt, UpdatedAt, and DeletedAt.

databaseSetup.go

```
package models

import (
    "fmt"

    "github.com/joho/godotenv"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
    "os"
)

func Setup() (*gorm.DB, error) {

    err := godotenv.Load()
    if err != nil {
        log.Println("Error loading .env file")
    }
    dbUrl := fmt.Sprint(os.Getenv("DATABASE_URL"))

    db, err := gorm.Open(sqlite.Open(dbUrl), &gorm.Config{})

    if err != nil {
        log.Fatal(err.Error())
    }
    if err = db.AutoMigrate(&User{}); err != nil {
        log.Println(err)
    }

    return db, err
}

```

In this file we create a database using SQLite, we pass the database URL to `sqlite.Open()`.

### Register Handler

models/user.go

```
package models

import (
    "errors"

    "golang.org/x/crypto/bcrypt"
    "gorm.io/gorm"
    "html"
    "log"
    "strings"
)

type User struct {
    gorm.Model
    Username string `gorm:"size:255;not null;unique" json:"username"`
    Password string `gorm:"size:255;not null;" json:"-"`
}

func (user *User) HashPassword() error {
    hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)

    if err != nil {
        return err
    }
    user.Password = string(hashedPassword)

    user.Username = html.EscapeString(strings.TrimSpace(user.Username))

    return nil
}

```

In the `user.go` file we add the function to hash the password using `bcrypt` library.

auth.go

```
package handlers

import (
    "github.com/carlosm27/jwtGinApi/models"
    "github.com/carlosm27/jwtGinApi/utils"
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
    "net/http"
)

type RegisterInput struct {
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
}

type LoginInput struct {
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
}

type Server struct {
    db *gorm.DB
}

func NewServer(db *gorm.DB) *Server {
    return &Server{db: db}
}

func (s *Server) Register(c *gin.Context) {
    var input RegisterInput

    if err := c.ShouldBind(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user := models.User{Username: input.Username, Password: input.Password}
    user.HashPassword()

    if err := s.db.Create(&user).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
    }

    c.JSON(http.StatusCreated , gin.H{"message": "User created"})
}

func (s *Server) Login(c *gin.Context) {

}

```

Here, we develop our register and login handlers. The login handler will be developed further in this article.

The `register` handler takes the username and password passed by the user and binds them to the variable `input`, if any or some field is missed, it will send a `BadRequest` status code.

Then, the username and password are passed to the `user` variable to create a User instance. Then we pass the variable `user` as a reference to the `create()` function after we hash the password, to create a user in our database.

If there is no problem creating a user, the server will send the message "User created".

main.go

```
package main

import (
    "github.com/carlosm27/jwtGinApi/models"
    "github.com/carlosm27/jwtGinApi/handlers"
    "github.com/carlosm27/jwtGinApi/middleware"
    "github.com/gin-gonic/gin"
    "github.com/joho/godotenv"
    "gorm.io/gorm"
    "log"
    "os"
)

func DbInit() *gorm.DB {
    db, err := models.Setup()
    if err != nil {
        log.Println("Problem setting up database")
    }
    return db
}

func SetupRouter() *gin.Engine {
    r := gin.Default()

    db := DbInit()

    server := handlers.NewServer(db)

    router := r.Group("/api")

    router.POST("/register", server.Register)
    router.POST("/login", server.Login)

    return r

}

```

In `main.go` we create a function to initialize our database and create our routers. We create an instance of our database and pass it as an argument to initialize an instance of the server and call our handlers.

```
func main() {

    if err := godotenv.Load(); err != nil {
        log.Println("Error loading .env file")
    }
    port := os.Getenv("PORT")

    r := SetupRouter()

    log.Fatal(r.Run(":"+port))

}

```

Here we load our environment variables and initialize the Gin router.

```
go run github.com/<username>/<module>

```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1668384274670/BqRi5a28q.png)

### Login handler

To develop our login handler we need to write a couple of functions to check the login credentials and verify the password.

user.go

```
 ...

func VerifyPassword(password, hashedPassword string) error {
    return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}

```

The `VerifyPassword` function verifies if the password passed by the user matches the hashed password. This function uses The `compareHashAndPassword` function from the bcrypt library.

If the password and the hashed password match, then the function continues and generate a token, and returns it.

We create a folder named "utils" in our root directory and create the `token.go` file.

token.go

```
package utils

import (
    "fmt"
    "github.com/carlosm27/jwtGinApi/models"
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v4"
    "os"
    "strconv"
    "strings"
    "time"
)

func GenerateToken(user model.User) (string, error) {

    tokenLifespan, err := strconv.Atoi(os.Getenv("TOKEN_HOUR_LIFESPAN"))

    if err != nil {
        return "", err
    }

    claims := jwt.MapClaims{}
    claims["authorized"] = true
    claims["id"] = user.ID
    claims["exp"] = time.Now().Add(time.Hour * time.Duration(tokenLifespan)).Unix()
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

    return token.SignedString([]byte(os.Getenv("API_SECRET")))

}

```

According to its documentation, MapClaims is a claims type that uses the map[string]interface{} for JSON decoding. This is the default claims type if you don't supply one.

Then, we passed `SigningMethodHS256` and the variable `claims` to the function `NewWithClaims`. This function creates a new Token with the specified signing method and claims, according to the [documentation](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#section-readme).

And finally return a `SignedString` that creates and returns a complete, signed JWT. The token is signed using the SigningMethod specified in the token. We passed to this function an "API\_SECRET" or a "SECRETE\_KEY".

To generate a key, we have to have installed OpenSSL in our machine, and execute the following command in our terminal.

```
openssl rand -hex 32

```

And the output will be something like this:

```
09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7

```

auth.go

```
func (s *Server)LoginCheck(username, password string) (string, error) {
    var err error

    user := models.User{}

    if err = s.db.Model(models.User{}).Where("username=?", username).Take(&user).Error; err != nil {
        return "", err
    }

    err = models.VerifyPassword(password, user.Password)

    if err != nil && err == bcrypt.ErrMismatchedHashAndPassword {
        return "", err
    }

    token, err := utils.GenerateToken(user)

    if err != nil {
        return "", err
    }

    return token, nil

}

```

In the `LoginCheck` function we pass a username and a password. The function search in the database to see if the username exists. If it exists, then the function proceeds to verify the password.

```
...

func (s *Server) Login(c *gin.Context) {
    var input LoginInput

    if err := c.ShouldBind(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user := models.User{Username: input.Username, Password: input.Password}

    token, err := models.LoginCheck(user.Username, user.Password)

    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "The username or password is not correct"})
        return
    }

    c.JSON(http.StatusOK, gin.H{"token": token})
}

```

The `Login` function receives a username and a password and binds them to `input` for data validation.

Then it creates a `User` instance with the data passed and checks the credentials. If the credentials are valid, then it returns a token.

```
go run github.com/<username>/<module>

```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1668383998116/aUUbWwy6e.png)

### Authentication

token.go

```
func ValidateToken (c *gin.Context) error {
    token, err := GetToken(c)

    if err != nil {
        return err
    }

    _, ok := token.Claims.(jwt.MapClaims)
    if ok && token.Valid {
        return nil
    }

    return errors.New("Invalid token provided")
}

```

`ValidateToken` verify that incoming requests contain a valid token. If there is not a valid token, it will return an error.

```
func GetToken(c *gin.Context) (*jwt.Token, error) {
    tokenString := getTokenFromRequest(c)
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }

        return privateKey, nil
    })
    return token, err
}

```

`GetToken` receives a token and parses it.

The documentation says that the method `Parse` parses, validates, verifies the signature, and returns the parsed token. `keyFunc` will receive the parsed token and should return the key for validation.

```
type Keyfunc func(*Token) (interface{}, error)

```

> Keyfunc will be used by the Parse methods as a callback function to supply the key for verification. The function receives the parsed, but unverified Token. This allows you to use properties in the Header of the token (such as `kid`) to identify which key to use.

```
func getTokenFromRequest(c *gin.Context) string {
    bearerToken := c.Request.Header.Get("Authorization")

    splitToken := strings.Split(bearerToken, " ")
    if len(splitToken) == 2 {
        return splitToken[1]
    }
    return ""
}

```

Bearer tokens come in the format `bearer <JWT>`, so we split them to return a JWT string.

### Authorization

middleware.go

```
package middleware

import (
    "fmt"
    "github.com/carlosm27/jwtGinApi/utils"
    "github.com/gin-gonic/gin"
    "net/http"
)

func JwtAuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        err := utils.ValidateToken(c)

        if err != nil {
            c.JSON(http.StatusUnauthorized, gin.H{"Unauthorized":"Authentication required"})
            fmt.Println(err)
            c.Abort()
            return
        }
        c.Next()
    }
}

```

We create a [custom middleware.](https://github.com/gin-gonic/gin#custom-middleware) This handler receives a token and calls `ValidateToken()`, if the token is valid, the next handler is called. Otherwise, it will return the status code `Unauthorized`.

models/grocery.go

```
package models

import (

    "gorm.io/gorm"

)

type Grocery struct {
    gorm.Model
    Name string `json: "name"`
    Quantity int `json: "quantity"`

}

```

database.go

```
package models

import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
    "os"
)

func Setup() (*gorm.DB, error) {

    dbUrl := fmt.Sprint(os.Getenv("DATABASE_URL"))

    db, err := gorm.Open(sqlite.Open(dbUrl), &gorm.Config{})

    if err != nil {
        log.Fatal(err.Error())
    }
    if err = db.AutoMigrate(&User{}); err != nil {
        log.Println(err)
    }

    if err = db.AutoMigrate(&Grocery{}); err != nil {
        log.Println(err)
    }

    return db, err
}

```

handlers/grocery.go

```
package handlers

import (
    "github.com/carlosm27/jwtGinApi/models"
    "github.com/gin-gonic/gin"
    "net/http"

)

type NewGrocery struct {
    Name string `json: "name" binding: "required"`
    Quantity int `json: "quantity" binding: "required"`
}

func (s *Server) GetGroceries(c *gin.Context) {

    var groceries []models.Grocery

    if err := s.db.Find(&groceries).Error; err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, groceries)

}

func (s *Server) PostGrocery(c *gin.Context) {

    var grocery NewGrocery

    if err := c.ShouldBindJSON(&grocery); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    newGrocery := models.Grocery{Name: grocery.Name, Quantity: grocery.Quantity}

    if err := s.db.Create(&newGrocery).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusCreated, newGrocery)
}

```

In this file are two handlers. One is to get all the groceries in the database. And the other is to add a grocery to the database.

These two handlers will be accessed only by authorized users.

main.go

```
package main

import (
    "github.com/carlosm27/jwtGinApi/models"
    "github.com/carlosm27/jwtGinApi/handlers"
    "github.com/carlosm27/jwtGinApi/middleware"
    "github.com/gin-gonic/gin"
    "github.com/joho/godotenv"
    "gorm.io/gorm"
    "log"
    "os"
)

func DbInit() *gorm.DB {
    db, err := models.Setup()
    if err != nil {
        log.Println("Problem setting up database")
    }
    return db
}

func SetupRouter() *gin.Engine {
    r := gin.Default()

    db := DbInit()

    server := handlers.NewServer(db)

    router := r.Group("/api")

    router.POST("/register", server.Register)
    router.POST("/login", server.Login)

    authorized := r.Group("/api/admin")
    authorized.Use(middleware.JwtAuthMiddleware())
    authorized.GET("/groceries", server.GetGroceries)
    authorized.POST("/grocery", server.PostGrocery)

    return r

}

```

We create a router group for our groceries handlers with the `JwtAuthMiddleware` handler. This ensures that anyone who wants to get access to `GetGroceries` and `PostGrocery` has to be authorized and pass through the middleware.

With this approach, any authorized users can see all the groceries in the database, even the groceries add it by other users.

Now, we will add a one-to-many relationship. So, every user only can add and get only their groceries.

### Adding one-to-many relationship.

models/user.go

```
type User struct {
    gorm.Model
    Username string `gorm:"size:255;not null;unique" json:"username"`
    Password string `gorm:"size:255;not null;" json:"-"`
    Groceries []Grocery
}

func GetUserById(uid uint) (User, error) {
    var user User

    db, err := Setup()

    if err != nil {
        log.Println(err)
        return User{}, err
    }
    if err := db.Preload("Groceries").Where("id=?", uid).Find(&user).Error; err != nil {
        return user, errors.New("user not found")

    }

    return user, nil
}

```

We add the field `Grocery` to the `User`. And we add the `GetUserById` function. This function will receive an `uid` and will search in the database for the user with that `uid`.

models/grocery.go

```
package models

import (

    "gorm.io/gorm"
)

type Grocery struct {
    gorm.Model
    Name string `json: "name"`
    Quantity int `json: "quantity"`
    UserId uint
}

```

Here we add the field `UserId`, which represents the owner of the grocery.

utils/token.go

```
...

func CurrentUser(c *gin.Context) (models.User, error) {
    err := ValidateToken(c)
    if err != nil {
        return models.User{}, err
    }
    token, _ := GetToken(c)
    claims, _ := token.Claims.(jwt.MapClaims)
    userId := uint(claims["id"].(float64))

    user, err := models.GetUserById(userId)
    if err != nil {
        return models.User{}, err
    }
    return user, nil
}

```

`CurrentUser` extract the token from the request and validate it, and then extract the "id" of the user from the claims. Then the "id" is passed to the `GetUserById` function and returns the user.

handlers/grocery.go

```
func (s *Server) GetGroceries(c *gin.Context) {

    user, err := utils.CurrentUser(c)

    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{"data": user.Groceries})

}

```

Now `GetGroceries` only will return the list of the groceries of the user who is making the request.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1668384552794/UAs9d8ezn.png)

```
func (s *Server) PostGrocery(c *gin.Context) {

    var grocery models.Grocery

    if err := c.ShouldBindJSON(&grocery); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user, err := utils.CurrentUser(c)
    if err != nil {

        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    grocery.UserId = user.ID

    if err := s.db.Create(&grocery).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusCreated, grocery)
}

```

When a user wants to add a grocery, only will be added to the list the user owns.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1668384506977/atC0d2qGL.png)

### Conclusion

This is the first time I build an authentication app using JWT, and writing this article help me a lot to understand JWT. It was a relief to find enough resources about JWT and how to build an authentication app with Go like the article written by [Oluyemi Olususi](https://www.twilio.com/blog/build-restful-api-using-golang-and-gin) or the article written by [Seef Nasrul](https://seefnasrul.medium.com/create-your-first-go-rest-api-with-jwt-authentication-in-gin-framework-dbe5bda72817), because I was so confused about the pattern, and how the server knows that is an authenticated user who is trying to fetch the data. And those articles help me a lot and the Jwt-Go documentation of course.

You don't have to build your own authentication for every app you build, you may use any alternative of BaaS. But, I did it because I wanted to learn about it. And I learn by building and writing.

I hope you enjoy this article, thank you for taking the time to read it.

If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through [Twitter](https://twitter.com/Carlos_marcv), or [LinkedIn](https://www.linkedin.com/in/carlos-marcano-a2135a134/).

The source code is [here](https://github.com/carlosm27/jwtGinApi)

### References

- [Jwt-Go documentation.](https://pkg.go.dev/github.com/golang-jwt/jwt/v4#pkg-overview)

- [JSON Web Token Introduction.](https://jwt.io/introduction)

- [Gin Github page.](https://github.com/gin-gonic/gin)

- [Build a RESTful API using Golang and Gin by Oluyemi Olususi.](https://www.twilio.com/blog/build-restful-api-using-golang-and-gin)

- [Create your first Go REST API with JWT Authentication in Gin Framework by Seef Nasrul](https://seefnasrul.medium.com/create-your-first-go-rest-api-with-jwt-authentication-in-gin-framework-dbe5bda72817)

- [JWT authentication In Golang with gin by Bikash Dulal](https://articles.wesionary.team/jwt-authentication-in-golang-with-gin-63dbc0816d55)