1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use crate::{AdapterRecord, AuthAccount};
use crate::{AuthError, AuthResult, AuthSchema, CreateAccount, UpdateAccount};
use async_trait::async_trait;
#[async_trait]
pub trait AccountStore<S: AuthSchema>: Send + Sync {
/// Apply the physical token TEXT column's scalar affinity before persistence.
/// SQL adapters override this for their own boolean/numeric representation.
async fn provider_token_text(&self, value: &serde_json::Value) -> AuthResult<Option<String>> {
if value.is_null() {
return Ok(None);
}
if value.is_object() || value.is_array() {
return Err(AuthError::internal(
"Unsupported provider token SQL parameter",
));
}
crate::utils::json::JsValue::from(value.clone())
.coerce_string()
.map(Some)
.map_err(AuthError::internal)
}
/// Return a retained adapter record. The default is the physical model's
/// serialized snapshot; initialized stores apply their declared output policy.
async fn create_account_record(
&self,
create_account: CreateAccount,
) -> AuthResult<AdapterRecord<S::Account>> {
AdapterRecord::physical(self.create_account(create_account).await?)
}
/// Return a retained adapter record. The default is the physical model's
/// serialized snapshot; initialized stores apply their declared output policy.
async fn get_account_record(
&self,
provider: &str,
provider_account_id: &str,
) -> AuthResult<Option<AdapterRecord<S::Account>>> {
self.get_account(provider, provider_account_id)
.await?
.map(AdapterRecord::physical)
.transpose()
}
/// Return a retained adapter record. The default is the physical model's
/// serialized snapshot; initialized stores apply their declared output policy.
/// Retain the selected physical credential result. Initialization applies
/// output policy only to that selected row, never unrelated linked accounts.
async fn get_credential_account_record(
&self,
user_id: &str,
) -> AuthResult<Option<AdapterRecord<S::Account>>> {
self.get_user_accounts(user_id)
.await?
.into_iter()
.find(|account| {
account.provider_id() == "credential" && account.account_id() == user_id
})
.map(AdapterRecord::physical)
.transpose()
}
async fn get_user_accounts_record(
&self,
user_id: &str,
) -> AuthResult<Vec<AdapterRecord<S::Account>>> {
self.get_user_accounts(user_id)
.await?
.into_iter()
.map(AdapterRecord::physical)
.collect()
}
/// Return a retained adapter record. The default is the physical model's
/// serialized snapshot; initialized stores apply their declared output policy.
async fn update_account_record(
&self,
id: &str,
update: UpdateAccount,
) -> AuthResult<AdapterRecord<S::Account>> {
AdapterRecord::physical(self.update_account(id, update).await?)
}
async fn create_account(&self, create_account: CreateAccount) -> AuthResult<S::Account>;
/// Resolve a global provider identity only when exactly one physical row matches.
/// Duplicate rows (including duplicates owned by one user) must return
/// `DatabaseError::AmbiguousAccount`; choosing a row would make ownership arbitrary.
async fn get_account(
&self,
provider: &str,
provider_account_id: &str,
) -> AuthResult<Option<S::Account>>;
/// Return all scoped physical rows in the adapter's native order. Do not
/// select or collapse duplicate provider identities, or sort by mutable dates.
async fn get_user_accounts(&self, user_id: &str) -> AuthResult<Vec<S::Account>>;
async fn update_account(&self, id: &str, update: UpdateAccount) -> AuthResult<S::Account>;
async fn delete_account(&self, id: &str) -> AuthResult<()>;
}