Skip to main content

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);