From 0125a020a0866756b8ea43ba8a00e074947863a5 Mon Sep 17 00:00:00 2001 From: linhdevtran99 Date: Tue, 16 Jan 2024 17:14:47 +0700 Subject: [PATCH] begin doing opt verify --- go.mod | 3 + go.sum | 6 + models/models.go | 9 ++ rest-api/init-api.go | 8 +- rest-api/routes/auth.go | 15 ++- rest-api/services/register.go | 105 +++++++++++++++++- ...inhdevtran99@wliafdew.methewind1113212.png | Bin 1117 -> 0 bytes utils/crypto.go | 27 ++++- utils/restapiType.go | 1 + 9 files changed, 162 insertions(+), 12 deletions(-) delete mode 100644 temp/linhdevtran99@wliafdew.methewind1113212.png diff --git a/go.mod b/go.mod index a6061ba..69e91ef 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index b55bb56..025b052 100644 --- a/go.sum +++ b/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= diff --git a/models/models.go b/models/models.go index 2290f0c..831673a 100644 --- a/models/models.go +++ b/models/models.go @@ -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"` +} diff --git a/rest-api/init-api.go b/rest-api/init-api.go index 66ca3e2..a1593f6 100644 --- a/rest-api/init-api.go +++ b/rest-api/init-api.go @@ -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") } diff --git a/rest-api/routes/auth.go b/rest-api/routes/auth.go index 21b6e5e..7544898 100644 --- a/rest-api/routes/auth.go +++ b/rest-api/routes/auth.go @@ -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") } diff --git a/rest-api/services/register.go b/rest-api/services/register.go index 32a7f41..361eda2 100644 --- a/rest-api/services/register.go +++ b/rest-api/services/register.go @@ -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 +} diff --git a/temp/linhdevtran99@wliafdew.methewind1113212.png b/temp/linhdevtran99@wliafdew.methewind1113212.png deleted file mode 100644 index bd21c0479e73da2369916dc4b8d3fdc90c4fa5c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1117 zcmV-j1fu(iP)ct@FN6KGg$2{1bT(<>y+l|m2A?6Ym04z{Ysi9 z>SkX-pyIYRY0iMqfAQRWVT;{?XxgasudtDh|L2JQtj?tY*~l5XQXYLoSY4gLoidQJ z8$k1Gv|IG@0N0URulPIZ~u z&OL}+4uEch&tg^dU%T@^MFVC^uB@MvnOTY3DEqa%zSYERV2SH z8uu#LWl{;dXS-9 zGIO1q>z0h>KsOc3_TtNOBbo!hD>~sk0IXMUG@?BK^RwPMMBq{fm(28Cy`|WySymA{ zg}gEXsw_lu+sVv`^=al_A-21V1>dRC-(2+(?T>}J1#)AbMRRu5(M&-7dUL&^E@eyc zOngHk;r#kay&`~eL?svJ5TdUFsq7Q?O3E}+q~S_ve<#}n?7tp=R*ys&fn-GVM4s(| zSQZ^SMaK&bp0a2vlaFYFlRp~0DR&|Q5w5v07NJhTpb&qJy2mp~!b=oP5h05|s z*d77u6crZo*avqeg~iq@_34om9in5-A}J$S)tgRs zC6~W;i-`BKCC;Sye2N26)<^Di$l%EcMkc{BG?#Cqvfo6n5Lp@C-V-el?V`Et19i%v zvfZKrSe4yJ+XS1hkFV~Kv&S{(WOE*fUN)V|KDtgB=psXMb`jAogTqRyehJ(w?q1JI zq^q}EBbvfL6H1S0zGQu7Z*zIn(e9e%PVdX$tZfQ%dj-NyC4^M)m^wQ|M3y0;G{r{0 z_^J4MHQ_QP{}IazCf19w84LQDssn?Jren zW?A1Vq(?O8va5tGIvJX`VNqs$OprYSRYwIZR|SC2OaxZfDMVx@raG~r>G>_MtK6wm zG+=j95>c6(h-M^LzZ~2t11Xx5Gjvtw9gv2Bstoac@pmI)GJ-w!t0NH@Sm^^YM>>0B z9!0`Ru3&NfVw)#3d~&vt&lA$w;(CRY4UzNk$r}>LdSm=pdgVNY5K~z`5f*>mWs-Rk j+$n$7|6l(J00960Ymmmk*3k1I00000NkvXXu0mjfrH&L{ diff --git a/utils/crypto.go b/utils/crypto.go index 5ed6703..17cb1f0 100644 --- a/utils/crypto.go +++ b/utils/crypto.go @@ -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 +} diff --git a/utils/restapiType.go b/utils/restapiType.go index bd54d39..dad1d7e 100644 --- a/utils/restapiType.go +++ b/utils/restapiType.go @@ -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 {