account: stop rotating the SRP challenge on every getPassword read
GetPassword minted a brand-new random SRP server secret and B on every call while only ever assigning SRPID once (when zero). Two account.getPassword calls in a row -- e.g. a settings screen refreshing state, then the transfer- ownership dialog's own cloudPassword().reload() moments later -- silently invalidated each other's B with no signal the client could detect (SRPID unchanged), so a password check built from the first response's B failed with PASSWORD_HASH_INVALID even though the typed password was correct. The challenge now stays stable across reads and only rotates when it's missing entirely; UpdatePasswordSettings/RecoverPassword already mint their own fresh challenge whenever the password actually changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
1f6f25074a
commit
6435406690
2 changed files with 62 additions and 6 deletions
|
|
@ -271,18 +271,26 @@ func (s *Service) GetPassword(ctx context.Context, userID int64) (domain.Passwor
|
|||
return defaultPasswordSettings(), nil
|
||||
}
|
||||
settings = normalizePasswordSettings(settings)
|
||||
if settings.HasPassword {
|
||||
// Only mint a fresh SRP challenge (secret/B/SRPID together) when none is
|
||||
// outstanding yet. Regenerating B on every read while leaving SRPID
|
||||
// untouched let any two account.getPassword calls silently invalidate each
|
||||
// other's B without a signal the client could detect (SRPID unchanged) --
|
||||
// a client that fetched the password state, had a second screen/dialog
|
||||
// refresh it again, then submitted against the first B it saw got a false
|
||||
// PASSWORD_HASH_INVALID even with the correct password. The challenge must
|
||||
// stay stable across reads and only rotate when it's actually consumed by
|
||||
// a password change (UpdatePasswordSettings/RecoverPassword already mint
|
||||
// their own fresh challenge there).
|
||||
if settings.HasPassword && (len(settings.SRPBSecret) == 0 || len(settings.SRPB) == 0 || settings.SRPID == 0) {
|
||||
secret, b, err := makeSRPChallenge(settings.SRPVerifier)
|
||||
if err != nil {
|
||||
return domain.PasswordSettings{}, err
|
||||
}
|
||||
settings.SRPBSecret = secret
|
||||
settings.SRPB = b
|
||||
if settings.SRPID == 0 {
|
||||
settings.SRPID, err = randomInt64()
|
||||
if err != nil {
|
||||
return domain.PasswordSettings{}, err
|
||||
}
|
||||
settings.SRPID, err = randomInt64()
|
||||
if err != nil {
|
||||
return domain.PasswordSettings{}, err
|
||||
}
|
||||
if err := s.passwords.Save(ctx, userID, settings); err != nil {
|
||||
return domain.PasswordSettings{}, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue