aboutsummaryrefslogtreecommitdiff
path: root/api/auth_otp.go
diff options
context:
space:
mode:
Diffstat (limited to 'api/auth_otp.go')
-rw-r--r--api/auth_otp.go122
1 files changed, 122 insertions, 0 deletions
diff --git a/api/auth_otp.go b/api/auth_otp.go
new file mode 100644
index 0000000..de1cf24
--- /dev/null
+++ b/api/auth_otp.go
@@ -0,0 +1,122 @@
1package api
2
3import (
4 "bytes"
5 "fmt"
6 "image/png"
7
8 "github.com/gin-gonic/gin"
9 "github.com/pquerna/otp/totp"
10 "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db"
11)
12
13func GenerateSecret(user db.User) (bytes.Buffer, string, error) {
14 var buf bytes.Buffer
15 key, err := totp.Generate(totp.GenerateOpts{
16 Issuer: "cryptoportfolio",
17 AccountName: user.Email,
18 Period: 30,
19 })
20
21 if err != nil {
22 return buf, "", err
23 }
24
25 qrImage, err := key.Image(200, 200)
26 if err != nil {
27 return buf, "", err
28 }
29
30 png.Encode(&buf, qrImage)
31 if err != nil {
32 return buf, "", err
33 }
34
35 return buf, key.Secret(), nil
36}
37
38type OtpEnrollmentQuery struct {
39 In struct {
40 User db.User
41 }
42}
43
44func (q OtpEnrollmentQuery) ValidateParams() *Error {
45 return nil
46}
47
48func (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")}
51 }
52
53 buf, secret, err := GenerateSecret(q.In.User)
54 if err != nil {
55 return nil, "", NewInternalError(err)
56 }
57
58 err = db.SetOtpSecret(&q.In.User, secret, true)
59 if err != nil {
60 return nil, "", NewInternalError(err)
61 }
62
63 return &buf, secret, nil
64}
65
66type OtpValidateQuery struct {
67 In struct {
68 Pass string
69 User db.User
70 Claims JwtClaims
71 }
72
73 Out struct {
74 Token string `json:"token"`
75 }
76}
77
78func (q OtpValidateQuery) ValidateParams() *Error {
79 if q.In.Pass == "" {
80 return &Error{InvalidOtp, "invalid otp", fmt.Errorf("invalid otp '%v'", q.In.Pass)}
81 }
82
83 return nil
84}
85
86func (q OtpValidateQuery) Run() (interface{}, *Error) {
87 var err error
88
89 if q.In.User.OtpSecret == "" {
90 return nil, &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")}
91 }
92
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)}
95
96 } else if err := db.SetOtpSecret(&q.In.User, q.In.User.OtpSecret, false); err != nil {
97 return nil, NewInternalError(err)
98 }
99
100 q.In.Claims.Authorized = true
101 q.Out.Token, err = SignJwt(q.In.Claims)
102 if err != nil {
103 return nil, NewInternalError(err)
104 }
105
106 return q.Out, nil
107}
108
109func OtpAuth(c *gin.Context) *Error {
110 claims := GetClaims(c)
111 user := GetUser(c)
112
113 if user.IsOtpSetup == false || user.OtpSecret == "" {
114 return &Error{OtpNotSetup, "otp is not setup", fmt.Errorf("otp is not setup")}
115 }
116
117 if !claims.Authorized {
118 return &Error{NeedOtpValidation, "not authorized", fmt.Errorf("otp not authorized")}
119 }
120
121 return nil
122}