Skip to main content

alibi_sqlx/store/
oauth_clients.rs

1use super::{SqlxStore, entities::oauth_client::Model};
2use crate::{
3    model::{self, ActiveRow, SqlxModel},
4    schema::AuthSchema,
5};
6use alibi_core::{
7    AuthResult,
8    store::{CreateOAuthClient, OAuthClient, OAuthClientStore},
9};
10use async_trait::async_trait;
11#[async_trait]
12impl<S: AuthSchema> OAuthClientStore for SqlxStore<S> {
13    async fn store_oauth_refresh_replay(&self, id: &str, ciphertext: &str) -> AuthResult<()> {
14        let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
15        sql.ident("oauthRefreshToken");
16        sql.push(" SET ");
17        sql.assign("rotationReplayResponse", ciphertext);
18        sql.push(" WHERE ");
19        sql.compare("oauthRefreshToken", "id", " = ", id);
20        _ = self.exec().execute(sql).await?;
21        Ok(())
22    }
23    async fn claim_oauth_refresh_rotation(
24        &self,
25        token: &str,
26        at: chrono::DateTime<chrono::Utc>,
27        replay_expires: Option<chrono::DateTime<chrono::Utc>>,
28    ) -> AuthResult<bool> {
29        let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
30        sql.ident("oauthRefreshToken");
31        sql.push(" SET ");
32        sql.assign("revoked", at);
33        sql.push(", ");
34        sql.assign("rotatedAt", at);
35        if let Some(expires) = replay_expires {
36            sql.push(", ");
37            sql.assign("rotationReplayExpiresAt", expires);
38        }
39        sql.push(" WHERE ");
40        sql.compare("oauthRefreshToken", "token", " = ", token);
41        sql.push(" AND ");
42        sql.ident("revoked");
43        sql.push(" IS NULL");
44        Ok(self.exec().execute(sql).await? > 0)
45    }
46
47    async fn mark_oauth_token_revoked(
48        &self,
49        kind: alibi_core::store::OAuthTokenKind,
50        id: &str,
51        at: chrono::DateTime<chrono::Utc>,
52    ) -> AuthResult<()> {
53        let table = match kind {
54            alibi_core::store::OAuthTokenKind::Access => "oauthAccessToken",
55            alibi_core::store::OAuthTokenKind::Refresh => "oauthRefreshToken",
56        };
57        let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
58        sql.ident(table);
59        sql.push(" SET ");
60        sql.assign("revoked", at);
61        sql.push(" WHERE ");
62        sql.compare(table, "id", " = ", id);
63        _ = self.exec().execute(sql).await?;
64        Ok(())
65    }
66    async fn list_oauth_tokens_for_session(
67        &self,
68        kind: alibi_core::store::OAuthTokenKind,
69        session_id: &str,
70    ) -> AuthResult<Vec<alibi_core::store::OAuthToken>> {
71        match kind {
72            alibi_core::store::OAuthTokenKind::Access => {
73                let mut sql =
74                    model::select_model::<super::entities::oauth_access_token::Model>(self.exec());
75                sql.push(" WHERE ");
76                sql.compare(
77                    super::entities::oauth_access_token::Model::TABLE,
78                    "sessionId",
79                    " = ",
80                    session_id,
81                );
82                sql.push(" LIMIT ");
83                sql.bind(self.find_many_limit());
84                self.exec()
85                    .fetch_all::<super::entities::oauth_access_token::Model>(sql)
86                    .await?
87                    .into_iter()
88                    .map(TryInto::try_into)
89                    .collect()
90            }
91            alibi_core::store::OAuthTokenKind::Refresh => {
92                let mut sql =
93                    model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
94                sql.push(" WHERE ");
95                sql.compare(
96                    super::entities::oauth_refresh_token::Model::TABLE,
97                    "sessionId",
98                    " = ",
99                    session_id,
100                );
101                sql.push(" LIMIT ");
102                sql.bind(self.find_many_limit());
103                self.exec()
104                    .fetch_all::<super::entities::oauth_refresh_token::Model>(sql)
105                    .await?
106                    .into_iter()
107                    .map(TryInto::try_into)
108                    .collect()
109            }
110        }
111    }
112
113    async fn delete_oauth_token(
114        &self,
115        kind: alibi_core::store::OAuthTokenKind,
116        token: &str,
117    ) -> AuthResult<()> {
118        match kind {
119            alibi_core::store::OAuthTokenKind::Access => {
120                let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
121                sql.ident(super::entities::oauth_access_token::Model::TABLE);
122                sql.push(" WHERE ");
123                sql.compare(
124                    super::entities::oauth_access_token::Model::TABLE,
125                    "token",
126                    " = ",
127                    token,
128                );
129                _ = self.exec().execute(sql).await?;
130            }
131            alibi_core::store::OAuthTokenKind::Refresh => {
132                let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
133                sql.ident(super::entities::oauth_refresh_token::Model::TABLE);
134                sql.push(" WHERE ");
135                sql.compare(
136                    super::entities::oauth_refresh_token::Model::TABLE,
137                    "token",
138                    " = ",
139                    token,
140                );
141                _ = self.exec().execute(sql).await?;
142            }
143        }
144        Ok(())
145    }
146    async fn revoke_oauth_refresh_token(
147        &self,
148        token: &str,
149        revoked_at: chrono::DateTime<chrono::Utc>,
150    ) -> AuthResult<bool> {
151        let mut lookup =
152            model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
153        lookup.push(" WHERE ");
154        lookup.compare("oauthRefreshToken", "token", " = ", token);
155        let Some(row) = self
156            .exec()
157            .fetch_optional::<super::entities::oauth_refresh_token::Model>(lookup)
158            .await?
159        else {
160            return Ok(false);
161        };
162        if row.revoked.is_some() {
163            return Ok(false);
164        }
165        let mut sql = crate::sql::Sql::with(self.exec().engine(), "UPDATE ");
166        sql.ident("oauthRefreshToken");
167        sql.push(" SET ");
168        sql.assign("revoked", revoked_at);
169        sql.push(" WHERE ");
170        sql.compare("oauthRefreshToken", "token", " = ", token);
171        sql.push(" AND ");
172        sql.ident("revoked");
173        sql.push(" IS NULL");
174        Ok(self.exec().execute(sql).await? > 0)
175    }
176    async fn delete_oauth_access_tokens_for_refresh(&self, refresh_id: &str) -> AuthResult<()> {
177        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
178        sql.ident("oauthAccessToken");
179        sql.push(" WHERE ");
180        sql.compare("oauthAccessToken", "refreshId", " = ", refresh_id);
181        _ = self.exec().execute(sql).await?;
182        Ok(())
183    }
184    async fn delete_oauth_refresh_family(&self, client_id: &str, user_id: &str) -> AuthResult<()> {
185        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
186        sql.ident("oauthAccessToken");
187        sql.push(" WHERE ");
188        sql.ident("refreshId");
189        sql.push(" IN (SELECT id FROM ");
190        sql.ident("oauthRefreshToken");
191        sql.push(" WHERE ");
192        sql.compare("oauthRefreshToken", "clientId", " = ", client_id);
193        sql.push(" AND ");
194        sql.compare("oauthRefreshToken", "userId", " = ", user_id);
195        sql.push(" LIMIT ");
196        sql.bind(
197            i64::try_from(self.config().advanced.database.default_find_many_limit)
198                .unwrap_or(i64::MAX),
199        );
200        sql.push(")");
201        _ = self.exec().execute(sql).await?;
202        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
203        sql.ident("oauthRefreshToken");
204        sql.push(" WHERE ");
205        sql.compare("oauthRefreshToken", "clientId", " = ", client_id);
206        sql.push(" AND ");
207        sql.compare("oauthRefreshToken", "userId", " = ", user_id);
208        _ = self.exec().execute(sql).await?;
209        Ok(())
210    }
211    async fn create_oauth_token(
212        &self,
213        kind: alibi_core::store::OAuthTokenKind,
214        data: alibi_core::store::CreateOAuthToken,
215    ) -> AuthResult<alibi_core::store::OAuthToken> {
216        match kind {
217            alibi_core::store::OAuthTokenKind::Access => {
218                let mut active = ActiveRow::new();
219                active.set(
220                    "id",
221                    self.generated_id(self.exec(), "oauthAccessToken", "oauthAccessToken", "id")
222                        .await?
223                        .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
224                );
225                active.set("token", data.token);
226                active.set("clientId", data.client_id);
227                active.set("userId", data.user_id);
228                active.set("sessionId", data.session_id);
229                active.set("authorizationCodeId", data.authorization_code_id);
230                active.set("createdAt", data.created_at);
231                active.set("expiresAt", data.expires_at);
232                let fields = super::entities::oauth_access_token::Model::columns_from_metadata(
233                    data.metadata,
234                )?
235                .into_active();
236                for (column, value) in fields.present() {
237                    if ![
238                        "id",
239                        "token",
240                        "clientId",
241                        "sessionId",
242                        "userId",
243                        "authorizationCodeId",
244                        "createdAt",
245                        "expiresAt",
246                    ]
247                    .contains(&column)
248                    {
249                        active.set(column, value.clone());
250                    }
251                }
252                model::insert::<super::entities::oauth_access_token::Model>(self.exec(), &active)
253                    .await?
254                    .try_into()
255            }
256            alibi_core::store::OAuthTokenKind::Refresh => {
257                let mut active = ActiveRow::new();
258                active.set(
259                    "id",
260                    self.generated_id(self.exec(), "oauthRefreshToken", "oauthRefreshToken", "id")
261                        .await?
262                        .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
263                );
264                active.set("token", data.token);
265                active.set("clientId", data.client_id);
266                active.set("userId", data.user_id);
267                active.set("sessionId", data.session_id);
268                active.set("authorizationCodeId", data.authorization_code_id);
269                active.set("createdAt", data.created_at);
270                active.set("expiresAt", data.expires_at);
271                let fields = super::entities::oauth_refresh_token::Model::columns_from_metadata(
272                    data.metadata,
273                )?
274                .into_active();
275                for (column, value) in fields.present() {
276                    if ![
277                        "id",
278                        "token",
279                        "clientId",
280                        "sessionId",
281                        "userId",
282                        "authorizationCodeId",
283                        "createdAt",
284                        "expiresAt",
285                    ]
286                    .contains(&column)
287                    {
288                        active.set(column, value.clone());
289                    }
290                }
291                model::insert::<super::entities::oauth_refresh_token::Model>(self.exec(), &active)
292                    .await?
293                    .try_into()
294            }
295        }
296    }
297    async fn get_oauth_token(
298        &self,
299        kind: alibi_core::store::OAuthTokenKind,
300        token: &str,
301    ) -> AuthResult<Option<alibi_core::store::OAuthToken>> {
302        match kind {
303            alibi_core::store::OAuthTokenKind::Access => {
304                let mut sql =
305                    model::select_model::<super::entities::oauth_access_token::Model>(self.exec());
306                sql.push(" WHERE ");
307                sql.compare(
308                    super::entities::oauth_access_token::Model::TABLE,
309                    "token",
310                    " = ",
311                    token,
312                );
313                self.exec()
314                    .fetch_optional::<super::entities::oauth_access_token::Model>(sql)
315                    .await?
316                    .map(TryInto::try_into)
317                    .transpose()
318            }
319            alibi_core::store::OAuthTokenKind::Refresh => {
320                let mut sql =
321                    model::select_model::<super::entities::oauth_refresh_token::Model>(self.exec());
322                sql.push(" WHERE ");
323                sql.compare(
324                    super::entities::oauth_refresh_token::Model::TABLE,
325                    "token",
326                    " = ",
327                    token,
328                );
329                self.exec()
330                    .fetch_optional::<super::entities::oauth_refresh_token::Model>(sql)
331                    .await?
332                    .map(TryInto::try_into)
333                    .transpose()
334            }
335        }
336    }
337    async fn delete_oauth_tokens_for_code(&self, code: &str) -> AuthResult<()> {
338        {
339            let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
340            sql.ident(super::entities::oauth_access_token::Model::TABLE);
341            sql.push(" WHERE ");
342            sql.compare(
343                super::entities::oauth_access_token::Model::TABLE,
344                "authorizationCodeId",
345                " = ",
346                code,
347            );
348            _ = self.exec().execute(sql).await?;
349        }
350        {
351            let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
352            sql.ident(super::entities::oauth_refresh_token::Model::TABLE);
353            sql.push(" WHERE ");
354            sql.compare(
355                super::entities::oauth_refresh_token::Model::TABLE,
356                "authorizationCodeId",
357                " = ",
358                code,
359            );
360            _ = self.exec().execute(sql).await?;
361        }
362        Ok(())
363    }
364    async fn delete_oauth_client_resource(
365        &self,
366        client_id: &str,
367        resource_id: &str,
368    ) -> AuthResult<()> {
369        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
370        sql.ident(super::entities::oauth_client_resource::Model::TABLE);
371        sql.push(" WHERE ");
372        sql.compare(
373            super::entities::oauth_client_resource::Model::TABLE,
374            "clientId",
375            " = ",
376            client_id,
377        );
378        sql.push(" AND ");
379        sql.compare(
380            super::entities::oauth_client_resource::Model::TABLE,
381            "resourceId",
382            " = ",
383            resource_id,
384        );
385        self.exec().execute(sql).await.map(|_| ())
386    }
387    async fn create_oauth_client_resource(
388        &self,
389        data: alibi_core::store::CreateOAuthClientResource,
390    ) -> AuthResult<alibi_core::store::OAuthClientResource> {
391        let mut active = ActiveRow::new();
392        active.set(
393            "id",
394            self.generated_id(
395                self.exec(),
396                "oauthClientResource",
397                "oauthClientResource",
398                "id",
399            )
400            .await?
401            .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
402        );
403        active.set("clientId", data.client_id);
404        active.set("resourceId", data.resource_id);
405        active.set("metadata", Option::<String>::None);
406        active.set("createdAt", Some(data.created_at));
407        model::insert::<super::entities::oauth_client_resource::Model>(self.exec(), &active)
408            .await?
409            .try_into()
410    }
411    async fn delete_oauth_resource(&self, identifier: &str) -> AuthResult<()> {
412        let mut links = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
413        links.ident(super::entities::oauth_client_resource::Model::TABLE);
414        links.push(" WHERE ");
415        links.compare(
416            super::entities::oauth_client_resource::Model::TABLE,
417            "resourceId",
418            " = ",
419            identifier,
420        );
421        _ = self.exec().execute(links).await?;
422        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
423        sql.ident(super::entities::oauth_resource::Model::TABLE);
424        sql.push(" WHERE ");
425        sql.compare(
426            super::entities::oauth_resource::Model::TABLE,
427            "identifier",
428            " = ",
429            identifier,
430        );
431        self.exec().execute(sql).await.map(|_| ())
432    }
433    async fn update_oauth_resource(
434        &self,
435        identifier: &str,
436        metadata: serde_json::Value,
437        updated_at: chrono::DateTime<chrono::Utc>,
438    ) -> AuthResult<Option<alibi_core::store::OAuthResource>> {
439        let Some(row) = self.get_oauth_resource(identifier).await? else {
440            return Ok(None);
441        };
442        let mut active = ActiveRow::new();
443        active.set("id", row.id);
444        let fields =
445            super::entities::oauth_resource::Model::columns_from_metadata(metadata)?.into_active();
446        for (column, value) in fields.present() {
447            if !["id", "identifier", "createdAt", "updatedAt"].contains(&column) {
448                active.set(column, value.clone());
449            }
450        }
451        active.set("updatedAt", updated_at);
452        model::update::<super::entities::oauth_resource::Model>(self.exec(), &active)
453            .await?
454            .map(TryInto::try_into)
455            .transpose()
456    }
457    async fn list_oauth_resources(&self) -> AuthResult<Vec<alibi_core::store::OAuthResource>> {
458        let mut sql = model::select_model::<super::entities::oauth_resource::Model>(self.exec());
459        sql.push(" LIMIT ");
460        sql.bind(self.find_many_limit());
461        self.exec()
462            .fetch_all::<super::entities::oauth_resource::Model>(sql)
463            .await?
464            .into_iter()
465            .map(TryInto::try_into)
466            .collect()
467    }
468    async fn list_oauth_client_resources(
469        &self,
470        client_id: &str,
471    ) -> AuthResult<Vec<alibi_core::store::OAuthClientResource>> {
472        let mut sql =
473            model::select_model::<super::entities::oauth_client_resource::Model>(self.exec());
474        sql.push(" WHERE ");
475        sql.compare(
476            super::entities::oauth_client_resource::Model::TABLE,
477            "clientId",
478            " = ",
479            client_id,
480        );
481        sql.push(" LIMIT ");
482        sql.bind(self.find_many_limit());
483        self.exec()
484            .fetch_all::<super::entities::oauth_client_resource::Model>(sql)
485            .await?
486            .into_iter()
487            .map(TryInto::try_into)
488            .collect()
489    }
490    async fn create_oauth_resource(
491        &self,
492        data: alibi_core::store::CreateOAuthResource,
493    ) -> AuthResult<alibi_core::store::OAuthResource> {
494        let mut active = ActiveRow::new();
495        active.set(
496            "id",
497            self.generated_id(self.exec(), "oauthResource", "oauthResource", "id")
498                .await?
499                .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
500        );
501        active.set("identifier", data.identifier);
502        let fields = super::entities::oauth_resource::Model::columns_from_metadata(data.metadata)?
503            .into_active();
504        for (column, value) in fields.present() {
505            if !["id", "identifier", "createdAt", "updatedAt"].contains(&column) {
506                active.set(column, value.clone());
507            }
508        }
509        active.set("createdAt", data.created_at);
510        active.set("updatedAt", data.created_at);
511        model::insert::<super::entities::oauth_resource::Model>(self.exec(), &active)
512            .await?
513            .try_into()
514    }
515    async fn get_oauth_resource(
516        &self,
517        identifier: &str,
518    ) -> AuthResult<Option<alibi_core::store::OAuthResource>> {
519        let mut sql = model::select_model::<super::entities::oauth_resource::Model>(self.exec());
520        sql.push(" WHERE ");
521        sql.compare(
522            super::entities::oauth_resource::Model::TABLE,
523            "identifier",
524            " = ",
525            identifier,
526        );
527        model::limit_one(&mut sql);
528        self.exec()
529            .fetch_optional::<super::entities::oauth_resource::Model>(sql)
530            .await?
531            .map(TryInto::try_into)
532            .transpose()
533    }
534    async fn create_oauth_consent(
535        &self,
536        data: alibi_core::store::CreateOAuthConsent,
537    ) -> AuthResult<alibi_core::store::OAuthConsent> {
538        let mut active = ActiveRow::new();
539        active.set(
540            "id",
541            self.generated_id(self.exec(), "oauthConsent", "oauthConsent", "id")
542                .await?
543                .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
544        );
545        active.set("clientId", data.client_id);
546        active.set("userId", data.user_id);
547        let fields = super::entities::oauth_consent::Model::columns_from_metadata(data.metadata)?
548            .into_active();
549        for (column, value) in fields.present() {
550            if !["id", "clientId", "userId", "createdAt", "updatedAt"].contains(&column) {
551                active.set(column, value.clone());
552            }
553        }
554        active.set("createdAt", data.created_at);
555        active.set("updatedAt", data.created_at);
556        model::insert::<super::entities::oauth_consent::Model>(self.exec(), &active)
557            .await?
558            .try_into()
559    }
560    async fn list_oauth_consents_by_user(
561        &self,
562        user_id: &str,
563    ) -> AuthResult<Vec<alibi_core::store::OAuthConsent>> {
564        let mut sql = model::select_model::<super::entities::oauth_consent::Model>(self.exec());
565        sql.push(" WHERE ");
566        sql.compare(
567            super::entities::oauth_consent::Model::TABLE,
568            "userId",
569            " = ",
570            user_id,
571        );
572        sql.push(" LIMIT ");
573        sql.bind(self.find_many_limit());
574        self.exec()
575            .fetch_all::<super::entities::oauth_consent::Model>(sql)
576            .await?
577            .into_iter()
578            .map(TryInto::try_into)
579            .collect()
580    }
581    async fn update_oauth_consent_metadata(
582        &self,
583        id: &str,
584        metadata: serde_json::Value,
585        updated_at: chrono::DateTime<chrono::Utc>,
586    ) -> AuthResult<Option<alibi_core::store::OAuthConsent>> {
587        let mut active = ActiveRow::new();
588        active.set("id", id.to_owned());
589        let fields =
590            super::entities::oauth_consent::Model::columns_from_metadata(metadata)?.into_active();
591        for (column, value) in fields.present() {
592            if !["id", "clientId", "userId", "createdAt", "updatedAt"].contains(&column) {
593                active.set(column, value.clone());
594            }
595        }
596        active.set("updatedAt", updated_at);
597        model::update::<super::entities::oauth_consent::Model>(self.exec(), &active)
598            .await?
599            .map(TryInto::try_into)
600            .transpose()
601    }
602    async fn create_oauth_client(&self, data: CreateOAuthClient) -> AuthResult<OAuthClient> {
603        let mut active = ActiveRow::new();
604        active.set(
605            "id",
606            self.generated_id(self.exec(), "oauthClient", "oauthClient", "id")
607                .await?
608                .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
609        );
610        active.set("clientId", data.client_id);
611        active.set("userId", data.user_id);
612        active.set("clientSecret", data.client_secret);
613        let fields = Model::columns_from_metadata(data.metadata)?.into_active();
614        for (column, value) in fields.present() {
615            if ![
616                "id",
617                "clientId",
618                "userId",
619                "clientSecret",
620                "createdAt",
621                "updatedAt",
622            ]
623            .contains(&column)
624            {
625                active.set(column, value.clone());
626            }
627        }
628        active.set("createdAt", data.created_at);
629        active.set("updatedAt", data.created_at);
630        model::insert::<Model>(self.exec(), &active)
631            .await?
632            .try_into()
633    }
634    async fn get_oauth_consent(
635        &self,
636        id: &str,
637    ) -> AuthResult<Option<alibi_core::store::OAuthConsent>> {
638        let mut sql = model::select_model::<super::entities::oauth_consent::Model>(self.exec());
639        sql.push(" WHERE ");
640        sql.compare(
641            super::entities::oauth_consent::Model::TABLE,
642            "id",
643            " = ",
644            id,
645        );
646        model::limit_one(&mut sql);
647        self.exec()
648            .fetch_optional::<super::entities::oauth_consent::Model>(sql)
649            .await?
650            .map(TryInto::try_into)
651            .transpose()
652    }
653    async fn get_oauth_client(&self, client_id: &str) -> AuthResult<Option<OAuthClient>> {
654        let mut sql = model::select_model::<Model>(self.exec());
655        sql.push(" WHERE ");
656        sql.compare(Model::TABLE, "clientId", " = ", client_id);
657        model::limit_one(&mut sql);
658        self.exec()
659            .fetch_optional::<Model>(sql)
660            .await?
661            .map(TryInto::try_into)
662            .transpose()
663    }
664    async fn list_oauth_clients_by_user(&self, user_id: &str) -> AuthResult<Vec<OAuthClient>> {
665        let mut sql = model::select_model::<Model>(self.exec());
666        sql.push(" WHERE ");
667        sql.compare(Model::TABLE, "userId", " = ", user_id);
668        sql.push(" LIMIT ");
669        sql.bind(self.find_many_limit());
670        self.exec()
671            .fetch_all::<Model>(sql)
672            .await?
673            .into_iter()
674            .map(TryInto::try_into)
675            .collect()
676    }
677    async fn delete_oauth_consent(&self, id: &str) -> AuthResult<()> {
678        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
679        sql.ident(super::entities::oauth_consent::Model::TABLE);
680        sql.push(" WHERE ");
681        sql.compare(
682            super::entities::oauth_consent::Model::TABLE,
683            "id",
684            " = ",
685            id,
686        );
687        self.exec().execute(sql).await.map(|_| ())
688    }
689    async fn delete_oauth_client(&self, client_id: &str) -> AuthResult<()> {
690        let mut sql = crate::sql::Sql::with(self.exec().engine(), "DELETE FROM ");
691        sql.ident(Model::TABLE);
692        sql.push(" WHERE ");
693        sql.compare(Model::TABLE, "clientId", " = ", client_id);
694        self.exec().execute(sql).await.map(|_| ())
695    }
696    async fn update_oauth_client_secret(
697        &self,
698        client_id: &str,
699        secret: String,
700        updated_at: chrono::DateTime<chrono::Utc>,
701    ) -> AuthResult<Option<OAuthClient>> {
702        let Some(client) = self.get_oauth_client(client_id).await? else {
703            return Ok(None);
704        };
705        let mut active = ActiveRow::new();
706        active.set("id", client.id);
707        active.set("clientSecret", Some(secret));
708        active.set("updatedAt", updated_at);
709        model::update::<Model>(self.exec(), &active)
710            .await?
711            .map(TryInto::try_into)
712            .transpose()
713    }
714    async fn update_oauth_client_metadata(
715        &self,
716        client_id: &str,
717        metadata: serde_json::Value,
718        updated_at: chrono::DateTime<chrono::Utc>,
719    ) -> AuthResult<Option<OAuthClient>> {
720        let Some(client) = self.get_oauth_client(client_id).await? else {
721            return Ok(None);
722        };
723        let mut active = ActiveRow::new();
724        active.set("id", client.id);
725        let fields = Model::columns_from_metadata(metadata)?.into_active();
726        for (column, value) in fields.present() {
727            if ![
728                "id",
729                "clientId",
730                "userId",
731                "clientSecret",
732                "createdAt",
733                "updatedAt",
734            ]
735            .contains(&column)
736            {
737                active.set(column, value.clone());
738            }
739        }
740        active.set("updatedAt", updated_at);
741        model::update::<Model>(self.exec(), &active)
742            .await?
743            .map(TryInto::try_into)
744            .transpose()
745    }
746}