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 claim_oauth_client_assertion(
57 &self,
58 id: &str,
59 expires_at: DateTime<Utc>,
60 ) -> AuthResult<bool> {
61 let _ = (id, expires_at);
62 Err(AuthError::config(
63 "OAuth assertion persistence is not supported by this store",
64 ))
65 }
66
67 async fn store_oauth_refresh_replay(&self, id: &str, ciphertext: &str) -> AuthResult<()> {
69 let _ = (id, ciphertext);
70 Err(AuthError::config(
71 "OAuth token persistence is not supported by this store",
72 ))
73 }
74 async fn claim_oauth_refresh_rotation_by_id(
76 &self,
77 id: &str,
78 at: DateTime<Utc>,
79 replay_expires: Option<DateTime<Utc>>,
80 ) -> AuthResult<bool> {
81 let _ = (id, at, replay_expires);
82 Err(AuthError::config(
83 "OAuth refresh rotation by ID is not supported by this store",
84 ))
85 }
86 async fn claim_oauth_refresh_rotation(
87 &self,
88 _token: &str,
89 _at: DateTime<Utc>,
90 _replay_expires: Option<DateTime<Utc>>,
91 ) -> AuthResult<bool> {
92 Err(AuthError::config(
93 "OAuth token persistence is not supported by this store",
94 ))
95 }
96
97 async fn list_oauth_tokens_for_session(
98 &self,
99 _kind: OAuthTokenKind,
100 _session_id: &str,
101 ) -> AuthResult<Vec<OAuthToken>> {
102 Err(AuthError::config(
103 "OAuth token persistence is not supported by this store",
104 ))
105 }
106 async fn mark_oauth_token_revoked(
107 &self,
108 _kind: OAuthTokenKind,
109 _id: &str,
110 _at: DateTime<Utc>,
111 ) -> AuthResult<()> {
112 Err(AuthError::config(
113 "OAuth token persistence is not supported by this store",
114 ))
115 }
116
117 async fn delete_oauth_token(&self, _kind: OAuthTokenKind, _token: &str) -> AuthResult<()> {
118 Err(AuthError::config(
119 "OAuth token persistence is not supported by this store",
120 ))
121 }
122 async fn revoke_oauth_refresh_token(
124 &self,
125 _token: &str,
126 _revoked_at: DateTime<Utc>,
127 ) -> AuthResult<bool> {
128 Err(AuthError::config(
129 "OAuth token persistence is not supported by this store",
130 ))
131 }
132 async fn delete_oauth_access_tokens_for_refresh(&self, _refresh_id: &str) -> AuthResult<()> {
133 Err(AuthError::config(
134 "OAuth token persistence is not supported by this store",
135 ))
136 }
137 async fn delete_oauth_refresh_family(
138 &self,
139 _client_id: &str,
140 _user_id: &str,
141 ) -> AuthResult<()> {
142 Err(AuthError::config(
143 "OAuth token persistence is not supported by this store",
144 ))
145 }
146 async fn create_oauth_token(
147 &self,
148 _kind: OAuthTokenKind,
149 _data: CreateOAuthToken,
150 ) -> AuthResult<OAuthToken> {
151 Err(AuthError::config(
152 "OAuth token persistence is not supported by this store",
153 ))
154 }
155 async fn get_oauth_token(
156 &self,
157 _kind: OAuthTokenKind,
158 _token: &str,
159 ) -> AuthResult<Option<OAuthToken>> {
160 Err(AuthError::config(
161 "OAuth token persistence is not supported by this store",
162 ))
163 }
164 async fn delete_oauth_tokens_for_code(&self, _code: &str) -> AuthResult<()> {
165 Err(AuthError::config(
166 "OAuth token persistence is not supported by this store",
167 ))
168 }
169
170 async fn delete_oauth_client_resource(
171 &self,
172 _client_id: &str,
173 _resource_id: &str,
174 ) -> AuthResult<()> {
175 Err(AuthError::config(
176 "OAuth resource linkage is not supported by this store",
177 ))
178 }
179 async fn create_oauth_client_resource(
180 &self,
181 _data: CreateOAuthClientResource,
182 ) -> AuthResult<OAuthClientResource> {
183 Err(AuthError::config(
184 "OAuth resource linkage is not supported by this store",
185 ))
186 }
187 async fn delete_oauth_resource(&self, _identifier: &str) -> AuthResult<()> {
188 Err(AuthError::config(
189 "OAuth resource persistence is not supported by this store",
190 ))
191 }
192 async fn update_oauth_resource(
193 &self,
194 _identifier: &str,
195 _metadata: Value,
196 _updated_at: DateTime<Utc>,
197 ) -> AuthResult<Option<OAuthResource>> {
198 Err(AuthError::config(
199 "OAuth resource persistence is not supported by this store",
200 ))
201 }
202 async fn list_oauth_resources(&self) -> AuthResult<Vec<OAuthResource>> {
203 Err(AuthError::config(
204 "OAuth resource persistence is not supported by this store",
205 ))
206 }
207 async fn list_oauth_client_resources(
208 &self,
209 _client_id: &str,
210 ) -> AuthResult<Vec<OAuthClientResource>> {
211 Err(AuthError::config(
212 "OAuth resource linkage is not supported by this store",
213 ))
214 }
215 async fn create_oauth_resource(&self, _data: CreateOAuthResource) -> AuthResult<OAuthResource> {
216 Err(AuthError::config(
217 "OAuth resource persistence is not supported by this store",
218 ))
219 }
220 async fn get_oauth_resource(&self, _identifier: &str) -> AuthResult<Option<OAuthResource>> {
221 Err(AuthError::config(
222 "OAuth resource persistence is not supported by this store",
223 ))
224 }
225 async fn delete_oauth_consent(&self, _id: &str) -> AuthResult<()> {
226 Err(AuthError::config(
227 "OAuth consent persistence is not supported by this store",
228 ))
229 }
230 async fn get_oauth_consent(&self, _id: &str) -> AuthResult<Option<OAuthConsent>> {
231 Err(AuthError::config(
232 "OAuth consent persistence is not supported by this store",
233 ))
234 }
235 async fn create_oauth_consent(&self, _data: CreateOAuthConsent) -> AuthResult<OAuthConsent> {
236 Err(AuthError::config(
237 "OAuth consent persistence is not supported by this store",
238 ))
239 }
240 async fn list_oauth_consents_by_user(&self, _user_id: &str) -> AuthResult<Vec<OAuthConsent>> {
241 Err(AuthError::config(
242 "OAuth consent persistence is not supported by this store",
243 ))
244 }
245 async fn update_oauth_consent_metadata(
246 &self,
247 _id: &str,
248 _metadata: Value,
249 _updated_at: DateTime<Utc>,
250 ) -> AuthResult<Option<OAuthConsent>> {
251 Err(AuthError::config(
252 "OAuth consent persistence is not supported by this store",
253 ))
254 }
255 async fn create_oauth_client(&self, _data: CreateOAuthClient) -> AuthResult<OAuthClient> {
256 Err(AuthError::config(
257 "OAuth client persistence is not supported by this store",
258 ))
259 }
260 async fn get_oauth_client(&self, _client_id: &str) -> AuthResult<Option<OAuthClient>> {
261 Err(AuthError::config(
262 "OAuth client persistence is not supported by this store",
263 ))
264 }
265 async fn list_oauth_clients_by_user(&self, _user_id: &str) -> AuthResult<Vec<OAuthClient>> {
266 Err(AuthError::config(
267 "OAuth client persistence is not supported by this store",
268 ))
269 }
270 async fn delete_oauth_client(&self, _client_id: &str) -> AuthResult<()> {
271 Err(AuthError::config(
272 "OAuth client persistence is not supported by this store",
273 ))
274 }
275 async fn update_oauth_client_secret(
276 &self,
277 _client_id: &str,
278 _secret: String,
279 _updated_at: DateTime<Utc>,
280 ) -> AuthResult<Option<OAuthClient>> {
281 Err(AuthError::config(
282 "OAuth client persistence is not supported by this store",
283 ))
284 }
285 async fn update_oauth_client_metadata(
286 &self,
287 _client_id: &str,
288 _metadata: Value,
289 _updated_at: DateTime<Utc>,
290 ) -> AuthResult<Option<OAuthClient>> {
291 Err(AuthError::config(
292 "OAuth client persistence is not supported by this store",
293 ))
294 }
295}
296
297const CLIENT_FIELDS: &[(&str, &str)] = &[
299 ("client_discovery_id", "clientDiscoveryId"),
300 ("disabled", "disabled"),
301 ("skip_consent", "skipConsent"),
302 ("enable_end_session", "enableEndSession"),
303 ("subject_type", "subjectType"),
304 ("client_credentials_scopes", "clientCredentialsScopes"),
305 ("contacts", "contacts"),
306 ("software_id", "softwareId"),
307 ("software_version", "softwareVersion"),
308 ("software_statement", "softwareStatement"),
309 ("redirect_uris", "redirectUris"),
310 ("post_logout_redirect_uris", "postLogoutRedirectUris"),
311 ("backchannel_logout_uri", "backchannelLogoutUri"),
312 (
313 "backchannel_logout_session_required",
314 "backchannelLogoutSessionRequired",
315 ),
316 ("token_endpoint_auth_method", "tokenEndpointAuthMethod"),
317 ("application_type", "applicationType"),
318 ("jwks", "jwks"),
319 ("jwks_uri", "jwksUri"),
320 ("grant_types", "grantTypes"),
321 ("response_types", "responseTypes"),
322 ("require_pkce", "requirePKCE"),
323 ("dpop_bound_access_tokens", "dpopBoundAccessTokens"),
324 ("reference_id", "referenceId"),
325 ("client_name", "name"),
326 ("client_uri", "uri"),
327 ("logo_uri", "icon"),
328 ("tos_uri", "tos"),
329 ("policy_uri", "policy"),
330 ("scope", "scopes"),
331];
332
333pub fn metadata_to_columns(metadata: Value) -> AuthResult<Value> {
337 let Value::Object(mut metadata) = metadata else {
338 return Err(AuthError::bad_request(
339 "OAuth client metadata must be an object",
340 ));
341 };
342 let mut fields = serde_json::Map::new();
343 for (rfc, column) in CLIENT_FIELDS {
344 if let Some(mut value) = metadata.remove(*rfc) {
345 if *rfc == "scope"
346 && let Some(scope) = value.as_str()
347 {
348 value = Value::Array(
349 scope
350 .split_whitespace()
351 .map(|scope| Value::String(scope.into()))
352 .collect(),
353 );
354 }
355 if *rfc == "jwks" && value.is_object() {
356 value = Value::String(serde_json::to_string(&value)?);
357 }
358 _ = fields.insert((*column).into(), value);
359 }
360 }
361 if !fields.contains_key("redirectUris") {
362 _ = fields.insert("redirectUris".into(), serde_json::json!([]));
363 }
364 _ = fields.insert(
365 "metadata".into(),
366 if metadata.is_empty() {
367 Value::Null
368 } else {
369 Value::Object(metadata)
370 },
371 );
372 Ok(Value::Object(fields))
373}
374
375pub fn metadata_from_columns(fields: &Value) -> AuthResult<Value> {
379 let fields = fields
380 .as_object()
381 .ok_or_else(|| AuthError::internal("Stored OAuth client must be an object"))?;
382 let mut metadata = fields
383 .get("metadata")
384 .and_then(Value::as_object)
385 .cloned()
386 .unwrap_or_default();
387 for (rfc, column) in CLIENT_FIELDS {
388 if let Some(mut value) = fields
389 .get(*column)
390 .filter(|value| !value.is_null())
391 .cloned()
392 {
393 if *rfc == "scope"
394 && let Some(scopes) = value.as_array()
395 {
396 value = Value::String(
397 scopes
398 .iter()
399 .filter_map(Value::as_str)
400 .collect::<Vec<_>>()
401 .join(" "),
402 );
403 }
404 if *rfc == "jwks"
405 && let Some(jwks) = value.as_str()
406 {
407 value = serde_json::from_str(jwks)?;
408 }
409 _ = metadata.insert((*rfc).into(), value);
410 }
411 }
412 Ok(Value::Object(metadata))
413}
414
415#[derive(Clone, Debug)]
417pub struct OAuthConsent {
418 pub id: String,
419 pub client_id: String,
420 pub user_id: Option<String>,
421 pub metadata: Value,
422 pub created_at: DateTime<Utc>,
423 pub updated_at: DateTime<Utc>,
424}
425#[derive(Clone, Debug)]
426pub struct CreateOAuthConsent {
427 pub client_id: String,
428 pub user_id: Option<String>,
429 pub metadata: Value,
430 pub created_at: DateTime<Utc>,
431}
432
433#[derive(Clone, Debug)]
434pub struct OAuthResource {
435 pub id: String,
436 pub identifier: String,
437 pub metadata: Value,
438 pub created_at: Option<DateTime<Utc>>,
439 pub updated_at: Option<DateTime<Utc>>,
440}
441#[derive(Clone, Debug)]
442pub struct CreateOAuthResource {
443 pub identifier: String,
444 pub metadata: Value,
445 pub created_at: DateTime<Utc>,
446}
447
448#[derive(Clone, Debug)]
449pub struct OAuthClientResource {
450 pub id: String,
451 pub client_id: String,
452 pub resource_id: String,
453 pub metadata: Value,
454 pub created_at: Option<DateTime<Utc>>,
455}
456
457#[derive(Clone, Debug)]
458pub struct CreateOAuthClientResource {
459 pub client_id: String,
460 pub resource_id: String,
461 pub created_at: DateTime<Utc>,
462}