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 thedomain
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)
}
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
}
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
}
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
}
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)