begin doing opt verify
This commit is contained in:
parent
193b955915
commit
0125a020a0
3
go.mod
3
go.mod
|
|
@ -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
6
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(®isterInfo)
|
||||
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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue