1use crate::{AuthError, AuthResult};
3use async_trait::async_trait;
4use chrono::{DateTime, Utc};
5use serde_json::Value;
6#[derive(Clone, Debug)]
7pub struct OAuthClient {
8 pub id: String,
9 pub client_id: String,
10 pub user_id: Option<String>,
11 pub client_secret: Option<String>,
12 pub metadata: Value,
13 pub created_at: Option<DateTime<Utc>>,
14 pub updated_at: Option<DateTime<Utc>>,
15}
16#[derive(Clone, Debug)]
17pub struct CreateOAuthClient {
18 pub client_id: String,
19 pub user_id: Option<String>,
20 pub client_secret: Option<String>,
21 pub metadata: Value,
22 pub created_at: DateTime<Utc>,
23}
24#[derive(Clone, Copy, Debug)]
26pub enum OAuthTokenKind {
27 Access,
28 Refresh,
29}
30#[derive(Clone, Debug)]
31pub struct OAuthToken {
32 pub id: String,
33 pub token: String,
34 pub client_id: String,
35 pub user_id: Option<String>,
36 pub session_id: Option<String>,
37 pub authorization_code_id: Option<String>,
38 pub metadata: Value,
39 pub created_at: DateTime<Utc>,
40 pub expires_at: DateTime<Utc>,
41}
42#[derive(Clone, Debug)]
43pub struct CreateOAuthToken {
44 pub token: String,
45 pub client_id: String,
46 pub user_id: Option<String>,
47 pub session_id: Option<String>,
48 pub authorization_code_id: Option<String>,
49 pub metadata: Value,
50 pub created_at: DateTime<Utc>,
51 pub expires_at: DateTime<Utc>,
52}
53#[async_trait]
54pub trait OAuthClientStore: Send + Sync {
55 async fn delete_oauth_token(&self, _kind: OAuthTokenKind, _token: &str) -> AuthResult<()> {
56 Err(AuthError::config(
57 "OAuth token persistence is not supported by this store",
58 ))
59 }
60 async fn revoke_oauth_refresh_token(
62 &self,
63 _token: &str,
64 _revoked_at: DateTime<Utc>,
65 ) -> AuthResult<bool> {
66 Err(AuthError::config(
67 "OAuth token persistence is not supported by this store",
68 ))
69 }
70 async fn delete_oauth_access_tokens_for_refresh(&self, _refresh_id: &str) -> AuthResult<()> {
71 Err(AuthError::config(
72 "OAuth token persistence is not supported by this store",
73 ))
74 }
75 async fn delete_oauth_refresh_family(
76 &self,
77 _client_id: &str,
78 _user_id: &str,
79 ) -> AuthResult<()> {
80 Err(AuthError::config(
81 "OAuth token persistence is not supported by this store",
82 ))
83 }
84 async fn create_oauth_token(
85 &self,
86 _kind: OAuthTokenKind,
87 _data: CreateOAuthToken,
88 ) -> AuthResult<OAuthToken> {
89 Err(AuthError::config(
90 "OAuth token persistence is not supported by this store",
91 ))
92 }
93 async fn get_oauth_token(
94 &self,
95 _kind: OAuthTokenKind,
96 _token: &str,
97 ) -> AuthResult<Option<OAuthToken>> {
98 Err(AuthError::config(
99 "OAuth token persistence is not supported by this store",
100 ))
101 }
102 async fn delete_oauth_tokens_for_code(&self, _code: &str) -> AuthResult<()> {
103 Err(AuthError::config(
104 "OAuth token persistence is not supported by this store",
105 ))
106 }
107
108 async fn delete_oauth_client_resource(
109 &self,
110 _client_id: &str,
111 _resource_id: &str,
112 ) -> AuthResult<()> {
113 Err(AuthError::config(
114 "OAuth resource linkage is not supported by this store",
115 ))
116 }
117 async fn create_oauth_client_resource(
118 &self,
119 _data: CreateOAuthClientResource,
120 ) -> AuthResult<OAuthClientResource> {
121 Err(AuthError::config(
122 "OAuth resource linkage is not supported by this store",
123 ))
124 }
125 async fn delete_oauth_resource(&self, _identifier: &str) -> AuthResult<()> {
126 Err(AuthError::config(
127 "OAuth resource persistence is not supported by this store",
128 ))
129 }
130 async fn update_oauth_resource(
131 &self,
132 _identifier: &str,
133 _metadata: Value,
134 _updated_at: DateTime<Utc>,
135 ) -> AuthResult<Option<OAuthResource>> {
136 Err(AuthError::config(
137 "OAuth resource persistence is not supported by this store",
138 ))
139 }
140 async fn list_oauth_resources(&self) -> AuthResult<Vec<OAuthResource>> {
141 Err(AuthError::config(
142 "OAuth resource persistence is not supported by this store",
143 ))
144 }
145 async fn list_oauth_client_resources(
146 &self,
147 _client_id: &str,
148 ) -> AuthResult<Vec<OAuthClientResource>> {
149 Err(AuthError::config(
150 "OAuth resource linkage is not supported by this store",
151 ))
152 }
153 async fn create_oauth_resource(&self, _data: CreateOAuthResource) -> AuthResult<OAuthResource> {
154 Err(AuthError::config(
155 "OAuth resource persistence is not supported by this store",
156 ))
157 }
158 async fn get_oauth_resource(&self, _identifier: &str) -> AuthResult<Option<OAuthResource>> {
159 Err(AuthError::config(
160 "OAuth resource persistence is not supported by this store",
161 ))
162 }
163 async fn delete_oauth_consent(&self, _id: &str) -> AuthResult<()> {
164 Err(AuthError::config(
165 "OAuth consent persistence is not supported by this store",
166 ))
167 }
168 async fn get_oauth_consent(&self, _id: &str) -> AuthResult<Option<OAuthConsent>> {
169 Err(AuthError::config(
170 "OAuth consent persistence is not supported by this store",
171 ))
172 }
173 async fn create_oauth_consent(&self, _data: CreateOAuthConsent) -> AuthResult<OAuthConsent> {
174 Err(AuthError::config(
175 "OAuth consent persistence is not supported by this store",
176 ))
177 }
178 async fn list_oauth_consents_by_user(&self, _user_id: &str) -> AuthResult<Vec<OAuthConsent>> {
179 Err(AuthError::config(
180 "OAuth consent persistence is not supported by this store",
181 ))
182 }
183 async fn update_oauth_consent_metadata(
184 &self,
185 _id: &str,
186 _metadata: Value,
187 _updated_at: DateTime<Utc>,
188 ) -> AuthResult<Option<OAuthConsent>> {
189 Err(AuthError::config(
190 "OAuth consent persistence is not supported by this store",
191 ))
192 }
193 async fn create_oauth_client(&self, _data: CreateOAuthClient) -> AuthResult<OAuthClient> {
194 Err(AuthError::config(
195 "OAuth client persistence is not supported by this store",
196 ))
197 }
198 async fn get_oauth_client(&self, _client_id: &str) -> AuthResult<Option<OAuthClient>> {
199 Err(AuthError::config(
200 "OAuth client persistence is not supported by this store",
201 ))
202 }
203 async fn list_oauth_clients_by_user(&self, _user_id: &str) -> AuthResult<Vec<OAuthClient>> {
204 Err(AuthError::config(
205 "OAuth client persistence is not supported by this store",
206 ))
207 }
208 async fn delete_oauth_client(&self, _client_id: &str) -> AuthResult<()> {
209 Err(AuthError::config(
210 "OAuth client persistence is not supported by this store",
211 ))
212 }
213 async fn update_oauth_client_secret(
214 &self,
215 _client_id: &str,
216 _secret: String,
217 _updated_at: DateTime<Utc>,
218 ) -> AuthResult<Option<OAuthClient>> {
219 Err(AuthError::config(
220 "OAuth client persistence is not supported by this store",
221 ))
222 }
223 async fn update_oauth_client_metadata(
224 &self,
225 _client_id: &str,
226 _metadata: Value,
227 _updated_at: DateTime<Utc>,
228 ) -> AuthResult<Option<OAuthClient>> {
229 Err(AuthError::config(
230 "OAuth client persistence is not supported by this store",
231 ))
232 }
233}
234
235const CLIENT_FIELDS: &[(&str, &str)] = &[
237 ("client_discovery_id", "clientDiscoveryId"),
238 ("disabled", "disabled"),
239 ("skip_consent", "skipConsent"),
240 ("enable_end_session", "enableEndSession"),
241 ("subject_type", "subjectType"),
242 ("client_credentials_scopes", "clientCredentialsScopes"),
243 ("contacts", "contacts"),
244 ("software_id", "softwareId"),
245 ("software_version", "softwareVersion"),
246 ("software_statement", "softwareStatement"),
247 ("redirect_uris", "redirectUris"),
248 ("post_logout_redirect_uris", "postLogoutRedirectUris"),
249 ("backchannel_logout_uri", "backchannelLogoutUri"),
250 (
251 "backchannel_logout_session_required",
252 "backchannelLogoutSessionRequired",
253 ),
254 ("token_endpoint_auth_method", "tokenEndpointAuthMethod"),
255 ("application_type", "applicationType"),
256 ("jwks", "jwks"),
257 ("jwks_uri", "jwksUri"),
258 ("grant_types", "grantTypes"),
259 ("response_types", "responseTypes"),
260 ("require_pkce", "requirePKCE"),
261 ("dpop_bound_access_tokens", "dpopBoundAccessTokens"),
262 ("reference_id", "referenceId"),
263 ("client_name", "name"),
264 ("client_uri", "uri"),
265 ("logo_uri", "icon"),
266 ("tos_uri", "tos"),
267 ("policy_uri", "policy"),
268 ("scope", "scopes"),
269];
270
271pub fn metadata_to_columns(metadata: Value) -> AuthResult<Value> {
275 let Value::Object(mut metadata) = metadata else {
276 return Err(AuthError::bad_request(
277 "OAuth client metadata must be an object",
278 ));
279 };
280 let mut fields = serde_json::Map::new();
281 for (rfc, column) in CLIENT_FIELDS {
282 if let Some(mut value) = metadata.remove(*rfc) {
283 if *rfc == "scope"
284 && let Some(scope) = value.as_str()
285 {
286 value = Value::Array(
287 scope
288 .split_whitespace()
289 .map(|scope| Value::String(scope.into()))
290 .collect(),
291 );
292 }
293 if *rfc == "jwks" && value.is_object() {
294 value = Value::String(serde_json::to_string(&value)?);
295 }
296 _ = fields.insert((*column).into(), value);
297 }
298 }
299 if !fields.contains_key("redirectUris") {
300 _ = fields.insert("redirectUris".into(), serde_json::json!([]));
301 }
302 _ = fields.insert(
303 "metadata".into(),
304 if metadata.is_empty() {
305 Value::Null
306 } else {
307 Value::Object(metadata)
308 },
309 );
310 Ok(Value::Object(fields))
311}
312
313pub fn metadata_from_columns(fields: &Value) -> AuthResult<Value> {
317 let fields = fields
318 .as_object()
319 .ok_or_else(|| AuthError::internal("Stored OAuth client must be an object"))?;
320 let mut metadata = fields
321 .get("metadata")
322 .and_then(Value::as_object)
323 .cloned()
324 .unwrap_or_default();
325 for (rfc, column) in CLIENT_FIELDS {
326 if let Some(mut value) = fields
327 .get(*column)
328 .filter(|value| !value.is_null())
329 .cloned()
330 {
331 if *rfc == "scope"
332 && let Some(scopes) = value.as_array()
333 {
334 value = Value::String(
335 scopes
336 .iter()
337 .filter_map(Value::as_str)
338 .collect::<Vec<_>>()
339 .join(" "),
340 );
341 }
342 if *rfc == "jwks"
343 && let Some(jwks) = value.as_str()
344 {
345 value = serde_json::from_str(jwks)?;
346 }
347 _ = metadata.insert((*rfc).into(), value);
348 }
349 }
350 Ok(Value::Object(metadata))
351}
352
353#[derive(Clone, Debug)]
355pub struct OAuthConsent {
356 pub id: String,
357 pub client_id: String,
358 pub user_id: Option<String>,
359 pub metadata: Value,
360 pub created_at: DateTime<Utc>,
361 pub updated_at: DateTime<Utc>,
362}
363#[derive(Clone, Debug)]
364pub struct CreateOAuthConsent {
365 pub client_id: String,
366 pub user_id: Option<String>,
367 pub metadata: Value,
368 pub created_at: DateTime<Utc>,
369}
370
371#[derive(Clone, Debug)]
372pub struct OAuthResource {
373 pub id: String,
374 pub identifier: String,
375 pub metadata: Value,
376 pub created_at: Option<DateTime<Utc>>,
377 pub updated_at: Option<DateTime<Utc>>,
378}
379#[derive(Clone, Debug)]
380pub struct CreateOAuthResource {
381 pub identifier: String,
382 pub metadata: Value,
383 pub created_at: DateTime<Utc>,
384}
385
386#[derive(Clone, Debug)]
387pub struct OAuthClientResource {
388 pub id: String,
389 pub client_id: String,
390 pub resource_id: String,
391 pub metadata: Value,
392 pub created_at: Option<DateTime<Utc>>,
393}
394
395#[derive(Clone, Debug)]
396pub struct CreateOAuthClientResource {
397 pub client_id: String,
398 pub resource_id: String,
399 pub created_at: DateTime<Utc>,
400}