Skip to main content

better_auth_core/wire/
session.rs

1use super::*;
2/// Public session response shape.
3#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
4pub struct SessionView {
5    pub id: String,
6    #[serde(rename = "expiresAt")]
7    pub expires_at: DateTime<Utc>,
8    pub token: String,
9    #[serde(rename = "createdAt")]
10    pub created_at: DateTime<Utc>,
11    #[serde(rename = "updatedAt")]
12    pub updated_at: DateTime<Utc>,
13    #[serde(rename = "ipAddress")]
14    pub ip_address: Option<String>,
15    #[serde(rename = "userAgent")]
16    pub user_agent: Option<String>,
17    #[serde(rename = "userId")]
18    pub user_id: String,
19    #[serde(
20        rename = "impersonatedBy",
21        default,
22        skip_serializing_if = "Option::is_none"
23    )]
24    pub impersonated_by: Option<String>,
25    #[serde(
26        rename = "activeOrganizationId",
27        default,
28        skip_serializing_if = "Option::is_none"
29    )]
30    pub active_organization_id: Option<String>,
31    #[serde(
32        rename = "activeTeamId",
33        default,
34        skip_serializing_if = "Option::is_none"
35    )]
36    pub active_team_id: Option<String>,
37    #[serde(
38        flatten,
39        default,
40        deserialize_with = "crate::utils::json::deserialize_btree_map"
41    )]
42    pub extension_fields: std::collections::BTreeMap<String, serde_json::Value>,
43    #[serde(skip)]
44    pub active: bool,
45    #[serde(skip)]
46    pub omitted_fields: std::collections::BTreeSet<String>,
47}
48
49impl Serialize for SessionView {
50    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
51        use serde::ser::SerializeMap;
52        let mut map = serializer.serialize_map(None)?;
53        macro_rules! entry {
54            ($name:literal, $value:expr) => {
55                if !self.omitted_fields.contains($name) {
56                    map.serialize_entry($name, $value)?;
57                }
58            };
59        }
60        entry!("id", &self.id);
61        entry!(
62            "expiresAt",
63            &crate::utils::datetime::json_date_millis(self.expires_at.timestamp_millis())
64        );
65        entry!("token", &self.token);
66        entry!(
67            "createdAt",
68            &crate::utils::datetime::json_date_millis(self.created_at.timestamp_millis())
69        );
70        entry!(
71            "updatedAt",
72            &crate::utils::datetime::json_date_millis(self.updated_at.timestamp_millis())
73        );
74        entry!("ipAddress", &self.ip_address);
75        entry!("userAgent", &self.user_agent);
76        entry!("userId", &self.user_id);
77        if let Some(value) = &self.impersonated_by {
78            entry!("impersonatedBy", value);
79        }
80        if let Some(value) = &self.active_organization_id {
81            entry!("activeOrganizationId", value);
82        }
83        if let Some(value) = &self.active_team_id {
84            entry!("activeTeamId", value);
85        }
86        for (name, value) in &self.extension_fields {
87            if !self.omitted_fields.contains(name) {
88                map.serialize_entry(name, value)?;
89            }
90        }
91        map.end()
92    }
93}
94
95impl<T: AuthSession> From<&T> for SessionView {
96    fn from(session: &T) -> Self {
97        Self {
98            id: session.id().into_owned(),
99            expires_at: session.expires_at(),
100            token: session.token().to_owned(),
101            created_at: session.created_at(),
102            updated_at: session.updated_at(),
103            ip_address: session.ip_address().map(str::to_owned),
104            user_agent: session.user_agent().map(str::to_owned),
105            user_id: session.user_id().into_owned(),
106            impersonated_by: session.impersonated_by().map(str::to_owned),
107            active_organization_id: session.active_organization_id().map(str::to_owned),
108            active_team_id: session.active_team_id().map(str::to_owned),
109            extension_fields: session.additional_fields().into_iter().collect(),
110            omitted_fields: std::collections::BTreeSet::default(),
111            active: session.active(),
112        }
113    }
114}
115
116impl AuthSession for SessionView {
117    fn retained_session_view(&self) -> Option<&SessionView> {
118        Some(self)
119    }
120    fn additional_fields(&self) -> serde_json::Map<String, serde_json::Value> {
121        self.extension_fields.clone().into_iter().collect()
122    }
123    fn id(&self) -> Cow<'_, str> {
124        Cow::Borrowed(&self.id)
125    }
126    fn expires_at(&self) -> DateTime<Utc> {
127        self.expires_at
128    }
129    fn token(&self) -> &str {
130        &self.token
131    }
132    fn created_at(&self) -> DateTime<Utc> {
133        self.created_at
134    }
135    fn updated_at(&self) -> DateTime<Utc> {
136        self.updated_at
137    }
138    fn ip_address(&self) -> Option<&str> {
139        self.ip_address.as_deref()
140    }
141    fn user_agent(&self) -> Option<&str> {
142        self.user_agent.as_deref()
143    }
144    fn user_id(&self) -> Cow<'_, str> {
145        Cow::Borrowed(&self.user_id)
146    }
147    fn impersonated_by(&self) -> Option<&str> {
148        self.impersonated_by.as_deref()
149    }
150    fn active_organization_id(&self) -> Option<&str> {
151        self.active_organization_id.as_deref()
152    }
153    fn active_team_id(&self) -> Option<&str> {
154        self.active_team_id.as_deref()
155    }
156    fn active(&self) -> bool {
157        self.active
158    }
159}