ft_sdk/auth/
mod.rs

1#[cfg(feature = "auth-provider")]
2pub mod provider;
3mod schema;
4mod utils;
5
6pub use ft_sys_shared::SESSION_KEY;
7pub use schema::fastn_user;
8pub use utils::{user_data_by_query, Counter};
9
10#[derive(Clone, Debug)]
11pub struct UserId(pub i64);
12
13#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Default)]
14#[serde(default)]
15pub struct ProviderData {
16    pub identity: String,
17    pub username: Option<String>,
18    pub name: Option<String>,
19    pub emails: Vec<String>,
20    pub verified_emails: Vec<String>,
21    pub profile_picture: Option<String>,
22    pub custom: serde_json::Value,
23}
24
25impl ProviderData {
26    pub fn get_custom<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
27        self.custom
28            .get(key)
29            .and_then(|v| serde_json::from_value(v.clone()).ok())
30    }
31
32    /// get the first verified or unverified email address
33    pub fn first_email(&self) -> Option<String> {
34        self.verified_emails
35            .first()
36            .cloned()
37            .or_else(|| self.emails.first().cloned())
38    }
39}
40
41/// Get the currently logged-in user's userid. Returns `None` if the user is not logged in.
42pub fn user_id() -> Option<UserId> {
43    todo!()
44}
45
46/// get the currently logged in user's username
47pub fn username(_provider: &str) -> Option<String> {
48    todo!()
49}
50
51/// Get all user data stored against the user in the database. Only allowed if scope
52/// auth:* is granted. Based on permission, whatever you have access to will be given.
53pub fn get_user_data() -> std::collections::HashMap<String, ProviderData> {
54    todo!()
55}
56
57pub fn is_authenticated() -> bool {
58    todo!()
59}
60
61/// This gives you a list of IDs related to the provider, for the user.
62pub fn provider_ids(_provider: &str) -> Vec<String> {
63    todo!()
64}
65
66/// This gives you a list of IDs related to the provider, for the session.
67pub fn session_provider_ids(_provider: &str) -> Vec<String> {
68    todo!()
69}
70
71/// This returns a list of providers whose credentials are attached to the current user
72/// account.
73pub fn providers() -> Vec<String> {
74    todo!()
75}
76
77/// This returns a list of providers whose credentials are attached to this session.
78pub fn session_providers() -> Vec<String> {
79    todo!()
80}
81
82#[cfg(feature = "field-extractors")]
83pub fn ud(
84    cookie: ft_sdk::Cookie<SESSION_KEY>,
85    conn: &mut ft_sdk::Connection,
86) -> Result<Option<ft_sys::UserData>, UserDataError> {
87    if let Some(v) = ft_sys::env::var("DEBUG_LOGGED_IN".to_string()) {
88        let mut v = v.splitn(4, ' ');
89        return Ok(Some(ft_sys::UserData {
90            id: v.next().unwrap().parse().unwrap(),
91            identity: v.next().unwrap_or_default().to_string(),
92            name: v.next().map(|v| v.to_string()).unwrap_or_default(),
93            email: v.next().map(|v| v.to_string()).unwrap_or_default(),
94            verified_email: true,
95        }));
96    }
97
98    ft_sdk::println!("sid: {cookie}");
99
100    let sid = match cookie.0 {
101        Some(v) => v,
102        None => return Ok(None),
103    };
104
105    let (UserId(id), identity, data) = match utils::user_data_by_query(
106        conn,
107        r#"
108            SELECT
109                fastn_user.id as id, identity, fastn_user.data -> 'email' as data
110            FROM fastn_user
111            JOIN fastn_session
112            WHERE
113                fastn_session.id = $1
114                AND fastn_user.id = fastn_session.uid
115            "#,
116        sid.as_str(),
117    ) {
118        Ok(v) => v,
119        Err(UserDataError::NoDataFound) => return Ok(None),
120        Err(e) => return Err(e),
121    };
122
123    let email = data
124        .first_email()
125        .expect("email provider must have an email");
126
127    Ok(Some(ft_sys::UserData {
128        id,
129        identity: identity.expect("user fetched from session cookie must have identity"),
130        name: data.name.unwrap_or_default(),
131        email,
132        verified_email: !data.verified_emails.is_empty(),
133    }))
134}
135
136#[derive(Debug, thiserror::Error)]
137pub enum UserDataError {
138    #[error("no data found for the provider")]
139    NoDataFound,
140    #[error("multiple rows found")]
141    MultipleRowsFound,
142    #[error("db error: {0:?}")]
143    DatabaseError(#[from] diesel::result::Error),
144    #[error("failed to deserialize data from db: {0:?}")]
145    FailedToDeserializeData(#[from] serde_json::Error),
146}