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