Skip to main content

alibi_plugins/passkey/
authentication.rs

1//! Trusted application policy after successful cryptographic authentication.
2use alibi_core::{AuthConfig, AuthRequest, AuthResult, ContextExtensions, utils::json::JsValue};
3use async_trait::async_trait;
4use std::sync::Arc;
5/// Verification authority: Core verifies supported typed keys; the bounded raw
6/// verifier verifies original COSE facts without manufacturing a Core result.
7#[derive(Debug, Clone)]
8pub enum AuthenticationResult {
9    Core(webauthn_rs::prelude::AuthenticationResult),
10    Raw(RawAuthenticationResult),
11}
12
13#[derive(Debug, Clone)]
14pub struct RawAuthenticationResult {
15    pub(super) credential_id: webauthn_rs::prelude::CredentialID,
16    pub(super) counter: u32,
17    pub(super) user_verified: bool,
18    pub(super) backup_eligible: bool,
19    pub(super) backup_state: bool,
20}
21
22impl AuthenticationResult {
23    pub fn cred_id(&self) -> &webauthn_rs::prelude::CredentialID {
24        match self {
25            Self::Core(result) => result.cred_id(),
26            Self::Raw(result) => &result.credential_id,
27        }
28    }
29    pub fn counter(&self) -> u32 {
30        match self {
31            Self::Core(result) => result.counter(),
32            Self::Raw(result) => result.counter,
33        }
34    }
35    pub fn user_verified(&self) -> bool {
36        match self {
37            Self::Core(result) => result.user_verified(),
38            Self::Raw(result) => result.user_verified,
39        }
40    }
41    pub fn backup_eligible(&self) -> bool {
42        match self {
43            Self::Core(result) => result.backup_eligible(),
44            Self::Raw(result) => result.backup_eligible,
45        }
46    }
47    pub fn backup_state(&self) -> bool {
48        match self {
49            Self::Core(result) => result.backup_state(),
50            Self::Raw(result) => result.backup_state,
51        }
52    }
53}
54
55/// Immutable request and application settings. Captures may hold typed services.
56pub struct PasskeyAuthenticationContext<'a> {
57    /// Original physical request, when one was supplied.
58    pub request: Option<&'a AuthRequest>,
59    /// Genuine logical input for server API calls.
60    pub endpoint: Option<&'a alibi_core::endpoint::EndpointCall>,
61    pub auth_config: &'a AuthConfig,
62    pub extensions: &'a ContextExtensions,
63}
64
65impl std::fmt::Debug for PasskeyAuthenticationContext<'_> {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        f.debug_struct("PasskeyAuthenticationContext")
68            .finish_non_exhaustive()
69    }
70}
71
72/// Actual verifier result, supplied only after the signed assertion succeeds.
73#[derive(Debug, Clone)]
74pub struct VerifiedPasskeyAuthentication {
75    pub result: AuthenticationResult,
76    pub origin: String,
77    pub rp_id: String,
78}
79
80/// Awaited before counter persistence and session creation. The client data is
81/// untrusted application input; it cannot replace the verified credential owner.
82#[async_trait]
83pub trait PasskeyAuthenticationAfterVerification: Send + Sync {
84    async fn after_verification(
85        &self,
86        context: &PasskeyAuthenticationContext<'_>,
87        verification: &VerifiedPasskeyAuthentication,
88        client_data: &JsValue,
89    ) -> AuthResult<()>;
90}
91
92#[derive(Clone, Default)]
93pub struct PasskeyAuthenticationConfig {
94    pub extensions: Option<super::PasskeyExtensions>,
95    pub after_verification: Option<Arc<dyn PasskeyAuthenticationAfterVerification>>,
96}
97impl std::fmt::Debug for PasskeyAuthenticationConfig {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        f.debug_struct("PasskeyAuthenticationConfig")
100            .field("extensions", &self.extensions)
101            .field("after_verification", &self.after_verification.is_some())
102            .finish()
103    }
104}