Skip to main content

better_auth_api/plugins/admin/
banned_message.rs

1//! Banned-user messages resolved before session creation.
2
3use better_auth_core::{AuthResult, wire::UserView};
4use std::{fmt, future::Future, pin::Pin, sync::Arc};
5
6/// A message callback may borrow the stored user while awaiting application work.
7pub type BannedUserMessageFuture<'a> =
8    Pin<Box<dyn Future<Output = AuthResult<String>> + Send + 'a>>;
9type MessageCallback = dyn for<'a> Fn(&'a UserView) -> BannedUserMessageFuture<'a> + Send + Sync;
10
11/// Fixed text or an asynchronous message generated from the stored user.
12#[derive(Clone)]
13pub enum BannedUserMessage {
14    /// Reuse this message for every active ban.
15    Text(String),
16    /// Resolve the message before rejecting session creation.
17    Callback(Arc<MessageCallback>),
18}
19
20impl BannedUserMessage {
21    /// Build a callback that can inspect private application user fields.
22    pub fn callback<F>(callback: F) -> Self
23    where
24        F: for<'a> Fn(&'a UserView) -> BannedUserMessageFuture<'a> + Send + Sync + 'static,
25    {
26        Self::Callback(Arc::new(callback))
27    }
28
29    pub(crate) async fn resolve(&self, user: &UserView) -> AuthResult<String> {
30        match self {
31            Self::Text(message) => Ok(message.clone()),
32            Self::Callback(callback) => callback(user).await.map_err(|error| {
33                if better_auth_core::hooks::current_request_hook_context()
34                    .is_some_and(|ctx| ctx.is_http)
35                    && error.status_code() == 500
36                    && !matches!(
37                        error,
38                        better_auth_core::AuthError::Response(_)
39                            | better_auth_core::AuthError::Upstream { .. }
40                    )
41                {
42                    better_auth_core::observability::logger::current().error(
43                        "Admin banned-user message callback failed",
44                        &[better_auth_core::observability::LogArgument::Error(&error)],
45                    );
46                    better_auth_core::AuthResponse::new(500).into()
47                } else {
48                    error
49                }
50            }),
51        }
52    }
53}
54
55impl Default for BannedUserMessage {
56    fn default() -> Self {
57        Self::Text("You have been banned from this application. Please contact support if you believe this is an error.".into())
58    }
59}
60
61impl From<String> for BannedUserMessage {
62    fn from(message: String) -> Self {
63        Self::Text(message)
64    }
65}
66impl From<&str> for BannedUserMessage {
67    fn from(message: &str) -> Self {
68        Self::Text(message.into())
69    }
70}
71impl fmt::Debug for BannedUserMessage {
72    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match self {
74            Self::Text(message) => formatter.debug_tuple("Text").field(message).finish(),
75            Self::Callback(_) => formatter.write_str("Callback(..)"),
76        }
77    }
78}