matrix_sdk/authentication/
mod.rs

1// Copyright 2023 Kévin Commaille
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Types and functions related to authentication in Matrix.
16
17use std::sync::Arc;
18
19use as_variant::as_variant;
20use matrix_sdk_base::SessionMeta;
21use tokio::sync::{broadcast, Mutex, OnceCell};
22
23pub mod matrix;
24#[cfg(feature = "experimental-oidc")]
25pub mod oidc;
26
27use self::matrix::{MatrixAuth, MatrixAuthData};
28#[cfg(feature = "experimental-oidc")]
29use self::oidc::{Oidc, OidcAuthData, OidcCtx};
30use crate::{Client, RefreshTokenError, SessionChange};
31
32#[cfg(all(feature = "experimental-oidc", feature = "e2e-encryption", not(target_arch = "wasm32")))]
33pub mod qrcode;
34
35/// Session tokens, for any kind of authentication.
36#[allow(missing_debug_implementations, clippy::large_enum_variant)]
37pub enum SessionTokens {
38    /// Tokens for a [`matrix`] session.
39    Matrix(matrix::MatrixSessionTokens),
40    #[cfg(feature = "experimental-oidc")]
41    /// Tokens for an [`oidc`] session.
42    Oidc(oidc::OidcSessionTokens),
43}
44
45pub(crate) type SessionCallbackError = Box<dyn std::error::Error + Send + Sync>;
46pub(crate) type SaveSessionCallback =
47    dyn Fn(Client) -> Result<(), SessionCallbackError> + Send + Sync;
48pub(crate) type ReloadSessionCallback =
49    dyn Fn(Client) -> Result<SessionTokens, SessionCallbackError> + Send + Sync;
50
51/// All the data relative to authentication, and that must be shared between a
52/// client and all its children.
53pub(crate) struct AuthCtx {
54    #[cfg(feature = "experimental-oidc")]
55    pub(crate) oidc: OidcCtx,
56
57    /// Whether to try to refresh the access token automatically when an
58    /// `M_UNKNOWN_TOKEN` error is encountered.
59    pub(crate) handle_refresh_tokens: bool,
60
61    /// Lock making sure we're only doing one token refresh at a time.
62    pub(crate) refresh_token_lock: Arc<Mutex<Result<(), RefreshTokenError>>>,
63
64    /// Session change publisher. Allows the subscriber to handle changes to the
65    /// session such as logging out when the access token is invalid or
66    /// persisting updates to the access/refresh tokens.
67    pub(crate) session_change_sender: broadcast::Sender<SessionChange>,
68
69    /// Authentication data to keep in memory.
70    pub(crate) auth_data: OnceCell<AuthData>,
71
72    /// A callback called whenever we need an absolute source of truth for the
73    /// current session tokens.
74    ///
75    /// This is required only in multiple processes setups.
76    pub(crate) reload_session_callback: OnceCell<Box<ReloadSessionCallback>>,
77
78    /// A callback to save a session back into the app's secure storage.
79    ///
80    /// This is always called, independently of the presence of a cross-process
81    /// lock.
82    ///
83    /// Internal invariant: this must be called only after `set_session_tokens`
84    /// has been called, not before.
85    pub(crate) save_session_callback: OnceCell<Box<SaveSessionCallback>>,
86}
87
88/// An enum over all the possible authentication APIs.
89#[derive(Debug, Clone)]
90#[non_exhaustive]
91pub enum AuthApi {
92    /// The native Matrix authentication API.
93    Matrix(MatrixAuth),
94
95    /// The OpenID Connect API.
96    #[cfg(feature = "experimental-oidc")]
97    Oidc(Oidc),
98}
99
100/// A user session using one of the available authentication APIs.
101#[derive(Debug, Clone)]
102#[non_exhaustive]
103pub enum AuthSession {
104    /// A session using the native Matrix authentication API.
105    Matrix(matrix::MatrixSession),
106
107    /// A session using the OpenID Connect API.
108    #[cfg(feature = "experimental-oidc")]
109    Oidc(Box<oidc::OidcSession>),
110}
111
112impl AuthSession {
113    /// Get the matrix user information of this session.
114    pub fn meta(&self) -> &SessionMeta {
115        match self {
116            AuthSession::Matrix(session) => &session.meta,
117            #[cfg(feature = "experimental-oidc")]
118            AuthSession::Oidc(session) => &session.user.meta,
119        }
120    }
121
122    /// Take the matrix user information of this session.
123    pub fn into_meta(self) -> SessionMeta {
124        match self {
125            AuthSession::Matrix(session) => session.meta,
126            #[cfg(feature = "experimental-oidc")]
127            AuthSession::Oidc(session) => session.user.meta,
128        }
129    }
130
131    /// Get the access token of this session.
132    pub fn access_token(&self) -> &str {
133        match self {
134            AuthSession::Matrix(session) => &session.tokens.access_token,
135            #[cfg(feature = "experimental-oidc")]
136            AuthSession::Oidc(session) => &session.user.tokens.access_token,
137        }
138    }
139
140    /// Get the refresh token of this session.
141    pub fn get_refresh_token(&self) -> Option<&str> {
142        match self {
143            AuthSession::Matrix(session) => session.tokens.refresh_token.as_deref(),
144            #[cfg(feature = "experimental-oidc")]
145            AuthSession::Oidc(session) => session.user.tokens.refresh_token.as_deref(),
146        }
147    }
148}
149
150impl From<matrix::MatrixSession> for AuthSession {
151    fn from(session: matrix::MatrixSession) -> Self {
152        Self::Matrix(session)
153    }
154}
155
156#[cfg(feature = "experimental-oidc")]
157impl From<oidc::OidcSession> for AuthSession {
158    fn from(session: oidc::OidcSession) -> Self {
159        Self::Oidc(session.into())
160    }
161}
162
163/// Data for an authentication API.
164#[derive(Debug)]
165pub(crate) enum AuthData {
166    /// Data for the native Matrix authentication API.
167    Matrix(MatrixAuthData),
168    /// Data for the OpenID Connect API.
169    #[cfg(feature = "experimental-oidc")]
170    Oidc(OidcAuthData),
171}
172
173impl AuthData {
174    pub(crate) fn as_matrix(&self) -> Option<&MatrixAuthData> {
175        as_variant!(self, Self::Matrix)
176    }
177
178    pub(crate) fn access_token(&self) -> Option<String> {
179        let token = match self {
180            Self::Matrix(d) => d.tokens.get().access_token,
181            #[cfg(feature = "experimental-oidc")]
182            Self::Oidc(d) => d.tokens.get()?.get().access_token,
183        };
184
185        Some(token)
186    }
187}