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