DEV Community

ShellRean
ShellRean

Posted on

Golang: Create Authentication JWT #2

Write DTO token

Create dto to transfer our token, create file token.go save it in entities/DTO

package dto

type TokenResponse struct {
    AccessToken     string      `json:"access_token"`
    RefreshToken    string      `json:"refresh_token"`
}
Enter fullscreen mode Exit fullscreen mode

Create handler

create main_handler.go and save it in interface/http/handler and write code below.

package handler

import (
    "net/http"
    "github.com/gin-gonic/gin"
    "shellrean.com/auth/domain"
    "shellrean.com/auth/entities/DTO"
)

type UserHandler struct {
    userUsecase     domain.UserUsecase
}

func NewUserHandler(r *gin.Engine, m domain.UserUsecase) {
    handler := &UserHandler{
        userUsecase:    m,
    }

    r.POST("/auth", handler.Autheticate)
    r.POST("/refresh-token", handler.RefreshToken)
}
Enter fullscreen mode Exit fullscreen mode

We declare 2 routes, /auth will handle for authentication and /refresh-token for give user refresh token, at the same file main_handler.go add handler Authenticate

func (h *UserHandler) Autheticate(c *gin.Context) {
    var u domain.User
    if err := c.ShouldBindJSON(&u); err != nil {
        c.JSON(
            http.StatusUnprocessableEntity,
            gin.H{"error": "Unprocessable Entity"},
        )
        return
    }

    res, err := h.userUsecase.Authentication(c, user)
    if err != nil {
        c.JSON(
            http.StatusInternalServerError,
            gin.H{"error": err}
        )
        return
    }

    c.JSON(http.StatusOK, gin.H{"data": res})
}
Enter fullscreen mode Exit fullscreen mode

We also create handler for handle Refresh token request.

func (h *UserHandler) RefreshToken(c *gin.Context) {
    var u dto.TokenResponse
    if err := c.ShouldBindJSON(&u); err != nil {
        c.JSON(
            http.StatusUnprocessableEntity,
            gin.H{"error": "Unprocessable Entity"},
        )
        return
    }

    token := domain.Token {
        AccessToken: u.AccessToken,
        RefreshToken: u.RefreshToken,
    }

    err := h.userUsecase.RefreshToken(c, &token)
    if err != nil {
        c.JSON(
            http.StatusInternalServerError,
            gin.H{"error": err}
        )
        return
    }

    data := dto.TokenResponse{
        token.AccessToken,
        token.RefreshToken,
    }

    c.JSON(http.StatusOK, gin.H{"data": data})
}
Enter fullscreen mode Exit fullscreen mode

Create usecase

package usecase

import (
    "context"
    "time"
    "fmt"

    "golang.org/x/crypto/bcrypt"

    "shellrean.com/auth/domain"
    "shellrean.com/auth/config"
    "shellrean.com/auth/entities/helper"
)

type userUsecase struct {
    userRepo        domain.UserRepository
    contextTimeout  time.Duration
}

func NewUserUsecase(d domain.UserRepository, timeout time.Duration) domain.UserUsecase {
    return &userUsecase {
        userRepo:       d,
        contextTimeout: timeout,
    }
}

func (u *userUsecase) Authentication(c context.Context, ur domain.User) (td domain.Token, err error) {
    ctx, cancel := context.WithTimeout(c, u.contextTimeout)
    defer cancel()

    user, err := u.userRepo.GetByEmail(ctx, ur.Email)
    if err != nil {
        return domain.Token{}, err
    }

    if user == (domain.User{}) {
        return domain.Token{}, fmt.Errorf("Error user not found")
    }

    err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(ur.Password))
    if err != nil {
        return domain.Token{}, fmt.Errorf("Invalid credential")
    }

    helper.GenerateTokenDetail(&td)

    err = helper.CreateAccessToken(u.cfg.JWTAccessKey, user, &td)
    if err != nil {
        return domain.Token{}, err
    }

    err = helper.CreateRefreshToken(u.cfg.JWTRefreshKey, user, &td)
    if err != nil {
        return domain.Token{}, err
    }

    return
}

func (u *userUsecase) RefreshToken(c context.Context, td *domain.Token) (err error) {
    ctx, cancel := context.WithTimeout(c, u.contextTimeout)
    defer cancel()

    token, errs := helper.VerifyToken(u.cfg.JWTRefreshKey, td.RefreshToken)
    if errs != nil {
        return fmt.Errorf("Invalid when verification")
    }
    err = helper.TokenValid(token)
    if err != nil {
        return fmt.Errorf("Session invalid")
    }

    data := helper.ExtractTokenMetadata(token)

    user := domain.User{
        ID: int64(data["user_id"].(float64)),
    }

    helper.GenerateTokenDetail(td)

    err = helper.CreateAccessToken(u.cfg.JWTAccessKey, user, td)
    if err != nil {
        return err
    }

    err = helper.CreateRefreshToken(u.cfg.JWTRefreshKey, user, td)
    if err != nil {
        return err
    }

    return
}
Enter fullscreen mode Exit fullscreen mode

And the last, we create main.go store it int app/main.go

Enter fullscreen mode Exit fullscreen mode

Discussion (0)