From 4d1884f3a46dac50ed9d98e61c90ecc910321aaf Mon Sep 17 00:00:00 2001 From: Nilo Roberto C Paim Date: Tue, 15 Aug 2023 14:48:55 -0300 Subject: [PATCH] initial auth route --- database/database.go | 9 +++--- go.mod | 4 ++- go.sum | 4 +++ models/users.go | 9 ++++++ routes/login_route.go | 70 ++++++++++++++++++++++++++++++++++++++++ routes/setup_routes.go | 2 ++ utils/utils.go | 73 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 models/users.go create mode 100644 routes/login_route.go create mode 100644 utils/utils.go diff --git a/database/database.go b/database/database.go index 85f0f74..cd2b3da 100644 --- a/database/database.go +++ b/database/database.go @@ -2,7 +2,8 @@ package database import ( "api/globals" - // "api/models" + "api/models" + // "api/utils" "fmt" "log" @@ -42,9 +43,9 @@ func ConnectDB() error { log.Println("Migrating database tables") - // if result := db.AutoMigrate(&models.User{}); result != nil { - // return result - // } + if result := db.AutoMigrate(&models.User{}); result != nil { + return result + } // if result := db.AutoMigrate(&models.Event{}); result != nil { // return result diff --git a/go.mod b/go.mod index fb28b70..7e08205 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,11 @@ module api go 1.20 require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gofiber/fiber/v2 v2.48.0 + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/joho/godotenv v1.5.1 + golang.org/x/crypto v0.8.0 gorm.io/driver/postgres v1.5.2 gorm.io/gorm v1.25.3 ) @@ -25,7 +28,6 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.48.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/crypto v0.8.0 // indirect golang.org/x/sys v0.10.0 // indirect golang.org/x/text v0.9.0 // indirect ) diff --git a/go.sum b/go.sum index 74ecfcc..e80668b 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,12 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0= github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= diff --git a/models/users.go b/models/users.go new file mode 100644 index 0000000..24adc14 --- /dev/null +++ b/models/users.go @@ -0,0 +1,9 @@ +package models + +type User struct { + Id uint `gorm:"primary key" json:"id"` + Name string `gorm:"size:40;not null" json:"name"` + Email string `gorm:"size:40;not null;unique" json:"email"` + Password []byte `gorm:"size:100;not null;" json:"-"` + UserType string `gorm:"size:1;not null;default:U" json:"usertype"` +} diff --git a/routes/login_route.go b/routes/login_route.go new file mode 100644 index 0000000..7a1c75d --- /dev/null +++ b/routes/login_route.go @@ -0,0 +1,70 @@ +package routes + +import ( + "api/models" + "log" + "os" + "strconv" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt/v4" + "golang.org/x/crypto/bcrypt" +) + +func Login(c *fiber.Ctx) error { + + var data map[string]string + + if err := c.BodyParser(&data); err != nil { + log.Printf("Bad Request on parse: %v\n", err) + return fiber.ErrBadRequest + } + + var user models.User + + if user.Id == 0 { + return c.JSON(fiber.Map{ + "message": "Usuário não encontrado", + "userId": 0, + "userType": "", + "userName": "", + "token": ""}) + } + + if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil { + return c.JSON(fiber.Map{ + "message": "Senha inválida", + "userId": 0, + "userType": "", + "userName": "", + "token": ""}) + } + + type customClaims struct { + Userid string `json:"user"` + jwt.StandardClaims + } + + tok := customClaims{ + Userid: strconv.Itoa(int(user.Id)), + StandardClaims: jwt.StandardClaims{ + Issuer: strconv.Itoa(int(user.Id)), + ExpiresAt: time.Now().Add(time.Hour * 1).Unix(), + }, + } + + claims := jwt.NewWithClaims(jwt.SigningMethodHS256, tok) + + token, err := claims.SignedString([]byte(os.Getenv("API_SECRET"))) + if err != nil { + return fiber.ErrInternalServerError + } + + return c.JSON(fiber.Map{ + "message": "", + "userId": user.Id, + "userType": user.UserType, + "userName": user.Name, + "token": token}) +} diff --git a/routes/setup_routes.go b/routes/setup_routes.go index 07a3fad..0077207 100644 --- a/routes/setup_routes.go +++ b/routes/setup_routes.go @@ -6,4 +6,6 @@ func Setup(app *fiber.App) { app.Get("/", GetVersion) + app.Post("/login", Login) + } diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..e6f0f54 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,73 @@ +package utils + +import ( + "encoding/json" + "fmt" + "log" + "os" + "strings" + + "github.com/dgrijalva/jwt-go" + "github.com/gofiber/fiber/v2" + "golang.org/x/crypto/bcrypt" +) + +func HashPassword(password string) ([]byte, error) { + return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) +} + +func VerifyAuthentication(c *fiber.Ctx, cookie string) (*jwt.StandardClaims, error) { + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(os.Getenv("API_SECRET")), nil + }) + + if err != nil { + return nil, err + } + + claims := token.Claims.(*jwt.StandardClaims) + + return claims, nil +} + +func ProcessToken(c *fiber.Ctx) (interface{}, error) { + bearToken := c.Get("Authorization") + + var token string + + // Normally Authorization HTTP header. + onlyToken := strings.Split(bearToken, " ") + if len(onlyToken) == 2 { + token = onlyToken[1] + } else { + token = bearToken + } + + tk, err := jwt.Parse(token, jwtKeyFunc) + + if err != nil { + fmt.Println("Error 1") + return nil, err + } + + claims, ok := tk.Claims.(jwt.MapClaims) + + if ok && tk.Valid { + return claims["user"], nil + } + + return nil, err +} + +func jwtKeyFunc(token *jwt.Token) (interface{}, error) { + return []byte(os.Getenv("API_SECRET")), nil +} + +func PrettyPrintJson(data interface{}) { + b, err := json.MarshalIndent(data, "", " ") + if err != nil { + fmt.Println("error:", err) + } + log.Println(string(b)) +}