mas_storage/oauth2/
authorization_grant.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use async_trait::async_trait;
8use mas_data_model::{AuthorizationCode, AuthorizationGrant, Client, Session};
9use oauth2_types::{requests::ResponseMode, scope::Scope};
10use rand_core::RngCore;
11use ulid::Ulid;
12use url::Url;
13
14use crate::{Clock, repository_impl};
15
16/// An [`OAuth2AuthorizationGrantRepository`] helps interacting with
17/// [`AuthorizationGrant`] saved in the storage backend
18#[async_trait]
19pub trait OAuth2AuthorizationGrantRepository: Send + Sync {
20    /// The error type returned by the repository
21    type Error;
22
23    /// Create a new authorization grant
24    ///
25    /// Returns the newly created authorization grant
26    ///
27    /// # Parameters
28    ///
29    /// * `rng`: A random number generator
30    /// * `clock`: The clock used to generate timestamps
31    /// * `client`: The client that requested the authorization grant
32    /// * `redirect_uri`: The redirect URI the client requested
33    /// * `scope`: The scope the client requested
34    /// * `code`: The authorization code used by this grant, if the `code`
35    ///   `response_type` was requested
36    /// * `state`: The state the client sent, if set
37    /// * `nonce`: The nonce the client sent, if set
38    /// * `response_mode`: The response mode the client requested
39    /// * `response_type_id_token`: Whether the `id_token` `response_type` was
40    ///   requested
41    /// * `login_hint`: The login_hint the client sent, if set
42    ///
43    /// # Errors
44    ///
45    /// Returns [`Self::Error`] if the underlying repository fails
46    #[allow(clippy::too_many_arguments)]
47    async fn add(
48        &mut self,
49        rng: &mut (dyn RngCore + Send),
50        clock: &dyn Clock,
51        client: &Client,
52        redirect_uri: Url,
53        scope: Scope,
54        code: Option<AuthorizationCode>,
55        state: Option<String>,
56        nonce: Option<String>,
57        response_mode: ResponseMode,
58        response_type_id_token: bool,
59        login_hint: Option<String>,
60    ) -> Result<AuthorizationGrant, Self::Error>;
61
62    /// Lookup an authorization grant by its ID
63    ///
64    /// Returns the authorization grant if found, `None` otherwise
65    ///
66    /// # Parameters
67    ///
68    /// * `id`: The ID of the authorization grant to lookup
69    ///
70    /// # Errors
71    ///
72    /// Returns [`Self::Error`] if the underlying repository fails
73    async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
74
75    /// Find an authorization grant by its code
76    ///
77    /// Returns the authorization grant if found, `None` otherwise
78    ///
79    /// # Parameters
80    ///
81    /// * `code`: The code of the authorization grant to lookup
82    ///
83    /// # Errors
84    ///
85    /// Returns [`Self::Error`] if the underlying repository fails
86    async fn find_by_code(&mut self, code: &str)
87    -> Result<Option<AuthorizationGrant>, Self::Error>;
88
89    /// Fulfill an authorization grant, by giving the [`Session`] that it
90    /// created
91    ///
92    /// Returns the updated authorization grant
93    ///
94    /// # Parameters
95    ///
96    /// * `clock`: The clock used to generate timestamps
97    /// * `session`: The session that was created using this authorization grant
98    /// * `authorization_grant`: The authorization grant to fulfill
99    ///
100    /// # Errors
101    ///
102    /// Returns [`Self::Error`] if the underlying repository fails
103    async fn fulfill(
104        &mut self,
105        clock: &dyn Clock,
106        session: &Session,
107        authorization_grant: AuthorizationGrant,
108    ) -> Result<AuthorizationGrant, Self::Error>;
109
110    /// Mark an authorization grant as exchanged
111    ///
112    /// Returns the updated authorization grant
113    ///
114    /// # Parameters
115    ///
116    /// * `clock`: The clock used to generate timestamps
117    /// * `authorization_grant`: The authorization grant to mark as exchanged
118    ///
119    /// # Errors
120    ///
121    /// Returns [`Self::Error`] if the underlying repository fails
122    async fn exchange(
123        &mut self,
124        clock: &dyn Clock,
125        authorization_grant: AuthorizationGrant,
126    ) -> Result<AuthorizationGrant, Self::Error>;
127}
128
129repository_impl!(OAuth2AuthorizationGrantRepository:
130    async fn add(
131        &mut self,
132        rng: &mut (dyn RngCore + Send),
133        clock: &dyn Clock,
134        client: &Client,
135        redirect_uri: Url,
136        scope: Scope,
137        code: Option<AuthorizationCode>,
138        state: Option<String>,
139        nonce: Option<String>,
140        response_mode: ResponseMode,
141        response_type_id_token: bool,
142        login_hint: Option<String>,
143    ) -> Result<AuthorizationGrant, Self::Error>;
144
145    async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
146
147    async fn find_by_code(&mut self, code: &str)
148        -> Result<Option<AuthorizationGrant>, Self::Error>;
149
150    async fn fulfill(
151        &mut self,
152        clock: &dyn Clock,
153        session: &Session,
154        authorization_grant: AuthorizationGrant,
155    ) -> Result<AuthorizationGrant, Self::Error>;
156
157    async fn exchange(
158        &mut self,
159        clock: &dyn Clock,
160        authorization_grant: AuthorizationGrant,
161    ) -> Result<AuthorizationGrant, Self::Error>;
162);