diff --git a/pkg/modules/auth/openid/openid_test.go b/pkg/modules/auth/openid/openid_test.go index a7c739f55..05ade6745 100644 --- a/pkg/modules/auth/openid/openid_test.go +++ b/pkg/modules/auth/openid/openid_test.go @@ -18,12 +18,14 @@ package openid import ( "testing" + "time" "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/user" "github.com/coreos/go-oidc/v3/oidc" + "github.com/pquerna/otp/totp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -420,6 +422,57 @@ func TestMergeClaims(t *testing.T) { }) } +func TestEnforceTOTPIfRequired(t *testing.T) { + // user 10 has TOTP enabled in pkg/db/fixtures/totp.yml with this secret. + const user10Secret = "JBSWY3DPEHPK3PXP" + + t.Run("user without TOTP - no passcode required", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + // user 1 has a totp row but with enabled=false. + u := &user.User{ID: 1} + err := enforceTOTPIfRequired(s, u, "") + require.NoError(t, err) + }) + + t.Run("TOTP enabled - missing passcode returns ErrInvalidTOTPPasscode", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + u := &user.User{ID: 10} + err := enforceTOTPIfRequired(s, u, "") + require.Error(t, err) + assert.True(t, user.IsErrInvalidTOTPPasscode(err)) + }) + + t.Run("TOTP enabled - invalid passcode returns ErrInvalidTOTPPasscode", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + u := &user.User{ID: 10} + err := enforceTOTPIfRequired(s, u, "000000") + require.Error(t, err) + assert.True(t, user.IsErrInvalidTOTPPasscode(err)) + }) + + t.Run("TOTP enabled - valid passcode succeeds", func(t *testing.T) { + db.LoadAndAssertFixtures(t) + s := db.NewSession() + defer s.Close() + + passcode, err := totp.GenerateCode(user10Secret, time.Now()) + require.NoError(t, err) + + u := &user.User{ID: 10} + err = enforceTOTPIfRequired(s, u, passcode) + require.NoError(t, err) + }) +} + func TestSyncUserAvatarFromOpenID(t *testing.T) { t.Run("empty picture URL resets openid provider to default", func(t *testing.T) { db.LoadAndAssertFixtures(t)