Skip to main content

authly_core/
lib.rs

1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5pub mod pkce;
6
7/// A unified identity structure returned by all providers.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Identity {
10    /// The provider identifier (e.g., "github", "google")
11    pub provider_id: String,
12    /// The unique ID of the user within the provider's system
13    pub external_id: String,
14    /// The user's email address, if available and authorized
15    pub email: Option<String>,
16    /// The user's username or display name, if available
17    pub username: Option<String>,
18    /// Additional provider-specific attributes
19    pub attributes: HashMap<String, String>,
20}
21
22/// Represents the tokens returned by an OAuth2 provider.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct OAuthToken {
25    /// The access token used for API requests
26    pub access_token: String,
27    /// The type of token (usually "Bearer")
28    pub token_type: String,
29    /// Seconds until the access token expires
30    pub expires_in: Option<u64>,
31    /// The refresh token used to obtain new access tokens
32    pub refresh_token: Option<String>,
33    /// The scopes granted by the user
34    pub scope: Option<String>,
35    /// The OIDC ID Token
36    pub id_token: Option<String>,
37}
38
39/// Errors that can occur during the authentication process.
40#[derive(Debug, thiserror::Error)]
41pub enum AuthError {
42    /// An error returned by the authentication provider
43    #[error("Provider error: {0}")]
44    Provider(String),
45    /// The provided credentials (email/password) are invalid
46    #[error("Invalid credentials")]
47    InvalidCredentials,
48    /// The authorization code is invalid or expired
49    #[error("Invalid code")]
50    InvalidCode,
51    /// A network error occurred during communication with the provider
52    #[error("Network error")]
53    Network,
54    /// An error occurred during session management
55    #[error("Session error: {0}")]
56    Session(String),
57    /// An error occurred during token processing
58    #[error("Token error: {0}")]
59    Token(String),
60    /// The CSRF state parameter does not match the expected value
61    #[error("CSRF state mismatch")]
62    CsrfMismatch,
63}
64
65/// Trait for an OAuth2-compatible provider.
66#[async_trait]
67pub trait OAuthProvider: Send + Sync {
68    /// Helper to get the authorization URL.
69    fn get_authorization_url(
70        &self,
71        state: &str,
72        scopes: &[&str],
73        code_challenge: Option<&str>,
74    ) -> String;
75
76    /// Exchange an authorization code for an Identity.
77    async fn exchange_code_for_identity(
78        &self,
79        code: &str,
80        code_verifier: Option<&str>,
81    ) -> Result<(Identity, OAuthToken), AuthError>;
82
83    /// Refresh an access token using a refresh token.
84    async fn refresh_token(&self, _refresh_token: &str) -> Result<OAuthToken, AuthError> {
85        Err(AuthError::Provider(
86            "Token refresh not supported by this provider".into(),
87        ))
88    }
89
90    /// Revoke an access token.
91    async fn revoke_token(&self, _token: &str) -> Result<(), AuthError> {
92        Err(AuthError::Provider(
93            "Token revocation not supported by this provider".into(),
94        ))
95    }
96}
97
98/// Trait for a Credentials-based provider (e.g., Email/Password).
99#[async_trait]
100pub trait CredentialsProvider: Send + Sync {
101    type Credentials;
102
103    /// Validate credentials and return an Identity.
104    async fn authenticate(&self, creds: Self::Credentials) -> Result<Identity, AuthError>;
105}
106
107/// Trait for mapping a provider identity to a local user.
108#[async_trait]
109pub trait UserMapper: Send + Sync {
110    type LocalUser: Send + Sync;
111
112    /// Map an identity to a local user.
113    /// This could involve creating a new user or finding an existing one.
114    async fn map_user(&self, identity: &Identity) -> Result<Self::LocalUser, AuthError>;
115}
116
117#[async_trait]
118impl UserMapper for () {
119    type LocalUser = ();
120    async fn map_user(&self, _identity: &Identity) -> Result<Self::LocalUser, AuthError> {
121        Ok(())
122    }
123}