feat(email): PWA login sends code only; drop admin link from alert emails
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m40s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 15s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m40s
Two email changes, per the owner: 1. Code-only login from an installed PWA. A login requested while running as a standalone PWA now omits the one-tap confirm link from the email — the link would open in a separate browser whose minted session cannot reach the PWA, stranding the login. The code is typed in the same window instead. The client sends a `pwa` flag (isStandalone) on the email-code request (a new FBS field, threaded through the gateway); the backend omits the deeplink when it is set, reusing the existing deletion-code link-omission path. Non-PWA browser logins keep the one-tap link. No polling, no migration. 2. Security: the operator alert digest no longer embeds the admin-console (/_gm) URL. An admin link must never travel in an email, where a mail provider could cache or index it; the operator opens the console directly. Tests: an inttest asserting the PWA login email omits the link (and a browser one keeps it); a codec round-trip of the new pwa field; the alert-digest test flipped to guard the admin link is absent. Docs: ARCHITECTURE + FUNCTIONAL (+ru).
This commit is contained in:
@@ -33,21 +33,21 @@ type Notifier struct {
|
||||
complaints ComplaintCounter
|
||||
from string
|
||||
to string
|
||||
consoleURL string
|
||||
clock func() time.Time
|
||||
log *zap.Logger
|
||||
last time.Time
|
||||
}
|
||||
|
||||
// New constructs a Notifier. from and to are the alert sender and recipient(s); consoleURL,
|
||||
// when non-empty, is the admin-console link included in the email. log may be nil. The
|
||||
// watermark starts at "now", so only items arriving after start-up are reported.
|
||||
func New(mailer account.Mailer, fb FeedbackCounter, cp ComplaintCounter, from, to, consoleURL string, log *zap.Logger) *Notifier {
|
||||
// New constructs a Notifier. from and to are the alert sender and recipient(s); log may be
|
||||
// nil. The watermark starts at "now", so only items arriving after start-up are reported. The
|
||||
// digest deliberately carries no admin-console link — an admin URL must never travel in an
|
||||
// email, where a mail provider could cache or index it.
|
||||
func New(mailer account.Mailer, fb FeedbackCounter, cp ComplaintCounter, from, to string, log *zap.Logger) *Notifier {
|
||||
if log == nil {
|
||||
log = zap.NewNop()
|
||||
}
|
||||
return &Notifier{
|
||||
mailer: mailer, feedback: fb, complaints: cp, from: from, to: to, consoleURL: consoleURL,
|
||||
mailer: mailer, feedback: fb, complaints: cp, from: from, to: to,
|
||||
clock: func() time.Time { return time.Now().UTC() }, log: log, last: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
@@ -103,10 +103,9 @@ func (n *Notifier) digest(fb, cp int) account.Message {
|
||||
parts = append(parts, fmt.Sprintf("%d new word complaint(s)", cp))
|
||||
}
|
||||
summary := strings.Join(parts, ", ")
|
||||
// No admin-console link in the body: an admin URL must never travel in an email (a mail
|
||||
// provider could cache or index it). The operator opens the console directly.
|
||||
text := summary + "."
|
||||
if n.consoleURL != "" {
|
||||
text += "\n\nOpen the admin console: " + n.consoleURL
|
||||
}
|
||||
return account.Message{
|
||||
From: n.from,
|
||||
To: n.to,
|
||||
|
||||
Reference in New Issue
Block a user