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    /// * `locale`: The locale the detected when the user asked for the
43    ///   authorization grant
44    ///
45    /// # Errors
46    ///
47    /// Returns [`Self::Error`] if the underlying repository fails
48    #[allow(clippy::too_many_arguments)]
49    async fn add(
50        &mut self,
51        rng: &mut (dyn RngCore + Send),
52        clock: &dyn Clock,
53        client: &Client,
54        redirect_uri: Url,
55        scope: Scope,
56        code: Option<AuthorizationCode>,
57        state: Option<String>,
58        nonce: Option<String>,
59        response_mode: ResponseMode,
60        response_type_id_token: bool,
61        login_hint: Option<String>,
62        locale: Option<String>,
63    ) -> Result<AuthorizationGrant, Self::Error>;
64
65    /// Lookup an authorization grant by its ID
66    ///
67    /// Returns the authorization grant if found, `None` otherwise
68    ///
69    /// # Parameters
70    ///
71    /// * `id`: The ID of the authorization grant to lookup
72    ///
73    /// # Errors
74    ///
75    /// Returns [`Self::Error`] if the underlying repository fails
76    async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
77
78    /// Find an authorization grant by its code
79    ///
80    /// Returns the authorization grant if found, `None` otherwise
81    ///
82    /// # Parameters
83    ///
84    /// * `code`: The code of the authorization grant to lookup
85    ///
86    /// # Errors
87    ///
88    /// Returns [`Self::Error`] if the underlying repository fails
89    async fn find_by_code(&mut self, code: &str)
90    -> Result<Option<AuthorizationGrant>, Self::Error>;
91
92    /// Fulfill an authorization grant, by giving the [`Session`] that it
93    /// created
94    ///
95    /// Returns the updated authorization grant
96    ///
97    /// # Parameters
98    ///
99    /// * `clock`: The clock used to generate timestamps
100    /// * `session`: The session that was created using this authorization grant
101    /// * `authorization_grant`: The authorization grant to fulfill
102    ///
103    /// # Errors
104    ///
105    /// Returns [`Self::Error`] if the underlying repository fails
106    async fn fulfill(
107        &mut self,
108        clock: &dyn Clock,
109        session: &Session,
110        authorization_grant: AuthorizationGrant,
111    ) -> Result<AuthorizationGrant, Self::Error>;
112
113    /// Mark an authorization grant as exchanged
114    ///
115    /// Returns the updated authorization grant
116    ///
117    /// # Parameters
118    ///
119    /// * `clock`: The clock used to generate timestamps
120    /// * `authorization_grant`: The authorization grant to mark as exchanged
121    ///
122    /// # Errors
123    ///
124    /// Returns [`Self::Error`] if the underlying repository fails
125    async fn exchange(
126        &mut self,
127        clock: &dyn Clock,
128        authorization_grant: AuthorizationGrant,
129    ) -> Result<AuthorizationGrant, Self::Error>;
130}
131
132repository_impl!(OAuth2AuthorizationGrantRepository:
133    async fn add(
134        &mut self,
135        rng: &mut (dyn RngCore + Send),
136        clock: &dyn Clock,
137        client: &Client,
138        redirect_uri: Url,
139        scope: Scope,
140        code: Option<AuthorizationCode>,
141        state: Option<String>,
142        nonce: Option<String>,
143        response_mode: ResponseMode,
144        response_type_id_token: bool,
145        login_hint: Option<String>,
146        locale: Option<String>,
147    ) -> Result<AuthorizationGrant, Self::Error>;
148
149    async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
150
151    async fn find_by_code(&mut self, code: &str)
152        -> Result<Option<AuthorizationGrant>, Self::Error>;
153
154    async fn fulfill(
155        &mut self,
156        clock: &dyn Clock,
157        session: &Session,
158        authorization_grant: AuthorizationGrant,
159    ) -> Result<AuthorizationGrant, Self::Error>;
160
161    async fn exchange(
162        &mut self,
163        clock: &dyn Clock,
164        authorization_grant: AuthorizationGrant,
165    ) -> Result<AuthorizationGrant, Self::Error>;
166);