package api import ( "bytes" "fmt" "image/png" "github.com/gin-gonic/gin" "github.com/pquerna/otp/totp" "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db" ) func GenerateSecret(user db.User) (bytes.Buffer, string, error) { var buf bytes.Buffer key, err := totp.Generate(totp.GenerateOpts{ Issuer: "cryptoportfolio", AccountName: user.Email, Period: 30, }) if err != nil { return buf, "", err } qrImage, err := key.Image(200, 200) if err != nil { return buf, "", err } png.Encode(&buf, qrImage) if err != nil { return buf, "", err } return buf, key.Secret(), nil } type OtpEnrollmentQuery struct { In struct { User db.User } } func (q OtpEnrollmentQuery) ValidateParams() *Error { return nil } func (q OtpEnrollmentQuery) Run() (*bytes.Buffer, string, *Error) { if q.In.User.OtpSecret != "" && q.In.User.IsOtpSetup == true { return nil, "", &Error{OtpAlreadySetup, "otp is already setup", fmt.Errorf("otp already setup")} } buf, secret, err := GenerateSecret(q.In.User) if err != nil { return nil, "", NewInternalError(err) } err = db.SetOtpSecret(&q.In.User, secret, true) if err != nil { return nil, "", NewInternalError(err) } return &buf, secret, nil } type OtpValidateQuery struct { In struct { Pass string User db.User Claims JwtClaims } Out struct { Token string `json:"token"` } } func (q OtpValidateQuery) ValidateParams() *Error { if q.In.Pass == "" { return &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)} } return nil } func (q OtpValidateQuery) Run() (interface{}, *Error) { var err error if q.In.User.OtpSecret == "" { return nil, &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")} } if !totp.Validate(q.In.Pass, q.In.User.OtpSecret) { return nil, &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)} } else if err := db.SetOtpSecret(&q.In.User, q.In.User.OtpSecret, false); err != nil { return nil, NewInternalError(err) } q.In.Claims.Authorized = true q.Out.Token, err = SignJwt(q.In.Claims) if err != nil { return nil, NewInternalError(err) } return q.Out, nil } func OtpAuth(c *gin.Context) *Error { claims := GetClaims(c) user := GetUser(c) if user.IsOtpSetup == false || user.OtpSecret == "" { return &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")} } if !claims.Authorized { return &Error{NeedOtpValidation, "not authorized", fmt.Errorf("otp not authorized")} } return nil }