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)