openauth-passkey 0.0.5

Server-side passkey plugin for OpenAuth.
Documentation
use openauth_core::crypto::random::generate_random_string;
use openauth_core::db::DbAdapter;
use openauth_core::error::OpenAuthError;
use openauth_core::verification::{CreateVerificationInput, DbVerificationStore};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use time::{Duration, OffsetDateTime};

use crate::options::PasskeyRegistrationUser;

pub const CHALLENGE_MAX_AGE_SECONDS: u64 = 60 * 5;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChallengeKind {
    Registration,
    Authentication,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChallengeValue {
    pub kind: ChallengeKind,
    pub state: Value,
    pub user: Option<PasskeyRegistrationUser>,
    pub context: Option<String>,
}

pub async fn create_challenge(
    adapter: &dyn DbAdapter,
    value: ChallengeValue,
) -> Result<String, OpenAuthError> {
    let token = generate_random_string(32);
    let expires_at =
        OffsetDateTime::now_utc() + Duration::seconds(CHALLENGE_MAX_AGE_SECONDS as i64);
    DbVerificationStore::new(adapter)
        .create_verification(CreateVerificationInput::new(
            token.clone(),
            serde_json::to_string(&value).map_err(|error| OpenAuthError::Api(error.to_string()))?,
            expires_at,
        ))
        .await?;
    Ok(token)
}

pub async fn find_challenge(
    adapter: &dyn DbAdapter,
    token: &str,
) -> Result<Option<ChallengeValue>, OpenAuthError> {
    DbVerificationStore::new(adapter)
        .find_verification(token)
        .await?
        .map(|verification| {
            serde_json::from_str::<ChallengeValue>(&verification.value)
                .map_err(|error| OpenAuthError::Api(error.to_string()))
        })
        .transpose()
}