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 std::num::NonZeroU32;
8
9use async_trait::async_trait;
10use mas_data_model::{AuthorizationCode, AuthorizationGrant, Client, Session};
11use oauth2_types::{requests::ResponseMode, scope::Scope};
12use rand_core::RngCore;
13use ulid::Ulid;
14use url::Url;
15
16use crate::{Clock, repository_impl};
17
18/// An [`OAuth2AuthorizationGrantRepository`] helps interacting with
19/// [`AuthorizationGrant`] saved in the storage backend
20#[async_trait]
21pub trait OAuth2AuthorizationGrantRepository: Send + Sync {
22 /// The error type returned by the repository
23 type Error;
24
25 /// Create a new authorization grant
26 ///
27 /// Returns the newly created authorization grant
28 ///
29 /// # Parameters
30 ///
31 /// * `rng`: A random number generator
32 /// * `clock`: The clock used to generate timestamps
33 /// * `client`: The client that requested the authorization grant
34 /// * `redirect_uri`: The redirect URI the client requested
35 /// * `scope`: The scope the client requested
36 /// * `code`: The authorization code used by this grant, if the `code`
37 /// `response_type` was requested
38 /// * `state`: The state the client sent, if set
39 /// * `nonce`: The nonce the client sent, if set
40 /// * `max_age`: The maximum age since the user last authenticated, if asked
41 /// by the client
42 /// * `response_mode`: The response mode the client requested
43 /// * `response_type_id_token`: Whether the `id_token` `response_type` was
44 /// requested
45 /// * `requires_consent`: Whether the client explicitly requested consent
46 /// * `login_hint`: The login_hint the client sent, if set
47 ///
48 /// # Errors
49 ///
50 /// Returns [`Self::Error`] if the underlying repository fails
51 #[allow(clippy::too_many_arguments)]
52 async fn add(
53 &mut self,
54 rng: &mut (dyn RngCore + Send),
55 clock: &dyn Clock,
56 client: &Client,
57 redirect_uri: Url,
58 scope: Scope,
59 code: Option<AuthorizationCode>,
60 state: Option<String>,
61 nonce: Option<String>,
62 max_age: Option<NonZeroU32>,
63 response_mode: ResponseMode,
64 response_type_id_token: bool,
65 requires_consent: bool,
66 login_hint: Option<String>,
67 ) -> Result<AuthorizationGrant, Self::Error>;
68
69 /// Lookup an authorization grant by its ID
70 ///
71 /// Returns the authorization grant if found, `None` otherwise
72 ///
73 /// # Parameters
74 ///
75 /// * `id`: The ID of the authorization grant to lookup
76 ///
77 /// # Errors
78 ///
79 /// Returns [`Self::Error`] if the underlying repository fails
80 async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
81
82 /// Find an authorization grant by its code
83 ///
84 /// Returns the authorization grant if found, `None` otherwise
85 ///
86 /// # Parameters
87 ///
88 /// * `code`: The code of the authorization grant to lookup
89 ///
90 /// # Errors
91 ///
92 /// Returns [`Self::Error`] if the underlying repository fails
93 async fn find_by_code(&mut self, code: &str)
94 -> Result<Option<AuthorizationGrant>, Self::Error>;
95
96 /// Fulfill an authorization grant, by giving the [`Session`] that it
97 /// created
98 ///
99 /// Returns the updated authorization grant
100 ///
101 /// # Parameters
102 ///
103 /// * `clock`: The clock used to generate timestamps
104 /// * `session`: The session that was created using this authorization grant
105 /// * `authorization_grant`: The authorization grant to fulfill
106 ///
107 /// # Errors
108 ///
109 /// Returns [`Self::Error`] if the underlying repository fails
110 async fn fulfill(
111 &mut self,
112 clock: &dyn Clock,
113 session: &Session,
114 authorization_grant: AuthorizationGrant,
115 ) -> Result<AuthorizationGrant, Self::Error>;
116
117 /// Mark an authorization grant as exchanged
118 ///
119 /// Returns the updated authorization grant
120 ///
121 /// # Parameters
122 ///
123 /// * `clock`: The clock used to generate timestamps
124 /// * `authorization_grant`: The authorization grant to mark as exchanged
125 ///
126 /// # Errors
127 ///
128 /// Returns [`Self::Error`] if the underlying repository fails
129 async fn exchange(
130 &mut self,
131 clock: &dyn Clock,
132 authorization_grant: AuthorizationGrant,
133 ) -> Result<AuthorizationGrant, Self::Error>;
134
135 /// Unset the `requires_consent` flag on an authorization grant
136 ///
137 /// Returns the updated authorization grant
138 ///
139 /// # Parameters
140 ///
141 /// * `authorization_grant`: The authorization grant to update
142 ///
143 /// # Errors
144 ///
145 /// Returns [`Self::Error`] if the underlying repository fails
146 async fn give_consent(
147 &mut self,
148 authorization_grant: AuthorizationGrant,
149 ) -> Result<AuthorizationGrant, Self::Error>;
150}
151
152repository_impl!(OAuth2AuthorizationGrantRepository:
153 async fn add(
154 &mut self,
155 rng: &mut (dyn RngCore + Send),
156 clock: &dyn Clock,
157 client: &Client,
158 redirect_uri: Url,
159 scope: Scope,
160 code: Option<AuthorizationCode>,
161 state: Option<String>,
162 nonce: Option<String>,
163 max_age: Option<NonZeroU32>,
164 response_mode: ResponseMode,
165 response_type_id_token: bool,
166 requires_consent: bool,
167 login_hint: Option<String>,
168 ) -> Result<AuthorizationGrant, Self::Error>;
169
170 async fn lookup(&mut self, id: Ulid) -> Result<Option<AuthorizationGrant>, Self::Error>;
171
172 async fn find_by_code(&mut self, code: &str)
173 -> Result<Option<AuthorizationGrant>, Self::Error>;
174
175 async fn fulfill(
176 &mut self,
177 clock: &dyn Clock,
178 session: &Session,
179 authorization_grant: AuthorizationGrant,
180 ) -> Result<AuthorizationGrant, Self::Error>;
181
182 async fn exchange(
183 &mut self,
184 clock: &dyn Clock,
185 authorization_grant: AuthorizationGrant,
186 ) -> Result<AuthorizationGrant, Self::Error>;
187
188 async fn give_consent(
189 &mut self,
190 authorization_grant: AuthorizationGrant,
191 ) -> Result<AuthorizationGrant, Self::Error>;
192);