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