atproto blogging
1#[cfg(target_arch = "wasm32")]
2use gloo_storage::{LocalStorage, SessionStorage, Storage};
3use jacquard::client::SessionStoreError;
4use jacquard::oauth::authstore::ClientAuthStore;
5#[cfg(not(target_arch = "wasm32"))]
6use jacquard::oauth::authstore::MemoryAuthStore;
7use jacquard::oauth::session::{AuthRequestData, ClientSessionData};
8use jacquard::types::string::Did;
9use std::future::Future;
10#[cfg(not(target_arch = "wasm32"))]
11use std::sync::LazyLock;
12
13#[cfg(target_arch = "wasm32")]
14#[derive(Clone)]
15pub struct AuthStore;
16
17#[cfg(target_arch = "wasm32")]
18impl AuthStore {
19 pub fn new() -> Self {
20 Self
21 }
22
23 fn session_key(did: &Did<'_>, session_id: &str) -> String {
24 format!("oauth_session_{}_{}", did.as_ref(), session_id)
25 }
26
27 fn auth_req_key(state: &str) -> String {
28 format!("oauth_auth_req_{}", state)
29 }
30}
31
32#[cfg(target_arch = "wasm32")]
33impl ClientAuthStore for AuthStore {
34 fn get_session(
35 &self,
36 did: &Did<'_>,
37 session_id: &str,
38 ) -> impl Future<Output = Result<Option<ClientSessionData<'_>>, SessionStoreError>> {
39 let key = Self::session_key(did, session_id);
40 async move {
41 match LocalStorage::get::<serde_json::Value>(&key) {
42 Ok(value) => {
43 let data: ClientSessionData<'static> =
44 jacquard::from_json_value::<ClientSessionData>(value).map_err(|e| {
45 SessionStoreError::Other(format!("Deserialize error: {}", e).into())
46 })?;
47 Ok(Some(data))
48 }
49 Err(gloo_storage::errors::StorageError::KeyNotFound(_)) => Ok(None),
50 Err(e) => Err(SessionStoreError::Other(
51 format!("LocalStorage error: {}", e).into(),
52 )),
53 }
54 }
55 }
56
57 fn upsert_session(
58 &self,
59 session: ClientSessionData<'_>,
60 ) -> impl Future<Output = Result<(), SessionStoreError>> {
61 async move {
62 use jacquard::IntoStatic;
63
64 let key = Self::session_key(&session.account_did, &session.session_id);
65 let static_session = session.into_static();
66
67 let value = serde_json::to_value(&static_session)
68 .map_err(|e| SessionStoreError::Other(format!("Serialize error: {}", e).into()))?;
69
70 LocalStorage::set(&key, &value).map_err(|e| {
71 SessionStoreError::Other(format!("LocalStorage error: {}", e).into())
72 })?;
73
74 Ok(())
75 }
76 }
77
78 fn delete_session(
79 &self,
80 did: &Did<'_>,
81 session_id: &str,
82 ) -> impl Future<Output = Result<(), SessionStoreError>> {
83 let key = Self::session_key(did, session_id);
84 async move {
85 LocalStorage::delete(&key);
86 Ok(())
87 }
88 }
89
90 fn get_auth_req_info(
91 &self,
92 state: &str,
93 ) -> impl Future<Output = Result<Option<AuthRequestData<'_>>, SessionStoreError>> {
94 let key = Self::auth_req_key(state);
95 async move {
96 match LocalStorage::get::<serde_json::Value>(&key) {
97 Ok(value) => {
98 let data: AuthRequestData<'static> =
99 jacquard::from_json_value::<AuthRequestData>(value).map_err(|e| {
100 SessionStoreError::Other(format!("Deserialize error: {}", e).into())
101 })?;
102 Ok(Some(data))
103 }
104 Err(gloo_storage::errors::StorageError::KeyNotFound(err)) => {
105 tracing::debug!("gloo error: {}", err);
106 Ok(None)
107 }
108 Err(e) => Err(SessionStoreError::Other(
109 format!("SessionStorage error: {}", e).into(),
110 )),
111 }
112 }
113 }
114
115 fn save_auth_req_info(
116 &self,
117 auth_req_info: &AuthRequestData<'_>,
118 ) -> impl Future<Output = Result<(), SessionStoreError>> {
119 async move {
120 use jacquard::IntoStatic;
121
122 let key = Self::auth_req_key(&auth_req_info.state);
123 let static_info = auth_req_info.clone().into_static();
124
125 let value = serde_json::to_value(&static_info)
126 .map_err(|e| SessionStoreError::Other(format!("Serialize error: {}", e).into()))?;
127
128 LocalStorage::set(&key, &value).map_err(|e| {
129 SessionStoreError::Other(format!("SessionStorage error: {}", e).into())
130 })?;
131
132 Ok(())
133 }
134 }
135
136 fn delete_auth_req_info(
137 &self,
138 state: &str,
139 ) -> impl Future<Output = Result<(), SessionStoreError>> {
140 let key = Self::auth_req_key(state);
141 async move {
142 LocalStorage::delete(&key);
143 Ok(())
144 }
145 }
146}
147#[cfg(not(target_arch = "wasm32"))]
148use std::sync::Arc;
149
150#[cfg(not(target_arch = "wasm32"))]
151pub struct AuthStore(Arc<MemoryAuthStore>);
152
153#[cfg(not(target_arch = "wasm32"))]
154static MEM_STORE: LazyLock<Arc<MemoryAuthStore>> =
155 LazyLock::new(|| Arc::new(MemoryAuthStore::new()));
156
157#[cfg(not(target_arch = "wasm32"))]
158impl AuthStore {
159 pub fn new() -> Self {
160 Self(MEM_STORE.clone())
161 }
162}
163
164#[cfg(not(target_arch = "wasm32"))]
165impl ClientAuthStore for AuthStore {
166 fn get_session(
167 &self,
168 did: &Did<'_>,
169 session_id: &str,
170 ) -> impl Future<Output = Result<Option<ClientSessionData<'_>>, SessionStoreError>> {
171 self.0.get_session(did, session_id)
172 }
173
174 fn upsert_session(
175 &self,
176 session: ClientSessionData<'_>,
177 ) -> impl Future<Output = Result<(), SessionStoreError>> {
178 self.0.upsert_session(session)
179 }
180
181 fn delete_session(
182 &self,
183 did: &Did<'_>,
184 session_id: &str,
185 ) -> impl Future<Output = Result<(), SessionStoreError>> {
186 self.0.delete_session(did, session_id)
187 }
188
189 fn get_auth_req_info(
190 &self,
191 state: &str,
192 ) -> impl Future<Output = Result<Option<AuthRequestData<'_>>, SessionStoreError>> {
193 self.0.get_auth_req_info(state)
194 }
195
196 fn save_auth_req_info(
197 &self,
198 auth_req_info: &AuthRequestData<'_>,
199 ) -> impl Future<Output = Result<(), SessionStoreError>> {
200 self.0.save_auth_req_info(auth_req_info)
201 }
202
203 fn delete_auth_req_info(
204 &self,
205 state: &str,
206 ) -> impl Future<Output = Result<(), SessionStoreError>> {
207 self.0.delete_auth_req_info(state)
208 }
209}