mas_storage/personal/access_token.rs
1// Copyright 2025, 2026 Element Creations Ltd.
2// Copyright 2025 New Vector Ltd.
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 chrono::Duration;
9use mas_data_model::{
10 Clock,
11 personal::{PersonalAccessToken, session::PersonalSession},
12};
13use rand_core::Rng;
14use ulid::Ulid;
15
16use crate::repository_impl;
17
18/// An [`PersonalAccessTokenRepository`] helps interacting with
19/// [`PersonalAccessToken`] saved in the storage backend
20#[async_trait]
21pub trait PersonalAccessTokenRepository: Send + Sync {
22 /// The error type returned by the repository
23 type Error;
24
25 /// Lookup an access token by its ID
26 ///
27 /// Returns the access token if it exists, `None` otherwise
28 ///
29 /// # Parameters
30 ///
31 /// * `id`: The ID of the access token to lookup
32 ///
33 /// # Errors
34 ///
35 /// Returns [`Self::Error`] if the underlying repository fails
36 async fn lookup(&mut self, id: Ulid) -> Result<Option<PersonalAccessToken>, Self::Error>;
37
38 /// Find an access token by its token
39 ///
40 /// Returns the access token if it exists, `None` otherwise
41 ///
42 /// # Parameters
43 ///
44 /// * `access_token`: The token of the access token to lookup
45 ///
46 /// # Errors
47 ///
48 /// Returns [`Self::Error`] if the underlying repository fails
49 async fn find_by_token(
50 &mut self,
51 access_token: &str,
52 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
53
54 /// Find the active access token belonging to a given session.
55 ///
56 /// Returns the active access token if it exists, `None` otherwise
57 ///
58 /// # Parameters
59 ///
60 /// * `session`: The session to lookup
61 ///
62 /// # Errors
63 ///
64 /// Returns [`Self::Error`] if the underlying repository fails
65 async fn find_active_for_session(
66 &mut self,
67 session: &PersonalSession,
68 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
69
70 /// Add a new access token to the database
71 ///
72 /// Returns the newly created access token
73 ///
74 /// # Parameters
75 ///
76 /// * `rng`: A random number generator
77 /// * `clock`: The clock used to generate timestamps
78 /// * `session`: The session the access token is associated with
79 /// * `access_token`: The access token to add
80 /// * `expires_after`: The duration after which the access token expires. If
81 /// [`None`] the access token never expires
82 ///
83 /// # Errors
84 ///
85 /// Returns [`Self::Error`] if the underlying repository fails
86 async fn add(
87 &mut self,
88 rng: &mut (dyn Rng + Send),
89 clock: &dyn Clock,
90 session: &PersonalSession,
91 access_token: &str,
92 expires_after: Option<Duration>,
93 ) -> Result<PersonalAccessToken, Self::Error>;
94
95 /// Revoke an access token
96 ///
97 /// Returns the revoked access token
98 ///
99 /// # Parameters
100 ///
101 /// * `clock`: The clock used to generate timestamps
102 /// * `access_token`: The access token to revoke
103 ///
104 /// # Errors
105 ///
106 /// Returns [`Self::Error`] if the underlying repository fails
107 async fn revoke(
108 &mut self,
109 clock: &dyn Clock,
110 access_token: PersonalAccessToken,
111 ) -> Result<PersonalAccessToken, Self::Error>;
112}
113
114repository_impl!(PersonalAccessTokenRepository:
115 async fn lookup(&mut self, id: Ulid) -> Result<Option<PersonalAccessToken>, Self::Error>;
116
117 async fn find_by_token(
118 &mut self,
119 access_token: &str,
120 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
121
122 async fn find_active_for_session(
123 &mut self,
124 session: &PersonalSession,
125 ) -> Result<Option<PersonalAccessToken>, Self::Error>;
126
127 async fn add(
128 &mut self,
129 rng: &mut (dyn Rng + Send),
130 clock: &dyn Clock,
131 session: &PersonalSession,
132 access_token: &str,
133 expires_after: Option<Duration>,
134 ) -> Result<PersonalAccessToken, Self::Error>;
135
136 async fn revoke(
137 &mut self,
138 clock: &dyn Clock,
139 access_token: PersonalAccessToken,
140 ) -> Result<PersonalAccessToken, Self::Error>;
141);