aboutsummaryrefslogblamecommitdiff
path: root/api/auth_otp.go
blob: bbf14647ab831ad916c734d2a638711c8df76531 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                     
                                                   















































































































                                                                                                                
package api

import (
	"bytes"
	"fmt"
	"image/png"

	"github.com/gin-gonic/gin"
	"github.com/pquerna/otp/totp"
	"git.immae.eu/Cryptoportfolio/Front.git/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
}