feat(api): 添加或删除 passkey、修改邮箱前须 5 分钟内用已有因子重新验证,变更后邮件通知账户,面板加确认对话框与修改邮箱入口
This commit is contained in:
36 files changed
+2735
-288
No files matched your search
@@ -131,6 +131,10 @@ func (a *API) handleEmailOTPStart(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, r, newError(http.StatusBadRequest, "bad_request", "a valid email is required"))
|
||||
return
|
||||
}
|
||||
// Gate the start: the verify only redeems a code minted here.
|
||||
if !a.requireReauth(w, r, p) {
|
||||
return
|
||||
}
|
||||
// Atomically reserve the cooldown on both the caller and the recipient BEFORE
|
||||
// minting, so a burst of truly concurrent starts yields exactly one winner. Here
|
||||
// the throttle is the sole defense and each admitted send is a real, non-idempotent
|
||||
@@ -249,10 +253,14 @@ func (a *API) handleEmailOTPVerify(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
a.audit(r, "account.email.verified", "")
|
||||
// Proving the address is an email reauth.
|
||||
a.markReauthQuietly(r)
|
||||
// Replacing a verified address moves where sign-in codes go, so a session
|
||||
// opened through the old one ends. A first verification retires nothing.
|
||||
// opened through the old one ends, and the old mailbox hears about it. A
|
||||
// first verification retires nothing.
|
||||
if p.EmailVerified && !strings.EqualFold(p.Email, email) {
|
||||
a.revokeOtherSessionsAfter(r, "email change")
|
||||
a.notifyEmailChanged(r, p.Email, email)
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"verified": true, "email": email})
|
||||
}
|
||||
@@ -320,6 +328,11 @@ func (a *API) handleSetEmail(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, r, newError(http.StatusBadRequest, "bad_request", "a valid email is required"))
|
||||
return
|
||||
}
|
||||
// Recording an address unverifies the current one, which would strip the
|
||||
// account's email factor and with it the reauth that guards adding a passkey.
|
||||
if !a.requireReauth(w, r, p) {
|
||||
return
|
||||
}
|
||||
if err := a.Repo.SetUserEmail(r.Context(), p.UserID, email); err != nil {
|
||||
writeError(w, r, err)
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user