use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use rand_core::{OsRng, RngCore}; use sha2::{Digest, Sha256}; /// An opaque bearer token handed to the client exactly once at login. /// Only its hash is persisted. pub struct SessionToken(String); impl std::fmt::Debug for SessionToken { /// Redacted so tokens cannot leak through logs or panic messages. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("SessionToken()") } } impl SessionToken { pub(crate) fn generate() -> SessionToken { let mut bytes = [0u8; 32]; OsRng.fill_bytes(&mut bytes); SessionToken(URL_SAFE_NO_PAD.encode(bytes)) } /// The secret value to return to the client. Deliberately not `Display` /// so tokens cannot leak into logs by accident. pub fn secret(&self) -> &str { &self.0 } } /// Hash used as the storage key for a token. pub(crate) fn hash_token(secret: &str) -> String { let digest = Sha256::digest(secret.as_bytes()); URL_SAFE_NO_PAD.encode(digest) }