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
}