matrix_sdk/authentication/
mod.rs1use std::{fmt, sync::Arc};
18
19use matrix_sdk_base::{SessionMeta, locks::Mutex};
20use serde::{Deserialize, Serialize};
21use tokio::sync::{Mutex as AsyncMutex, OnceCell, broadcast};
22
23pub mod matrix;
24pub mod oauth;
25
26use self::{
27 matrix::MatrixAuth,
28 oauth::{OAuth, OAuthAuthData, OAuthCtx},
29};
30use crate::{Client, RefreshTokenError, SessionChange};
31
32#[derive(Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
34#[allow(missing_debug_implementations)]
35pub struct SessionTokens {
36 pub access_token: String,
38
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub refresh_token: Option<String>,
42}
43
44#[cfg(not(tarpaulin_include))]
45impl fmt::Debug for SessionTokens {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.debug_struct("SessionTokens").finish_non_exhaustive()
48 }
49}
50
51pub(crate) struct SessionTokensState {
53 inner: SessionTokens,
55
56 access_token_expired: bool,
63}
64
65pub(crate) type SessionCallbackError = Box<dyn std::error::Error + Send + Sync>;
66
67#[cfg(not(target_family = "wasm"))]
68pub(crate) type SaveSessionCallback =
69 dyn Fn(Client) -> Result<(), SessionCallbackError> + Send + Sync;
70#[cfg(target_family = "wasm")]
71pub(crate) type SaveSessionCallback = dyn Fn(Client) -> Result<(), SessionCallbackError>;
72
73#[cfg(not(target_family = "wasm"))]
74pub(crate) type ReloadSessionCallback =
75 dyn Fn(Client) -> Result<SessionTokens, SessionCallbackError> + Send + Sync;
76#[cfg(target_family = "wasm")]
77pub(crate) type ReloadSessionCallback =
78 dyn Fn(Client) -> Result<SessionTokens, SessionCallbackError>;
79
80pub(crate) struct AuthCtx {
83 oauth: OAuthCtx,
84
85 pub(crate) handle_refresh_tokens: bool,
88
89 refresh_token_lock: Arc<AsyncMutex<Result<(), RefreshTokenError>>>,
91
92 pub(crate) session_change_sender: broadcast::Sender<SessionChange>,
96
97 pub(crate) auth_data: OnceCell<AuthData>,
99
100 tokens: OnceCell<Mutex<SessionTokensState>>,
102
103 pub(crate) reload_session_callback: OnceCell<Box<ReloadSessionCallback>>,
108
109 pub(crate) save_session_callback: OnceCell<Box<SaveSessionCallback>>,
117}
118
119impl AuthCtx {
120 pub(crate) fn new(handle_refresh_tokens: bool, allow_insecure_oauth: bool) -> Self {
122 Self {
123 handle_refresh_tokens,
124 refresh_token_lock: Arc::new(AsyncMutex::new(Ok(()))),
125 session_change_sender: broadcast::Sender::new(1),
126 auth_data: OnceCell::default(),
127 tokens: OnceCell::default(),
128 reload_session_callback: OnceCell::default(),
129 save_session_callback: OnceCell::default(),
130 oauth: OAuthCtx::new(allow_insecure_oauth),
131 }
132 }
133
134 pub(crate) fn session_tokens(&self) -> Option<SessionTokens> {
136 Some(self.tokens.get()?.lock().inner.clone())
137 }
138
139 pub(crate) fn access_token(&self) -> Option<String> {
141 Some(self.tokens.get()?.lock().inner.access_token.clone())
142 }
143
144 pub(crate) fn has_valid_access_token(&self) -> bool {
146 self.tokens.get().is_some_and(|tokens| !tokens.lock().access_token_expired)
147 }
148
149 pub(crate) fn set_session_tokens(&self, session_tokens: SessionTokens) {
151 let session_tokens = SessionTokensState {
152 inner: session_tokens,
153 access_token_expired: false,
155 };
156
157 if let Some(tokens) = self.tokens.get() {
158 *tokens.lock() = session_tokens;
159 } else {
160 let _ = self.tokens.set(Mutex::new(session_tokens));
161 }
162 }
163
164 pub(crate) fn set_access_token_expired(&self, access_token: &str) {
169 if let Some(tokens) = self.tokens.get() {
170 let mut tokens = tokens.lock();
171
172 if tokens.inner.access_token == access_token {
173 tokens.access_token_expired = true;
174 }
175 }
176 }
177}
178
179#[derive(Debug, Clone)]
181#[non_exhaustive]
182pub enum AuthApi {
183 Matrix(MatrixAuth),
185
186 OAuth(OAuth),
188}
189
190#[derive(Debug, Clone)]
192#[non_exhaustive]
193pub enum AuthSession {
194 Matrix(matrix::MatrixSession),
196
197 OAuth(Box<oauth::OAuthSession>),
199}
200
201impl AuthSession {
202 pub fn meta(&self) -> &SessionMeta {
204 match self {
205 AuthSession::Matrix(session) => &session.meta,
206 AuthSession::OAuth(session) => &session.user.meta,
207 }
208 }
209
210 pub fn into_meta(self) -> SessionMeta {
212 match self {
213 AuthSession::Matrix(session) => session.meta,
214 AuthSession::OAuth(session) => session.user.meta,
215 }
216 }
217
218 pub fn access_token(&self) -> &str {
220 match self {
221 AuthSession::Matrix(session) => &session.tokens.access_token,
222 AuthSession::OAuth(session) => &session.user.tokens.access_token,
223 }
224 }
225
226 pub fn get_refresh_token(&self) -> Option<&str> {
228 match self {
229 AuthSession::Matrix(session) => session.tokens.refresh_token.as_deref(),
230 AuthSession::OAuth(session) => session.user.tokens.refresh_token.as_deref(),
231 }
232 }
233}
234
235impl From<matrix::MatrixSession> for AuthSession {
236 fn from(session: matrix::MatrixSession) -> Self {
237 Self::Matrix(session)
238 }
239}
240
241impl From<oauth::OAuthSession> for AuthSession {
242 fn from(session: oauth::OAuthSession) -> Self {
243 Self::OAuth(session.into())
244 }
245}
246
247#[derive(Debug)]
249pub(crate) enum AuthData {
250 Matrix,
252 OAuth(OAuthAuthData),
254}