use super::claims::ClerkAuth;
use super::error::ClerkError;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct AuthState {
pub(crate) status: AuthStatus,
pub(crate) is_loaded: bool,
pub(crate) user_id: Option<String>,
pub(crate) session_id: Option<String>,
pub(crate) org_id: Option<String>,
pub(crate) org_slug: Option<String>,
pub(crate) org_role: Option<String>,
pub(crate) org_permissions: Vec<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AuthStatus {
#[default]
Loading,
SignedOut,
SignedIn,
}
impl AuthStatus {
pub fn is_loading(self) -> bool {
matches!(self, Self::Loading)
}
pub fn is_signed_out(self) -> bool {
matches!(self, Self::SignedOut)
}
pub fn is_signed_in(self) -> bool {
matches!(self, Self::SignedIn)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum AuthRequirement {
SignedIn,
Role(String),
Permission(String),
}
impl AuthRequirement {
pub fn signed_in() -> Self {
Self::SignedIn
}
pub fn role(role: impl Into<String>) -> Self {
Self::Role(role.into())
}
pub fn permission(permission: impl Into<String>) -> Self {
Self::Permission(permission.into())
}
}
impl AuthState {
pub fn loading() -> Self {
Self {
status: AuthStatus::Loading,
..Self::signed_out()
}
}
pub fn signed_out() -> Self {
Self {
status: AuthStatus::SignedOut,
is_loaded: false,
user_id: None,
session_id: None,
org_id: None,
org_slug: None,
org_role: None,
org_permissions: vec![],
}
}
pub fn signed_in(user_id: impl Into<String>) -> Self {
let user_id = user_id.into();
if user_id.is_empty() {
return Self::signed_out();
}
Self {
status: AuthStatus::SignedIn,
user_id: Some(user_id),
..Self::signed_out()
}
}
#[must_use]
pub fn with_loaded(mut self, is_loaded: bool) -> Self {
self.is_loaded = is_loaded;
self
}
#[must_use]
pub fn with_session_id(mut self, session_id: impl Into<String>) -> Self {
self.session_id = Some(session_id.into());
self
}
#[must_use]
pub fn with_org_id(mut self, org_id: impl Into<String>) -> Self {
self.org_id = Some(org_id.into());
self
}
#[must_use]
pub fn with_org_slug(mut self, org_slug: impl Into<String>) -> Self {
self.org_slug = Some(org_slug.into());
self
}
#[must_use]
pub fn with_org_role(mut self, org_role: impl Into<String>) -> Self {
self.org_role = Some(org_role.into());
self
}
#[must_use]
pub fn with_org_permissions(
mut self,
org_permissions: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.org_permissions = org_permissions.into_iter().map(Into::into).collect();
self
}
pub fn status(&self) -> AuthStatus {
self.status
}
pub fn is_loaded(&self) -> bool {
self.is_loaded
}
pub fn user_id(&self) -> Option<&str> {
self.user_id.as_deref()
}
pub fn session_id(&self) -> Option<&str> {
self.session_id.as_deref()
}
pub fn org_id(&self) -> Option<&str> {
self.org_id.as_deref()
}
pub fn org_slug(&self) -> Option<&str> {
self.org_slug.as_deref()
}
pub fn org_role(&self) -> Option<&str> {
self.org_role.as_deref()
}
pub fn org_permissions(&self) -> &[String] {
&self.org_permissions
}
pub fn is_signed_in(&self) -> bool {
self.status.is_signed_in()
}
pub fn is_loading(&self) -> bool {
self.status.is_loading()
}
pub fn is_signed_out(&self) -> bool {
self.status.is_signed_out()
}
pub fn require_signed_in(&self) -> Result<&str, ClerkError> {
if self.status.is_loading() {
return Err(ClerkError::NotLoaded);
}
if !self.status.is_signed_in() {
return Err(ClerkError::Unauthenticated);
}
self.user_id.as_deref().ok_or(ClerkError::Unauthenticated)
}
pub fn has_role(&self, role: &str) -> bool {
self.org_role.as_deref() == Some(role)
}
pub fn has_permission(&self, permission: &str) -> bool {
self.org_permissions.iter().any(|p| p == permission)
}
pub fn has(&self, requirement: &AuthRequirement) -> bool {
if !self.is_signed_in() {
return false;
}
match requirement {
AuthRequirement::SignedIn => true,
AuthRequirement::Role(role) => self.has_role(role),
AuthRequirement::Permission(permission) => self.has_permission(permission),
}
}
}
impl From<ClerkAuth> for AuthState {
fn from(c: ClerkAuth) -> Self {
if c.user_id.is_empty() {
return Self::signed_out();
}
Self {
status: AuthStatus::SignedIn,
is_loaded: false,
user_id: Some(c.user_id),
session_id: c.session_id,
org_id: c.org_id,
org_slug: c.org_slug,
org_role: c.org_role,
org_permissions: c.org_permissions,
}
}
}
impl From<&ClerkAuth> for AuthState {
fn from(c: &ClerkAuth) -> Self {
Self::from(c.clone())
}
}