aboutsummaryrefslogtreecommitdiff
path: root/api/auth_otp.go
blob: de1cf2406a37dc617ca2c51d94e7ca2730175010 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
}