Comments, fixes
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Melody Becker 2025-04-01 17:24:06 +02:00
parent 4fb0e17b69
commit ef91558600
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
8 changed files with 73 additions and 19 deletions

View file

@ -3,6 +3,7 @@ package auth
// Some helpful comments from: https://waters.me/internet/google-authenticator-implementation-note-key-length-token-reuse/
import (
"strings"
"time"
"git.mstar.dev/mstar/goutils/other"
@ -21,6 +22,7 @@ const totpTokenNoLongerRecentlyUsed = time.Second * 90
func (a *Authenticator) PerformTotpLogin(
username string,
sessionId uint64,
totpToken string,
) (LoginNextState, string, error) {
// First check if that token has been seen recently for that user
@ -31,17 +33,26 @@ func (a *Authenticator) PerformTotpLogin(
delete(a.recentlyUsedTotpTokens, totpToken+"+"+username)
}
}
// Then ensure user is allowed to log in
if ok, err := a.canUsernameLogin(username); !ok {
return 0, "", other.Error("auth", "user may not login", err)
}
acc, err := dbgen.User.Where(dbgen.User.Username.Eq(username)).First()
// DO NOT fetch the account directly. Go via session id
// otherwise you could in theory log in with only the totp token
// which obviously is a huge risk with it being at most 1'000'000 unique values.
// And even with rate limiting (performed by one of the upper layers)
// this wouldn't take too long
loginSession, err := dbgen.LoginProcessToken.Where(dbgen.LoginProcessToken.ID.Eq(sessionId)).
First()
if err != nil {
return LoginNextFailure, "", other.Error("auth", "failed to find account", err)
return 0, "", other.Error("auth", "no login session with this id", err)
}
acc := loginSession.User
dbSecrets := sliceutils.Filter(acc.AuthMethods, func(t models.UserAuthMethod) bool {
return t.AuthMethod == models.AuthMethodGAuth
})
encryptedSecrets := sliceutils.Map(
sliceutils.Filter(acc.AuthMethods, func(t models.UserAuthMethod) bool {
return t.AuthMethod == models.AuthMethodGAuth
}),
dbSecrets,
func(t models.UserAuthMethod) string {
return string(t.Token)
},
@ -59,9 +70,11 @@ func (a *Authenticator) PerformTotpLogin(
secrets = append(secrets, string(decrypted))
}
found := false
for _, secret := range secrets {
foundIndex := -1
for i, secret := range secrets {
if totp.Validate(totpToken, secret) {
found = true
foundIndex = i
break
}
}
@ -72,11 +85,20 @@ func (a *Authenticator) PerformTotpLogin(
ErrInvalidCombination,
)
}
// If not verified yet, mark as verified
if strings.HasSuffix(dbSecrets[foundIndex].Name, totpUnverifiedSuffix) {
dbgen.UserAuthMethod.
Where(dbgen.UserAuthMethod.ID.
Eq(dbSecrets[foundIndex].ID)).
Update(dbgen.UserAuthMethod.Name, strings.TrimSuffix(dbSecrets[foundIndex].Name, totpUnverifiedSuffix))
}
// store this token and username combination as recently used
a.recentlyUsedTotpTokens[totpToken+"+"+username] = time.Now()
// Generate access token and return since totp would be the end station for 2fa
token := models.AccessToken{
User: *acc,
User: acc,
UserId: acc.ID,
ExpiresAt: time.Now().Add(time.Hour * 24 * 365),
}