alibi_sqlx/store/
oauth_token_conversion.rs1use super::SqlxStore;
2use crate::{
3 model::SqlxModel,
4 pool::Exec,
5 schema::{AuthSchema, SqlxAccountModel},
6 sql::Sql,
7};
8use alibi_core::{
9 AuthError, AuthResult,
10 oauth_token_conversion::{OAuthTokenConversionStore, OAuthTokenSnapshot, OAuthTokenValues},
11};
12use async_trait::async_trait;
13
14#[async_trait]
15impl<S> OAuthTokenConversionStore<S> for SqlxStore<S>
16where
17 S: AuthSchema + Send + Sync,
18 S::Account: SqlxAccountModel,
19{
20 async fn compare_and_swap_oauth_tokens(
21 &self,
22 observed: &OAuthTokenSnapshot,
23 replacement: &OAuthTokenValues,
24 ) -> AuthResult<bool> {
25 let columns = S::Account::oauth_token_columns().ok_or_else(|| {
26 AuthError::NotImplemented("Account token conversion columns are not configured".into())
27 })?;
28 let table = <S::Account as SqlxModel>::TABLE;
29 let mut sql = Sql::with(self.exec().engine(), "UPDATE ");
30 sql.ident(table);
31 sql.push(" SET ");
32 for (index, (column, value)) in columns
33 .iter()
34 .zip([
35 &replacement.access_token,
36 &replacement.refresh_token,
37 &replacement.id_token,
38 ])
39 .enumerate()
40 {
41 if index > 0 {
42 sql.push(", ");
43 }
44 sql.assign(column, value.clone());
45 }
46 sql.push(" WHERE ");
47 drop(S::Account::parse_id(&observed.id)?);
50 drop(S::Account::parse_user_id(&observed.user_id)?);
51 for (index, (column, value)) in [
52 (S::Account::id_column(), &observed.id),
53 (S::Account::user_id_column(), &observed.user_id),
54 (S::Account::provider_id_column(), &observed.provider_id),
55 (S::Account::account_id_column(), &observed.account_id),
56 ]
57 .into_iter()
58 .enumerate()
59 {
60 if index > 0 {
61 sql.push(" AND ");
62 }
63 sql.push("CAST(");
64 sql.column(table, column);
65 sql.push(" AS TEXT)");
66 exact_collation(&mut sql);
67 sql.push(" = ");
68 sql.bind(value.clone());
69 }
70 for (column, value) in columns.iter().zip([
71 &observed.tokens.access_token,
72 &observed.tokens.refresh_token,
73 &observed.tokens.id_token,
74 ]) {
75 sql.push(" AND ");
76 sql.column(table, column);
78 exact_collation(&mut sql);
79 if let Some(value) = value {
80 sql.push(" = ");
81 sql.bind(value.clone());
82 } else {
83 sql.push(" IS NULL");
84 }
85 }
86 self.in_transaction(true, async move |tx| {
87 Ok(Exec::Tx(tx).execute(sql).await? == 1)
88 })
89 .await
90 }
91}
92
93fn exact_collation(sql: &mut Sql) {
94 match sql.engine() {
95 crate::pool::Engine::Sqlite => sql.push(" COLLATE BINARY"),
96 crate::pool::Engine::Postgres => sql.push(" COLLATE \"C\""),
97 }
98}