198 lines
5.6 KiB
Go
198 lines
5.6 KiB
Go
package utils
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base32"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/pquerna/otp"
|
|
"github.com/pquerna/otp/hotp"
|
|
"github.com/skip2/go-qrcode"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"linhdevtran99/rest-api/models"
|
|
"net/http"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
//const
|
|
|
|
var serect = os.Getenv("EMAIL_VERIFY_SECRET")
|
|
|
|
//Mail
|
|
|
|
type MyCustomClaims struct {
|
|
Email string `json:"email"`
|
|
jwt.RegisteredClaims
|
|
}
|
|
|
|
func buildTokenLink(registerInfo *models.PreusersMongo, token string, w http.ResponseWriter) (string, string) {
|
|
emailVerifyLink := "http://localhost:4200/register?uuid=" + registerInfo.UUID + "®ister_step=verify_with_jwt" + "&p=" + token
|
|
png, err := qrcode.Encode(emailVerifyLink, qrcode.Low, 200)
|
|
if err != nil {
|
|
fmt.Println("Internal log: error create qr ")
|
|
_ = WriteJSONInternalError(w, "error create QR code")
|
|
}
|
|
base64Image := base64.StdEncoding.EncodeToString(png)
|
|
|
|
return emailVerifyLink, base64Image
|
|
}
|
|
|
|
func EncryptAESMailLink(registerInfo *models.PreusersMongo, w http.ResponseWriter, mailChan chan models.MailVefiry, wg *sync.WaitGroup, timeCreate time.Time) chan models.MailVefiry {
|
|
claims := MyCustomClaims{
|
|
registerInfo.Email,
|
|
jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Hour)),
|
|
IssuedAt: jwt.NewNumericDate(timeCreate),
|
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
Issuer: "totodayShopRegister",
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
token.Header["purpose"] = "Email_Verify"
|
|
|
|
tokenString, err := token.SignedString([]byte(serect + registerInfo.UUID))
|
|
if err != nil {
|
|
fmt.Println("Internal Log: Error signed token fail", err.Error())
|
|
_ = WriteJSONInternalError(w, "Error signing token fail")
|
|
}
|
|
tokenBS64 := base64.StdEncoding.EncodeToString([]byte(tokenString))
|
|
|
|
linkMail, imageB64 := buildTokenLink(registerInfo, tokenBS64, w)
|
|
|
|
data := models.MailVefiry{
|
|
LinkMail: linkMail,
|
|
ImageBase64: imageB64,
|
|
}
|
|
|
|
mailChan <- data
|
|
|
|
wg.Done()
|
|
return mailChan
|
|
|
|
}
|
|
|
|
func DecryptAESMailLink(linkVerifyInfo *models.LinkVerify, w http.ResponseWriter) (bool, string) {
|
|
decodeBase64, err := base64.StdEncoding.DecodeString(linkVerifyInfo.JWT)
|
|
token, err := jwt.ParseWithClaims(string(decodeBase64), &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(serect + linkVerifyInfo.UUID), nil
|
|
})
|
|
if err != nil {
|
|
fmt.Println("Internal log: Error decrypt token fail", err.Error())
|
|
if errors.Is(err, jwt.ErrTokenMalformed) {
|
|
_ = WriteJSON(w, http.StatusBadRequest, models.ErrorAPI{
|
|
Errors: []string{"Token is malformed"},
|
|
Message: "Token is invalid",
|
|
Type: "TokenMalformed",
|
|
})
|
|
}
|
|
if errors.Is(err, jwt.ErrSignatureInvalid) {
|
|
_ = WriteJSON(w, http.StatusBadRequest, models.ErrorAPI{
|
|
Errors: []string{"UUID is invalid"},
|
|
Message: "UUID is invalid",
|
|
Type: "UUIDInvalid",
|
|
})
|
|
}
|
|
|
|
if errors.Is(err, jwt.ErrTokenExpired) {
|
|
_ = WriteJSON(w, http.StatusBadRequest, models.ErrorAPI{
|
|
Errors: []string{"Token is expired"},
|
|
Message: "Link is expired please register again",
|
|
Type: "TokenExpired",
|
|
})
|
|
}
|
|
|
|
return false, ""
|
|
}
|
|
|
|
if token.Valid && token.Header["purpose"] == "Email_Verify" && err == nil {
|
|
//get update date from preuser
|
|
var preUser models.PreusersMongo
|
|
email := token.Claims.(*MyCustomClaims).Email
|
|
err := PreUserData.FindOne(context.TODO(), bson.M{"email": email}).Decode(&preUser)
|
|
if err != nil {
|
|
//handler erorr later
|
|
fmt.Println("Internal log: Error find preuser fail", err.Error())
|
|
return false, ""
|
|
}
|
|
timeRegisterToken := preUser.UpdateDate.Unix()
|
|
timeIssueToken, _ := token.Claims.GetIssuedAt()
|
|
if timeRegisterToken != timeIssueToken.Unix() {
|
|
//handler erorr later
|
|
fmt.Println("Internal log: Error token is not valid to use")
|
|
return false, ""
|
|
}
|
|
return true, email
|
|
}
|
|
|
|
return false, ""
|
|
|
|
}
|
|
|
|
//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.UUID + registerInfo.Email))
|
|
passCode, err := hotp.GenerateCodeCustom(serectBase32, counter, hotp.ValidateOpts{
|
|
Digits: otp.Digits(otpDigits),
|
|
Algorithm: otp.AlgorithmSHA256,
|
|
})
|
|
|
|
if err != nil {
|
|
fmt.Println("Internal log: Fail to create OTP")
|
|
_ = WriteJSONInternalError(w, "Fail to create OTP")
|
|
}
|
|
|
|
hashed, err := bcrypt.GenerateFromPassword([]byte(passCode), 5)
|
|
if err != nil {
|
|
fmt.Println("Internal log: Fail to encrypt OTP")
|
|
_ = WriteJSONInternalError(w, "Fail to encrypt OTP")
|
|
}
|
|
|
|
data := models.OtpGenerate{
|
|
PureOTP: passCode,
|
|
HashOTP: string(hashed),
|
|
}
|
|
|
|
otpChan <- data
|
|
otpChan <- data
|
|
wg.Done()
|
|
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, models.ErrorAPI{
|
|
Errors: []string{"OTP is not match"},
|
|
Message: "OTP is not match",
|
|
Type: "OTP",
|
|
})
|
|
}
|
|
fmt.Println("isOTPMatch: ", isOTPMatch)
|
|
return isOTPMatch
|
|
}
|