fix(auth): longer IDs and invite codes, register limits, single-use invites (FLAW-32, FLAW-39, FLAW-40)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m2s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m2s
- NewID uses 96 random bits; invite codes get their own 100 bits in Base32. - Registration and forgot-password are limited per address via loginGuard. - The invite is claimed atomically before the account is created and released again when the account cannot be created. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1af3a6e90a
commit
3709282a4f
11 files changed
+220
-15
No files matched your search
@@ -168,6 +168,10 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "Ungültige Anfrage")
|
||||
return
|
||||
}
|
||||
if wait := s.loginGuard.throttle("register", clientIPFrom(r), limitRegisterPerIP); wait > 0 {
|
||||
writeThrottled(w, wait)
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(req.Email) == "" {
|
||||
writeError(w, http.StatusBadRequest, "E-Mail-Adresse erforderlich")
|
||||
return
|
||||
@@ -212,15 +216,26 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// The invite is claimed before the account exists, so it cannot be redeemed twice at once
|
||||
var claim string
|
||||
if validInvite != nil {
|
||||
if claim, err = s.store.ClaimUserInvite(validInvite.ID); err != nil {
|
||||
writeError(w, http.StatusForbidden, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
user, err := s.store.CreateUser(req.Email, hash, req.DisplayName, role)
|
||||
if err != nil {
|
||||
if validInvite != nil {
|
||||
s.store.ReleaseUserInvite(validInvite.ID, claim)
|
||||
}
|
||||
writeError(w, http.StatusConflict, "E-Mail-Adresse existiert bereits")
|
||||
return
|
||||
}
|
||||
|
||||
if validInvite != nil {
|
||||
// Otherwise the code could be used again
|
||||
if err := s.store.MarkUserInviteUsed(validInvite.ID, user.ID); err != nil {
|
||||
if err := s.store.FinishUserInvite(validInvite.ID, claim, user.ID); err != nil {
|
||||
slog.Error("Einladungscode konnte nicht als benutzt markiert werden", "invite", validInvite.ID, "error", err)
|
||||
}
|
||||
if validInvite.OrganizationID != "" {
|
||||
|
||||
Reference in new issue
Block a user