Golang: Create Authentication JWT #1
Tags: go, tutorial, jwt
Published: 2021-01-01 15:36:47

After all this time we learned Golang which is a simple application application, finally we want to make a complete application but we are stuck with the question, how do we authenticate in Golang? That was also my question when I decided to move from node-js to golang.

## Folder structure introduction
In this tutorial we will use the folder structure that I usually use
├───app
├───domain
├───entities
│   └───helper
├───interface
│   └───http
└───services
    └───user
        ├───repository
        │   └───postgres
        └───usecase
``app``: contains main.package which will be executed the first time the go program is run.
``domain``: contains the struct domain
``entities/helper``: contains helper functions
``interface/http``: contains the gin gonic routing handler
``service``: contains service
``usecase``: where to put usecase instead of the user.
``repository``: where to put the repository

now run ``go mod init shellrean.com/auth`` in the created folder.

## Create a table in postgresql
For our DBMS, this time using PostgreSQL, create a table with the names of users with the following specifications:
- id  (int) auto increment
- name (string)
- email (string)
- password (string)
- created_at (timestamp)
- updated_at (timestamp)
Then create 1 dummy user, make it free to generate passwords from bcrypt using the online tool at Bcrypt-Generator
## Create domain files
create ``user.go`` file and save it in the ``domain`` folder, we will create a domain struct and user interface in this file.
``` golang
package domain

import (
    "time"
    "context"    
)

type User struct {
    ID          int64       `json:"id"`
    Name        string      `json:"name" validate:"required"`
    Email       string      `json:"email" validate:"required,email"`
    Password    string      `json:"password" validate:"required,min=6"`
    CreatedAt   time.Time   `json:"created_at"`
    UpdatedAt   time.Time   `json:"updated_at"`
}

type UserUsecase interface {
    Authentication(ctx context.Context, ur DTOUserLoginRequest) (DTOTokenResponse, error)
    RefreshToken(ctx context.Context, ur DTOTokenResponse) (DTOTokenResponse, error)
}
type UserRepository interface {
    GetByID(ctx context.Context, id int64) (User, error)
    GetByEmail(ctx context.Context, email string) (User, error)
}

```
create a domain.go file then save it in the same folder, we will make a domain struck for the token in this file
``` golang
package domain

type TokenDetails struct {
	AccessToken		string
	RefreshToken	        string
	AtExpires		int64
	RtExpires		int64
}
```
## Create a file repository
create ``main_user.go`` file and save it in the ``services/user/repository/postgres`` folder, we will create a repository function for retrieving data from the database.
``` golang
package postgres

import (
    "context"
    "database/sql"

    "shellrean.com/auth/domain"
)

type postgresUserRepository struct {
    Conn *sql.DB
}

func NewPostgresUserRepository(Conn *sql.DB) domain.UserRepository {
    return &postgresUserRepository{
        Conn,
    }
}

func (m *postgresUserRepository) GetByEmail(ctx context.Context, email string) (u domain.User, err error) {
    query := `SELECT id,name,email,password,created_at,updated_at
            FROM users WHERE email=$1`

    err = m.Conn.QueryRowContext(ctx, query,email).
            Scan(&u.ID,&t.Name,&t.Email,&t.Password,&t.CreatedAt,&t.UpdatedAt)
    if err != nil {
        return err
    }

    return
}
```
## Create a helper file
create ``token.go`` file and save it in the ``entities /helper`` folder, we will create a helper function that will perform the operation with JWT
``` golang
package helper

import (
    "strings"
    "time"

    "github.com/dgrijalva/jwt-go"

    "shellrean.com/auth/domain"
)

func GenerateTokenDetail(td *domain.TokenDetails) {
    td.AtExpires = time.Now().Add(time.Minute * 15).Unix()
    td.RtExpires = time.Now().Add(time.Hour * 24 * 7).Unix()
}

func CreateAccessToken(key string, user domain.User, td *domain.TokenDetails) (err error) {
    atClaims := jwt.MapClaims{}
    atClaims["authorized"] = true
    atClaims["user_id"] = user.ID
    atClaims["exp"] = td.AtExpires
    
    at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
    td.AccessToken, err = at.SignedString([]byte(key))
    if err != nil {
        return domain.ErrSessDecode
    }
    return
}

func ExtractToken(bearer string) (res string) {
    str := strings.Split(bearer, " ")
    if len(str) == 2 {
        res = str[1]
        return
    }
    return
}

func VerifyToken(key string, tokenString string) (*jwt.Token, error) {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, domain.ErrSessDecode
        }
        return []byte(key), nil
    })
    if err != nil {
        return nil, domain.ErrSessVerifation
    }
    return token, nil
}

func TokenValid(token *jwt.Token) error {
    if _, ok := token.Claims.(jwt.Claims); !ok || !token.Valid {
        return domain.ErrSessInvalid
    }
    return nil
}

func ExtractTokenMetadata(token *jwt.Token) map[string]interface{}{
    claims, ok := token.Claims.(jwt.MapClaims)
    if ok && token.Valid {
        return claims
    }
    return nil
}
```

At this point, we have created 2 domain files, 1 repository file and 1 helper file, we will continue in the next article.