88 "fmt"
99 "github.com/0xJacky/Nginx-UI/api"
1010 "github.com/0xJacky/Nginx-UI/internal/crypto"
11+ "github.com/0xJacky/Nginx-UI/internal/user"
1112 "github.com/0xJacky/Nginx-UI/query"
1213 "github.com/0xJacky/Nginx-UI/settings"
1314 "github.com/gin-gonic/gin"
@@ -67,8 +68,8 @@ func GenerateTOTP(c *gin.Context) {
6768}
6869
6970func EnrollTOTP (c * gin.Context ) {
70- user := api .CurrentUser (c )
71- if len ( user . OTPSecret ) > 0 {
71+ cUser := api .CurrentUser (c )
72+ if cUser . EnabledOTP () {
7273 c .JSON (http .StatusBadRequest , gin.H {
7374 "message" : "User already enrolled" ,
7475 })
@@ -109,7 +110,7 @@ func EnrollTOTP(c *gin.Context) {
109110 }
110111
111112 u := query .Auth
112- _ , err = u .Where (u .ID .Eq (user .ID )).Update (u .OTPSecret , ciphertext )
113+ _ , err = u .Where (u .ID .Eq (cUser .ID )).Update (u .OTPSecret , ciphertext )
113114 if err != nil {
114115 api .ErrHandler (c , err )
115116 return
@@ -135,8 +136,8 @@ func ResetOTP(c *gin.Context) {
135136 api .ErrHandler (c , err )
136137 return
137138 }
138- user := api .CurrentUser (c )
139- k := sha1 .Sum (user .OTPSecret )
139+ cUser := api .CurrentUser (c )
140+ k := sha1 .Sum (cUser .OTPSecret )
140141 if ! bytes .Equal (k [:], recoverCode ) {
141142 c .JSON (http .StatusBadRequest , gin.H {
142143 "message" : "Invalid recovery code" ,
@@ -145,7 +146,7 @@ func ResetOTP(c *gin.Context) {
145146 }
146147
147148 u := query .Auth
148- _ , err = u .Where (u .ID .Eq (user .ID )).UpdateSimple (u .OTPSecret .Null ())
149+ _ , err = u .Where (u .ID .Eq (cUser .ID )).UpdateSimple (u .OTPSecret .Null ())
149150 if err != nil {
150151 api .ErrHandler (c , err )
151152 return
@@ -161,3 +162,40 @@ func OTPStatus(c *gin.Context) {
161162 "status" : len (api .CurrentUser (c ).OTPSecret ) > 0 ,
162163 })
163164}
165+
166+ func StartSecure2FASession (c * gin.Context ) {
167+ var json struct {
168+ OTP string `json:"otp"`
169+ RecoveryCode string `json:"recovery_code"`
170+ }
171+ if ! api .BindAndValid (c , & json ) {
172+ return
173+ }
174+ u := api .CurrentUser (c )
175+ if ! u .EnabledOTP () {
176+ c .JSON (http .StatusBadRequest , gin.H {
177+ "message" : "User not configured with 2FA" ,
178+ })
179+ return
180+ }
181+
182+ if json .OTP == "" && json .RecoveryCode == "" {
183+ c .JSON (http .StatusBadRequest , LoginResponse {
184+ Message : "The user has enabled 2FA" ,
185+ })
186+ return
187+ }
188+
189+ if err := user .VerifyOTP (u , json .OTP , json .RecoveryCode ); err != nil {
190+ c .JSON (http .StatusBadRequest , LoginResponse {
191+ Message : "Invalid 2FA or recovery code" ,
192+ })
193+ return
194+ }
195+
196+ sessionId := user .SetSecureSessionID (u .ID )
197+
198+ c .JSON (http .StatusOK , gin.H {
199+ "session_id" : sessionId ,
200+ })
201+ }
0 commit comments