use async_trait::async_trait;
use std::future::Future;
use systemprompt_identifiers::UserId;
use crate::BoxedSource;
pub type AuthResult<T> = Result<T, AuthProviderError>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum AuthProviderError {
#[error("Invalid credentials")]
InvalidCredentials,
#[error("User not found")]
UserNotFound,
#[error("Invalid token")]
InvalidToken,
#[error("Token expired")]
TokenExpired,
#[error("Insufficient permissions")]
InsufficientPermissions,
#[error("Internal error: {0}")]
Internal(#[source] BoxedSource),
}
#[derive(Debug, Clone)]
pub struct AuthUser {
pub id: UserId,
pub name: String,
pub email: String,
pub roles: Vec<String>,
pub is_active: bool,
}
#[derive(Debug, Clone, Default)]
pub struct FederatedIdentityClaims {
pub email: Option<String>,
pub email_verified: bool,
pub name: Option<String>,
pub preferred_username: Option<String>,
pub roles: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub enum SenderIdentity {
Linked(FederatedIdentityClaims),
#[default]
Unlinked,
}
impl SenderIdentity {
#[must_use]
pub fn claims(&self) -> FederatedIdentityClaims {
match self {
Self::Linked(claims) => claims.clone(),
Self::Unlinked => FederatedIdentityClaims::default(),
}
}
}
#[async_trait]
pub trait UserProvider: Send + Sync {
async fn find_by_id(&self, id: &UserId) -> AuthResult<Option<AuthUser>>;
async fn find_by_email(&self, email: &str) -> AuthResult<Option<AuthUser>>;
async fn find_by_name(&self, name: &str) -> AuthResult<Option<AuthUser>>;
async fn create_user(
&self,
name: &str,
email: &str,
full_name: Option<&str>,
) -> AuthResult<AuthUser>;
async fn create_anonymous(&self, fingerprint: &str) -> AuthResult<AuthUser>;
async fn assign_roles(&self, user_id: &UserId, roles: &[String]) -> AuthResult<()>;
async fn find_or_create_federated(
&self,
issuer: &str,
external_sub: &str,
claims: &FederatedIdentityClaims,
) -> AuthResult<UserId>;
async fn promote_anonymous(&self, source: &UserId, target: &UserId) -> AuthResult<u64>;
}
pub trait RoleProvider: Send + Sync {
fn get_roles(&self, user_id: &UserId) -> impl Future<Output = AuthResult<Vec<String>>> + Send;
fn assign_role(
&self,
user_id: &UserId,
role: &str,
) -> impl Future<Output = AuthResult<()>> + Send;
fn revoke_role(
&self,
user_id: &UserId,
role: &str,
) -> impl Future<Output = AuthResult<()>> + Send;
fn list_users_by_role(
&self,
role: &str,
) -> impl Future<Output = AuthResult<Vec<AuthUser>>> + Send;
}