Skip to content
Clone
main
56 lines 1.5 KB Rust
use std::time::Duration;
use crate::cache::Cache;
use crate::config::pow::PowConfig;
use crate::session::Session;
use crate::errors::{AuthError, AuthResult};
use crate::handlers::challenge::{ChallengeResponse, PowProofRequest, SESSION_CHALLENGE};
use crate::share::crypto::{now_secs, random_alnum};
pub const USED_KEY_PREFIX: &str = "auth:pow:used:";
pub async fn issue(session: &Session, config: &PowConfig) -> AuthResult<ChallengeResponse> {
let now = now_secs();
let challenge = ChallengeResponse {
context: random_alnum(32),
difficulty: config.difficulty,
issued_at: now,
expires_at: now + config.expires_secs,
};
session.set(SESSION_CHALLENGE, &challenge)?;
Ok(challenge)
}
pub async fn verify(
session: &Session,
cache: &Cache,
proof: &PowProofRequest,
expected_payload: &[u8],
) -> AuthResult<()> {
let challenge = session
.get::<ChallengeResponse>(SESSION_CHALLENGE)?
.ok_or(AuthError::Expired)?;
crate::pow::hash::verify_pow(&challenge, proof, expected_payload)?;
let remaining = challenge.expires_at.saturating_sub(now_secs()).max(1);
let used_key = used_key(&challenge.context);
let claimed = cache
.set_nx_ex(&used_key, 1, Duration::from_secs(remaining))
.await?;
if !claimed {
return Err(AuthError::Expired);
}
let _ = session.remove(SESSION_CHALLENGE);
Ok(())
}
pub fn used_key(context: &str) -> String {
format!("{USED_KEY_PREFIX}{context}")
}