feat(api): 添加或删除 passkey、修改邮箱前须 5 分钟内用已有因子重新验证,变更后邮件通知账户,面板加确认对话框与修改邮箱入口

This commit is contained in:
Lemon-miaow committed 2026-09-25 14:47:54 +08:00
1 parent 9e7f23ca13
commit d3769b5c31
36 files changed
+2735 -288

No files matched your search

+22 -2
View File
@@ -66,24 +66,43 @@ func hashCookie(value string) string {
return hex.EncodeToString(sum[:])
}
// signInProof says whether the sign-in minting a session proved a factor of the
// account. A proven sign-in counts as a fresh reauth, so the new session may add
// a passkey or change the email straight away (requireReauth).
type signInProof bool
const (
// provenSignIn: a passkey, an email code, op-login or the setup token.
provenSignIn signInProof = true
// bindCodeSignIn: the in-game identity alone, which never unlocks the
// account's other factors.
bindCodeSignIn signInProof = false
)
// startSession mints a session for userID and sets its cookie. Every sign-in door
// ends here, so every session records the device it was minted for.
func (a *API) startSession(w http.ResponseWriter, r *http.Request, userID string) error {
func (a *API) startSession(w http.ResponseWriter, r *http.Request, userID string, proof signInProof) error {
token, err := newSessionToken()
if err != nil {
return err
}
expires := a.now().Add(sessionTTL)
now := a.now()
expires := now.Add(sessionTTL)
ip := ""
if addr := a.clientIP(r); addr.IsValid() {
ip = addr.String()
}
var reauth time.Time
if proof == provenSignIn {
reauth = now
}
if err := a.Repo.CreateSession(r.Context(), NewSession{
TokenHash: hashCookie(token),
UserID: userID,
ExpiresAt: expires,
UserAgent: truncateUTF8(r.UserAgent(), maxSessionUserAgent),
ClientIP: ip,
ReauthAt: reauth,
}); err != nil {
return err
}
@@ -227,6 +246,7 @@ func (s SessionAuth) Authenticate(r *http.Request) (*Principal, error) {
ViaAdminAccess: staffRole(u.Role) && hostIsAdminConsole(r, s.RootDomain, s.AdminHostname),
EmailVerified: u.EmailVerified,
ViaSession: true,
ReauthAt: u.ReauthAt,
}, nil
}