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