feat(accounts): outgoing mail over SMTP (Resend), email verification, fail-closed password reset
- mail/ module (lettre, ru/en templates), links built only from PUBLIC_BASE_URL - migration 0006: accounts.email_verified_at, shared email_tokens table (verify + reset), existing accounts marked verified - reset mailer no longer logs tokens; RESET_MAIL_MODE=log is dev-only and refused with COOKIE_SECURE=true; Disabled by default answers 503 - forgot-password and resend-verification do their work in a background task (no timing oracle) - device linking requires a verified email; email_verified exposed via /me and gRPC - gateway rate limits, SMTP_* in compose and .env.example - frontend: verify, forgot-password, reset-password pages, verify banner, ru/en strings
This commit is contained in:
parent
12ce6216b6
commit
c57f851a8b
48 changed files with 1810 additions and 120 deletions
|
|
@ -38,6 +38,14 @@ pub fn test_config() -> Config {
|
|||
s3_secret_key: "minioadmin".into(),
|
||||
cookie_secure: false,
|
||||
avatar_public_base_url: String::new(),
|
||||
smtp_host: String::new(),
|
||||
smtp_port: 587,
|
||||
smtp_user: String::new(),
|
||||
smtp_password: String::new(),
|
||||
mail_from: String::new(),
|
||||
public_base_url: String::new(),
|
||||
mail_lang: accounts_service::mail::Lang::default(),
|
||||
reset_mail_log: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +58,9 @@ pub fn test_server(app: axum::Router) -> TestServer {
|
|||
server
|
||||
}
|
||||
|
||||
/// Registers a fresh random account; returns its id and email.
|
||||
/// Registers a fresh random account; returns its id and email. The account
|
||||
/// starts unverified, exactly like a real sign-up (device linking therefore
|
||||
/// needs `mark_verified` first).
|
||||
pub async fn register_account(server: &TestServer) -> (Uuid, String) {
|
||||
let email = format!("t-{}@example.com", Uuid::new_v4());
|
||||
let res = server
|
||||
|
|
@ -65,6 +75,17 @@ pub async fn register_account(server: &TestServer) -> (Uuid, String) {
|
|||
)
|
||||
}
|
||||
|
||||
/// Marks the account's email as verified in place. Stand-in for the real
|
||||
/// `/auth/verify-email` round trip in flows that only need a verified
|
||||
/// account (device linking, avatars).
|
||||
pub async fn mark_verified(pool: &sqlx::PgPool, account_id: Uuid) {
|
||||
sqlx::query("UPDATE accounts SET email_verified_at = now() WHERE id = $1")
|
||||
.bind(account_id)
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("mark account verified");
|
||||
}
|
||||
|
||||
/// Creates the test avatar bucket if it does not exist yet, so the avatar
|
||||
/// tests are self-contained: any S3-compatible backend (MinIO, SeaweedFS)
|
||||
/// works without external `mc mb` bootstrap. Mirrors `S3Storage::from_config`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue