From c52b2a4f83afbad1b55e1d5ac59de649837c4ed3 Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 9 Apr 2026 13:14:41 +0200 Subject: [PATCH] feat(auth): add enforceTOTPIfRequired helper for OIDC flow Extracts a TOTP gate that the OIDC callback will use to enforce 2FA for users with TOTP enabled. Mirrors the local-login TOTP flow in pkg/routes/api/v1/login.go. Not yet wired into HandleCallback. Refs GHSA-8jvc-mcx6-r4cg --- pkg/modules/auth/openid/openid.go | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/modules/auth/openid/openid.go b/pkg/modules/auth/openid/openid.go index 505860256..6760b0d1c 100644 --- a/pkg/modules/auth/openid/openid.go +++ b/pkg/modules/auth/openid/openid.go @@ -32,6 +32,7 @@ import ( "code.vikunja.io/api/pkg/modules/auth" "code.vikunja.io/api/pkg/modules/avatar" "code.vikunja.io/api/pkg/modules/avatar/upload" + "code.vikunja.io/api/pkg/modules/keyvalue" "code.vikunja.io/api/pkg/user" "code.vikunja.io/api/pkg/utils" @@ -119,6 +120,38 @@ func (p *Provider) Issuer() (issuerURL string, err error) { return iss.Issuer, nil } +// enforceTOTPIfRequired mirrors the TOTP gate from pkg/routes/api/v1/login.go +// for the OIDC flow. Returns nil when the user does not have TOTP enabled. +// See GHSA-8jvc-mcx6-r4cg. +func enforceTOTPIfRequired(s *xorm.Session, u *user.User, totpPasscode string) error { + totpEnabled, err := user.TOTPEnabledForUser(s, u) + if err != nil { + return err + } + if !totpEnabled { + return nil + } + + if totpPasscode == "" { + return user.ErrInvalidTOTPPasscode{} + } + + _, err = user.ValidateTOTPPasscode(s, &user.TOTPPasscode{ + User: u, + Passcode: totpPasscode, + }) + if err != nil { + return err + } + + // Reset the counter so old failed attempts don't trip a later lockout. + if err := keyvalue.Del(u.GetFailedTOTPAttemptsKey()); err != nil { + return err + } + + return nil +} + // HandleCallback handles the auth request callback after redirecting from the provider with an auth code // @Summary Authenticate a user with OpenID Connect // @Description After a redirect from the OpenID Connect provider to the frontend has been made with the authentication `code`, this endpoint can be used to obtain a jwt token for that user and thus log them in.