Skip to main content

mas_storage/compat/
refresh_token.rs

1// Copyright 2025, 2026 Element Creations Ltd.
2// Copyright 2024, 2025 New Vector Ltd.
3// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
4//
5// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6// Please see LICENSE files in the repository root for full details.
7
8use async_trait::async_trait;
9use mas_data_model::{Clock, CompatAccessToken, CompatRefreshToken, CompatSession};
10use rand_core::Rng;
11use ulid::Ulid;
12
13use crate::repository_impl;
14
15/// A [`CompatRefreshTokenRepository`] helps interacting with
16/// [`CompatRefreshToken`] saved in the storage backend
17#[async_trait]
18pub trait CompatRefreshTokenRepository: Send + Sync {
19    /// The error type returned by the repository
20    type Error;
21
22    /// Lookup a compat refresh token by its ID
23    ///
24    /// Returns the compat refresh token if it exists, `None` otherwise
25    ///
26    /// # Parameters
27    ///
28    /// * `id`: The ID of the compat refresh token to lookup
29    ///
30    /// # Errors
31    ///
32    /// Returns [`Self::Error`] if the underlying repository fails
33    async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatRefreshToken>, Self::Error>;
34
35    /// Find a compat refresh token by its token
36    ///
37    /// Returns the compat refresh token if found, `None` otherwise
38    ///
39    /// # Parameters
40    ///
41    /// * `refresh_token`: The token of the compat refresh token to lookup
42    ///
43    /// # Errors
44    ///
45    /// Returns [`Self::Error`] if the underlying repository fails
46    async fn find_by_token(
47        &mut self,
48        refresh_token: &str,
49    ) -> Result<Option<CompatRefreshToken>, Self::Error>;
50
51    /// Add a new compat refresh token to the database
52    ///
53    /// Returns the newly created compat refresh token
54    ///
55    /// # Parameters
56    ///
57    /// * `rng`: The random number generator to use
58    /// * `clock`: The clock used to generate timestamps
59    /// * `compat_session`: The compat session associated with this refresh
60    ///   token
61    /// * `compat_access_token`: The compat access token created alongside this
62    ///   refresh token
63    /// * `token`: The token of the refresh token
64    async fn add(
65        &mut self,
66        rng: &mut (dyn Rng + Send),
67        clock: &dyn Clock,
68        compat_session: &CompatSession,
69        compat_access_token: &CompatAccessToken,
70        token: String,
71    ) -> Result<CompatRefreshToken, Self::Error>;
72
73    /// Consume the given compat refresh token, as well as all other refresh
74    /// tokens from the same session, except for the given successor compat
75    /// refresh token.
76    ///
77    /// The given successor refresh token will thereafter be the only valid
78    /// refresh token for the session.
79    ///
80    /// # Historical context
81    ///
82    /// When using a refresh token, we must be able to mark multiple other
83    /// refresh tokens in the same session as consumed.
84    /// This is desirable because the syn2mas migration process can import
85    /// multiple refresh tokens for one device (compat session).
86    /// But once the user uses one of those, the others should no longer
87    /// be valid.
88    ///
89    /// # Parameters
90    ///
91    /// * `clock`: The clock used to generate timestamps
92    /// * `compat_refresh_token`: The compat refresh token to consume
93    ///
94    /// # Errors
95    ///
96    /// - Returns [`Self::Error`] if the underlying repository fails
97    /// - Returns an error if `compat_refresh_token` is not valid to be
98    ///   consumed.
99    /// - Returns an error if no refresh tokens would be consumed.
100    async fn consume_and_replace(
101        &mut self,
102        clock: &dyn Clock,
103        compat_refresh_token: CompatRefreshToken,
104        successor_compat_refresh_token: &CompatRefreshToken,
105    ) -> Result<CompatRefreshToken, Self::Error>;
106}
107
108repository_impl!(CompatRefreshTokenRepository:
109    async fn lookup(&mut self, id: Ulid) -> Result<Option<CompatRefreshToken>, Self::Error>;
110
111    async fn find_by_token(
112        &mut self,
113        refresh_token: &str,
114    ) -> Result<Option<CompatRefreshToken>, Self::Error>;
115
116    async fn add(
117        &mut self,
118        rng: &mut (dyn Rng + Send),
119        clock: &dyn Clock,
120        compat_session: &CompatSession,
121        compat_access_token: &CompatAccessToken,
122        token: String,
123    ) -> Result<CompatRefreshToken, Self::Error>;
124
125    async fn consume_and_replace(
126        &mut self,
127        clock: &dyn Clock,
128        compat_refresh_token: CompatRefreshToken,
129        successor_compat_refresh_token: &CompatRefreshToken,
130    ) -> Result<CompatRefreshToken, Self::Error>;
131);