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