DEV Community

ShellRean
ShellRean

Posted on • Edited on

Golang: Create Authentication JWT #1

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.
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) } 
Enter fullscreen mode Exit fullscreen mode

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

package domain type TokenDetails struct { AccessToken string RefreshToken string AtExpires int64 RtExpires int64 } 
Enter fullscreen mode Exit fullscreen mode

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.

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 } 
Enter fullscreen mode Exit fullscreen mode

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

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 } 
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)