begin doing opt verify

This commit is contained in:
Trần Duy Linh 2024-01-16 17:14:47 +07:00
parent 193b955915
commit 0125a020a0
9 changed files with 162 additions and 12 deletions

3
go.mod
View File

@ -6,6 +6,7 @@ require (
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-mail/mail v2.3.1+incompatible // indirect
github.com/go-playground/locales v0.14.1 // indirect
@ -13,6 +14,8 @@ require (
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect

6
go.sum
View File

@ -6,6 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/go-mail/mail v2.3.1+incompatible h1:UzNOn0k5lpfVtO31cK3hn6I4VEVGhe3lX8AJBAxXExM=
@ -23,6 +25,10 @@ github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=

View File

@ -13,6 +13,7 @@ import (
type PreusersMongo struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
Username string `bson:"username" json:"username" `
UUID string `bson:"uuid" json:"uuid"`
Email string `bson:"email" json:"email"`
HashPassword string `bson:"hash_password" json:"hash_password"`
PhoneNumber string `bson:"phone_number" json:"phone_number"`
@ -38,6 +39,7 @@ var Validate *validator.Validate
// API Type
type CreateUser struct {
Username string `json:"username" validate:"required,min=8,max=20"`
UUID string `json:"uuid" validate:"required,uuid"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,max=20,customPassword"`
ConfirmPassword string `json:"confirm_password" validate:"required,eqfield=Password"`
@ -102,5 +104,12 @@ type RedisOTP struct {
Email string `json:"email"`
User string `json:"user"`
CreatedDate int64 `json:"created_date"`
Counter uint64 `json:"counter"`
HashOTP string `json:"hashOTP"`
}
type OTPVerify struct {
OTP string `json:"otp"`
Email string `json:"email"`
UUID string `json:"uuid"`
}

View File

@ -2,6 +2,7 @@ package rest_api
import (
"fmt"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"linhdevtran99/rest-api/rest-api/routes"
"linhdevtran99/rest-api/utils"
@ -21,8 +22,10 @@ func NewAPIServer(listenAddr string) *APIServer {
func startMuxServer(s *APIServer, router *mux.Router) {
log.Println("Listening on", s.listenAddr)
if err := http.ListenAndServe(s.listenAddr, router); err != nil {
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"https://app.wliafdew.dev"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
if err := http.ListenAndServe(s.listenAddr, handlers.CORS(originsOk, headersOk, methodsOk)(router)); err != nil {
log.Fatal(err)
}
}
@ -40,6 +43,7 @@ func (s *APIServer) Run() {
func (s *APIServer) TestRoute(w http.ResponseWriter, r *http.Request) error {
if r.Method == http.MethodGet {
//utils.VerifyOTP()
fmt.Println("hello")
}

View File

@ -43,6 +43,7 @@ func RegisterNewAccount(w http.ResponseWriter, r *http.Request) error {
Email: registerInfo.Email,
PhoneNumber: registerInfo.PhoneNumber,
HashPassword: registerInfo.Password,
UUID: registerInfo.UUID,
CreatedDate: time.Now(),
UpdateDate: time.Now(),
VerifySentCount: 1,
@ -60,12 +61,15 @@ func RegisterNewAccount(w http.ResponseWriter, r *http.Request) error {
return nil
}
func Linh(w http.ResponseWriter, r *http.Request) error {
if r.Method == http.MethodGet {
var registerInfo models.CreateUser
func VerifyWithOTP(w http.ResponseWriter, r *http.Request) error {
_ = json.NewDecoder(r.Body).Decode(&registerInfo)
fmt.Println(registerInfo)
if r.Method == http.MethodPost {
// declare variable
var otpInfo models.OTPVerify
//get data from body
_ = json.NewDecoder(r.Body).Decode(&otpInfo)
//check otp and create user
services.CheckOTPIsValid(&otpInfo, w)
}
return nil
@ -74,4 +78,5 @@ func Linh(w http.ResponseWriter, r *http.Request) error {
func AuthRouterSetup(router *mux.Router) {
authRouter := router.PathPrefix("/account").Subrouter()
authRouter.Handle("/register", utils.MakeHTTPHandlerFn(RegisterNewAccount)).Methods("POST")
authRouter.Handle("/register/verifyAccountOTP", utils.MakeHTTPHandlerFn(VerifyWithOTP)).Methods("POST")
}

View File

@ -8,6 +8,7 @@ import (
"fmt"
"github.com/go-mail/mail"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"golang.org/x/crypto/bcrypt"
"linhdevtran99/rest-api/models"
@ -120,10 +121,10 @@ func CheckAccountValid(userName string, email string) (bool, *ResponseError) {
const (
otpDigits = 6
mailValidTime = time.Hour * 24
mailValidTime = time.Minute * 15
)
func GenerateVerifyAccount(registerInfo *models.PreusersMongo, w http.ResponseWriter) {
func GenerateVerifyAccount(registerInfo *models.PreusersMongo, w http.ResponseWriter) error {
var wg sync.WaitGroup
otpChannel := make(chan models.OtpGenerate, 1)
@ -135,9 +136,11 @@ func GenerateVerifyAccount(registerInfo *models.PreusersMongo, w http.ResponseWr
wg.Add(1)
go utils.EncryptAESMailLink(registerInfo, w, mailChannel, &wg)
go createMailVerify(registerInfo, otpChannel, mailChannel, w)
go writeOTPInRedis(registerInfo, otpChannel, w)
go writeOTPInRedis(counter, registerInfo, otpChannel, w)
wg.Wait()
err := utils.WriteJSON(w, http.StatusOK, "Success register account please verify your account")
return err
}
@ -223,7 +226,7 @@ func CheckAndWritePreuser(registerInfo *models.PreusersMongo, w http.ResponseWri
//write otp in redis for valid if otp expired
func writeOTPInRedis(registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, w http.ResponseWriter) {
func writeOTPInRedis(counter uint64, registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, w http.ResponseWriter) {
otp := <-otpChan
@ -232,6 +235,7 @@ func writeOTPInRedis(registerInfo *models.PreusersMongo, otpChan chan models.Otp
User: registerInfo.Username,
CreatedDate: registerInfo.CreatedDate.Unix(),
HashOTP: otp.HashOTP,
Counter: counter,
}
jsonData, err := json.Marshal(dataRedis)
@ -244,3 +248,96 @@ func writeOTPInRedis(registerInfo *models.PreusersMongo, otpChan chan models.Otp
fmt.Println(status.Err())
}
// ////////////////////////Create User Complete//////////////////////////
func CreateUserAfterVerify(otpInfo *models.OTPVerify, redis *models.RedisOTP, w http.ResponseWriter) bool {
isValid := true
var preUserData models.PreusersMongo
filter := bson.D{{"email", otpInfo.Email}}
err := utils.PreUserData.FindOne(context.Background(), filter).Decode(&preUserData)
if err != nil {
fmt.Println("Internal log: Can't get preuser data")
_ = utils.WriteJSONInternalError(w, "Please register again")
isValid = false
}
user := models.UsersMongo{
Username: preUserData.Username,
Email: preUserData.Email,
PhoneNumber: preUserData.PhoneNumber,
HashPassword: preUserData.HashPassword,
Active: true,
CreatedDate: time.Now(),
UpdateDate: time.Now(),
VerifySentCount: int(redis.Counter),
}
_, err = utils.User.InsertOne(context.Background(), user)
if err != nil {
fmt.Println("Internal log: Can't insert user data")
_ = utils.WriteJSONInternalError(w, "Can't insert user data")
isValid = false
}
if isValid == true {
fmt.Println("Internal log: Success verify account")
_ = utils.WriteJSON(w, http.StatusOK, "Success verify account")
}
return isValid
}
//////////////////////////Verify OTP//////////////////////////
func CheckUserVerify(otpInfo *models.OTPVerify, w http.ResponseWriter) bool {
isVerified := false
filter := bson.D{{"email", otpInfo.Email}}
var user models.UsersMongo
_ = utils.User.FindOne(context.Background(), filter).Decode(&user)
if user.Active == true {
fmt.Println("Internal log: Account already verify")
_ = utils.WriteJSON(w, http.StatusBadRequest, "Account already verify")
isVerified = true
}
return isVerified
}
func CheckOTPIsValid(otpInfo *models.OTPVerify, w http.ResponseWriter) bool {
var isValid bool
isValid = true
//get data from redis
var dataRedis models.RedisOTP
//verify uuid and email
value, err := utils.Redis.Get(context.Background(), "otp:"+otpInfo.Email).Result()
if err != nil {
fmt.Println("Internal log: Email not valid")
_ = utils.WriteJSON(w, http.StatusBadRequest, "Email is not valid")
isValid = false
return false
}
_ = json.Unmarshal([]byte(value), &dataRedis)
_, err = uuid.Parse(otpInfo.UUID)
if err != nil {
fmt.Println("Internal log: UUID not valid")
_ = utils.WriteJSON(w, http.StatusBadRequest, "UUID not valid")
isValid = false
}
if isVerified := CheckUserVerify(otpInfo, w); isVerified == true {
return false
}
isValid = utils.VerifyOTP(otpInfo, dataRedis, w)
if isValid == true {
CreateUserAfterVerify(otpInfo, &dataRedis, w)
}
return true
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -81,9 +81,10 @@ func EncryptAESMailLink(registerInfo *models.PreusersMongo, w http.ResponseWrite
//OTP+++++++++++++OTP//
// Gen Otp and hash otp
func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w http.ResponseWriter, otpChan chan models.OtpGenerate, wg *sync.WaitGroup) chan models.OtpGenerate {
serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + registerInfo.Username + registerInfo.Email))
serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + registerInfo.UUID + registerInfo.Email))
passCode, err := hotp.GenerateCodeCustom(serectBase32, counter, hotp.ValidateOpts{
Digits: otp.Digits(otpDigits),
Algorithm: otp.AlgorithmSHA256,
@ -111,3 +112,27 @@ func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w
return otpChan
}
//Decrypt OTP and verify otp
//task
//decrypt hack otp in redis //testing current
//verify otp //testing current
//write preuser to real user db
//reposne and countinue
func VerifyOTP(otpInfo *models.OTPVerify, redisOTP models.RedisOTP, w http.ResponseWriter) bool {
serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + otpInfo.UUID + otpInfo.Email))
isOTPMatch, _ := hotp.ValidateCustom(otpInfo.OTP, redisOTP.Counter, serectBase32, hotp.ValidateOpts{
Digits: otp.Digits(6),
Algorithm: otp.AlgorithmSHA256,
})
if isOTPMatch != true {
fmt.Println("Internal log: OTP is not match")
_ = WriteJSON(w, http.StatusBadRequest, "OTP is not match")
}
fmt.Println("isOTPMatch: ", isOTPMatch)
return isOTPMatch
}

View File

@ -8,6 +8,7 @@ import (
func WriteJSON(w http.ResponseWriter, status int, v any) error {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(status)
err := json.NewEncoder(w).Encode(v)
if err != nil {