8 "github.com/gin-gonic/gin"
9 "github.com/pquerna/otp/totp"
10 "git.immae.eu/Cryptoportfolio/Front.git/db"
13 func GenerateSecret(user db.User) (bytes.Buffer, string, error) {
15 key, err := totp.Generate(totp.GenerateOpts{
16 Issuer: "cryptoportfolio",
17 AccountName: user.Email,
25 qrImage, err := key.Image(200, 200)
30 png.Encode(&buf, qrImage)
35 return buf, key.Secret(), nil
38 type OtpEnrollmentQuery struct {
44 func (q OtpEnrollmentQuery) ValidateParams() *Error {
48 func (q OtpEnrollmentQuery) Run() (*bytes.Buffer, string, *Error) {
49 if q.In.User.OtpSecret != "" && q.In.User.IsOtpSetup == true {
50 return nil, "", &Error{OtpAlreadySetup, "otp is already setup", fmt.Errorf("otp already setup")}
53 buf, secret, err := GenerateSecret(q.In.User)
55 return nil, "", NewInternalError(err)
58 err = db.SetOtpSecret(&q.In.User, secret, true)
60 return nil, "", NewInternalError(err)
63 return &buf, secret, nil
66 type OtpValidateQuery struct {
74 Token string `json:"token"`
78 func (q OtpValidateQuery) ValidateParams() *Error {
80 return &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)}
86 func (q OtpValidateQuery) Run() (interface{}, *Error) {
89 if q.In.User.OtpSecret == "" {
90 return nil, &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")}
93 if !totp.Validate(q.In.Pass, q.In.User.OtpSecret) {
94 return nil, &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)}
96 } else if err := db.SetOtpSecret(&q.In.User, q.In.User.OtpSecret, false); err != nil {
97 return nil, NewInternalError(err)
100 q.In.Claims.Authorized = true
101 q.Out.Token, err = SignJwt(q.In.Claims)
103 return nil, NewInternalError(err)
109 func OtpAuth(c *gin.Context) *Error {
110 claims := GetClaims(c)
113 if user.IsOtpSetup == false || user.OtpSecret == "" {
114 return &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")}
117 if !claims.Authorized {
118 return &Error{NeedOtpValidation, "not authorized", fmt.Errorf("otp not authorized")}