use super::state::AccountCookiePayload;
use alibi_core::{AuthError, AuthResult, utils::jwe};
pub(super) fn encode(
secret: &str,
payload: &AccountCookiePayload,
max_age: f64,
) -> AuthResult<String> {
jwe::encode(
secret,
"better-auth-account",
&payload.wire_value()?,
max_age,
)
}
pub(super) fn decode(secret: &str, token: &str) -> AuthResult<AccountCookiePayload> {
let payload = jwe::decode_parsed(secret, "better-auth-account", token)
.map_err(|_error| AuthError::bad_request("Account not found"))?;
let snapshot = payload.as_object().cloned();
let mut account: AccountCookiePayload = serde_json::from_value(payload)
.map_err(|_error| AuthError::bad_request("Account not found"))?;
account.original = serde_json::to_value(&account)?.as_object().cloned();
account.snapshot = snapshot;
Ok(account)
}
#[cfg(test)]
mod tests {
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD as BASE64};
#[test]
fn account_cookie_accepts_pinned_encrypted_vectors_and_rejects_unauthenticated_values() {
let vectors: serde_json::Value = serde_json::from_str(include_str!(
"../../../../../tests/fixtures/oauth/account-cookie-vectors.json"
))
.unwrap();
let secret = (*(vectors)
.get("secret")
.expect("fixture contains the requested index"))
.as_str()
.unwrap();
for name in ["valid", "noKid"] {
let decoded = crate::oauth::state::decode_account_cookie_value(
&alibi_core::AuthConfig::new(secret),
(*(vectors)
.get(name)
.expect("fixture contains the requested index"))
.as_str()
.unwrap(),
)
.unwrap();
assert_eq!(
serde_json::to_value(&decoded).unwrap(),
(*(vectors)
.get("decoded")
.and_then(|expected| expected.get(name))
.expect("fixture contains the requested index"))
);
}
for name in ["wrongSalt", "wrongSecret", "expired", "gcm", "jws"] {
assert!(
crate::oauth::state::decode_account_cookie_value(
&alibi_core::AuthConfig::new(secret),
(*(vectors)
.get(name)
.expect("fixture contains the requested index"))
.as_str()
.unwrap()
)
.is_err(),
"{name} must not resolve a provider account"
);
}
let original = (*(vectors)
.get("valid")
.expect("fixture contains the requested index"))
.as_str()
.unwrap();
for segment in [0, 2, 3, 4] {
let mut parts: Vec<_> = original.split('.').map(str::to_owned).collect();
let mut bytes = BASE64
.decode(
(parts)
.get(segment)
.expect("fixture contains the requested index"),
)
.unwrap();
*bytes.first_mut().expect("decoded fixture is nonempty") ^= 1;
*parts.get_mut(segment).expect("fixture segment exists") = BASE64.encode(bytes);
assert!(
crate::oauth::state::decode_account_cookie_value(
&alibi_core::AuthConfig::new(secret),
&parts.join(".")
)
.is_err(),
"changed segment {segment} must not authenticate"
);
}
}
}