code.gitea.io/gitea@v1.21.7/routers/web/auth/2fa.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package auth 5 6 import ( 7 "errors" 8 "net/http" 9 10 "code.gitea.io/gitea/models/auth" 11 user_model "code.gitea.io/gitea/models/user" 12 "code.gitea.io/gitea/modules/base" 13 "code.gitea.io/gitea/modules/context" 14 "code.gitea.io/gitea/modules/setting" 15 "code.gitea.io/gitea/modules/web" 16 "code.gitea.io/gitea/services/externalaccount" 17 "code.gitea.io/gitea/services/forms" 18 ) 19 20 var ( 21 tplTwofa base.TplName = "user/auth/twofa" 22 tplTwofaScratch base.TplName = "user/auth/twofa_scratch" 23 ) 24 25 // TwoFactor shows the user a two-factor authentication page. 26 func TwoFactor(ctx *context.Context) { 27 ctx.Data["Title"] = ctx.Tr("twofa") 28 29 // Check auto-login. 30 if checkAutoLogin(ctx) { 31 return 32 } 33 34 // Ensure user is in a 2FA session. 35 if ctx.Session.Get("twofaUid") == nil { 36 ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) 37 return 38 } 39 40 ctx.HTML(http.StatusOK, tplTwofa) 41 } 42 43 // TwoFactorPost validates a user's two-factor authentication token. 44 func TwoFactorPost(ctx *context.Context) { 45 form := web.GetForm(ctx).(*forms.TwoFactorAuthForm) 46 ctx.Data["Title"] = ctx.Tr("twofa") 47 48 // Ensure user is in a 2FA session. 49 idSess := ctx.Session.Get("twofaUid") 50 if idSess == nil { 51 ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) 52 return 53 } 54 55 id := idSess.(int64) 56 twofa, err := auth.GetTwoFactorByUID(ctx, id) 57 if err != nil { 58 ctx.ServerError("UserSignIn", err) 59 return 60 } 61 62 // Validate the passcode with the stored TOTP secret. 63 ok, err := twofa.ValidateTOTP(form.Passcode) 64 if err != nil { 65 ctx.ServerError("UserSignIn", err) 66 return 67 } 68 69 if ok && twofa.LastUsedPasscode != form.Passcode { 70 remember := ctx.Session.Get("twofaRemember").(bool) 71 u, err := user_model.GetUserByID(ctx, id) 72 if err != nil { 73 ctx.ServerError("UserSignIn", err) 74 return 75 } 76 77 if ctx.Session.Get("linkAccount") != nil { 78 err = externalaccount.LinkAccountFromStore(ctx, ctx.Session, u) 79 if err != nil { 80 ctx.ServerError("UserSignIn", err) 81 return 82 } 83 } 84 85 twofa.LastUsedPasscode = form.Passcode 86 if err = auth.UpdateTwoFactor(ctx, twofa); err != nil { 87 ctx.ServerError("UserSignIn", err) 88 return 89 } 90 91 handleSignIn(ctx, u, remember) 92 return 93 } 94 95 ctx.RenderWithErr(ctx.Tr("auth.twofa_passcode_incorrect"), tplTwofa, forms.TwoFactorAuthForm{}) 96 } 97 98 // TwoFactorScratch shows the scratch code form for two-factor authentication. 99 func TwoFactorScratch(ctx *context.Context) { 100 ctx.Data["Title"] = ctx.Tr("twofa_scratch") 101 102 // Check auto-login. 103 if checkAutoLogin(ctx) { 104 return 105 } 106 107 // Ensure user is in a 2FA session. 108 if ctx.Session.Get("twofaUid") == nil { 109 ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) 110 return 111 } 112 113 ctx.HTML(http.StatusOK, tplTwofaScratch) 114 } 115 116 // TwoFactorScratchPost validates and invalidates a user's two-factor scratch token. 117 func TwoFactorScratchPost(ctx *context.Context) { 118 form := web.GetForm(ctx).(*forms.TwoFactorScratchAuthForm) 119 ctx.Data["Title"] = ctx.Tr("twofa_scratch") 120 121 // Ensure user is in a 2FA session. 122 idSess := ctx.Session.Get("twofaUid") 123 if idSess == nil { 124 ctx.ServerError("UserSignIn", errors.New("not in 2FA session")) 125 return 126 } 127 128 id := idSess.(int64) 129 twofa, err := auth.GetTwoFactorByUID(ctx, id) 130 if err != nil { 131 ctx.ServerError("UserSignIn", err) 132 return 133 } 134 135 // Validate the passcode with the stored TOTP secret. 136 if twofa.VerifyScratchToken(form.Token) { 137 // Invalidate the scratch token. 138 _, err = twofa.GenerateScratchToken() 139 if err != nil { 140 ctx.ServerError("UserSignIn", err) 141 return 142 } 143 if err = auth.UpdateTwoFactor(ctx, twofa); err != nil { 144 ctx.ServerError("UserSignIn", err) 145 return 146 } 147 148 remember := ctx.Session.Get("twofaRemember").(bool) 149 u, err := user_model.GetUserByID(ctx, id) 150 if err != nil { 151 ctx.ServerError("UserSignIn", err) 152 return 153 } 154 155 handleSignInFull(ctx, u, remember, false) 156 if ctx.Written() { 157 return 158 } 159 ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used")) 160 ctx.Redirect(setting.AppSubURL + "/user/settings/security") 161 return 162 } 163 164 ctx.RenderWithErr(ctx.Tr("auth.twofa_scratch_token_incorrect"), tplTwofaScratch, forms.TwoFactorScratchAuthForm{}) 165 }