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