renox_core/auth/events.rs
1//! Events the built-in auth pages emit, e.g. for a welcome mail or an audit
2//! log (`renox::audit` records them all). Listen like to any event:
3//!
4//! ```
5//! # use renox::prelude::*;
6//! use renox::auth::events::{LoggedIn, Registered};
7//!
8//! # let _ =
9//! App::new()
10//! .module(Auth::new())
11//! .listen(|e: Registered, state| async move {
12//! tracing::info!(user = e.user_id, "welcome!"); // e.g. queue a welcome mail
13//! let _ = state;
14//! Ok(())
15//! })
16//! .listen(|e: LoggedIn, _state| async move {
17//! tracing::info!(user = e.user_id, ip = ?e.ip, "logged in");
18//! Ok(())
19//! })
20//! # ;
21//! ```
22//!
23//! A listener's error is logged; it never fails the login itself. Your own
24//! login code (`auth::login`) emits nothing: emit these yourself if you need
25//! them.
26
27use crate::AppState;
28use crate::events::Event;
29
30/// A new account, after `on_registered` succeeded.
31#[derive(Debug, Clone)]
32#[non_exhaustive]
33pub struct Registered {
34 /// The new user's id.
35 pub user_id: i64,
36 /// The new user's email address (normalized).
37 pub email: String,
38}
39
40/// A successful login on the login page.
41#[derive(Debug, Clone)]
42#[non_exhaustive]
43pub struct LoggedIn {
44 /// The user who logged in.
45 pub user_id: i64,
46 /// The client's IP address (`ClientIp`), if known.
47 pub ip: Option<String>,
48}
49
50/// A wrong email or password on the login page.
51#[derive(Debug, Clone)]
52#[non_exhaustive]
53pub struct LoginFailed {
54 /// The email address that was entered.
55 pub email: String,
56 /// The client's IP address (`ClientIp`), if known.
57 pub ip: Option<String>,
58}
59
60/// Too many failed logins: this email (or address) must wait.
61#[derive(Debug, Clone)]
62#[non_exhaustive]
63pub struct LockedOut {
64 /// The email address that was entered.
65 pub email: String,
66 /// The client's IP address (`ClientIp`), if known.
67 pub ip: Option<String>,
68 /// Seconds left until another attempt is allowed.
69 pub seconds: u64,
70}
71
72/// A logout of one device.
73#[derive(Debug, Clone)]
74#[non_exhaustive]
75pub struct LoggedOut {
76 /// The user who logged out.
77 pub user_id: i64,
78}
79
80/// A password chosen through a reset link.
81#[derive(Debug, Clone)]
82#[non_exhaustive]
83pub struct PasswordReset {
84 /// The user whose password was reset.
85 pub user_id: i64,
86}
87
88/// A password changed on the account page.
89#[derive(Debug, Clone)]
90#[non_exhaustive]
91pub struct PasswordChanged {
92 /// The user whose password changed.
93 pub user_id: i64,
94}
95
96/// An email address confirmed through its link.
97#[derive(Debug, Clone)]
98#[non_exhaustive]
99pub struct EmailVerified {
100 /// The user whose email address was confirmed.
101 pub user_id: i64,
102}
103
104/// Name or email changed on the account page.
105#[derive(Debug, Clone)]
106#[non_exhaustive]
107pub struct ProfileUpdated {
108 /// The user whose profile changed.
109 pub user_id: i64,
110 /// Whether the email address changed.
111 pub email_changed: bool,
112}
113
114/// "Log out other devices" on the account page.
115#[derive(Debug, Clone)]
116#[non_exhaustive]
117pub struct OtherDevicesLoggedOut {
118 /// The user whose other sessions were ended.
119 pub user_id: i64,
120}
121
122/// An account deleted on the account page (the row is already gone).
123#[derive(Debug, Clone)]
124#[non_exhaustive]
125pub struct AccountDeleted {
126 /// The deleted user's id (no longer in `users`).
127 pub user_id: i64,
128 /// The deleted user's email address.
129 pub email: String,
130}
131
132impl Event for Registered {}
133impl Event for LoggedIn {}
134impl Event for LoginFailed {}
135impl Event for LockedOut {}
136impl Event for LoggedOut {}
137impl Event for PasswordReset {}
138impl Event for PasswordChanged {}
139impl Event for EmailVerified {}
140impl Event for ProfileUpdated {}
141impl Event for OtherDevicesLoggedOut {}
142impl Event for AccountDeleted {}
143
144/// Emits `event`; a listener's failure is logged, not returned.
145pub(crate) async fn announce<E: Event>(state: &AppState, event: E) {
146 if let Err(err) = state.emit(event).await {
147 tracing::error!(event = std::any::type_name::<E>(), error = ?err, "auth listener failed");
148 }
149}