alibi_plugins/passkey/
registration.rs1use alibi_core::{AuthConfig, AuthRequest, AuthResult, ContextExtensions, utils::json::JsValue};
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct PasskeyRegistrationUser {
10 pub id: String,
11 pub name: String,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub display_name: Option<String>,
14}
15
16pub struct PasskeyRegistrationContext<'a> {
19 pub request: &'a AuthRequest,
20 pub auth_config: &'a AuthConfig,
21 pub extensions: &'a ContextExtensions,
22}
23
24impl std::fmt::Debug for PasskeyRegistrationContext<'_> {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 f.debug_struct("PasskeyRegistrationContext")
27 .finish_non_exhaustive()
28 }
29}
30
31#[async_trait]
33pub trait PasskeyUserResolver: Send + Sync {
34 async fn resolve_user(
35 &self,
36 context: &PasskeyRegistrationContext<'_>,
37 requested_context: Option<&str>,
38 ) -> AuthResult<Option<PasskeyRegistrationUser>>;
39}
40
41#[derive(Debug, Clone)]
44pub struct VerifiedPasskeyRegistration {
45 pub credential_id: String,
46 pub public_key: Vec<u8>,
47 pub counter: u64,
48 pub aaguid: Option<String>,
49 pub device_type: String,
50 pub backed_up: bool,
51}
52
53#[derive(Debug, Default)]
54pub struct PasskeyRegistrationOverride {
55 pub user_id: Option<String>,
57 pub name: Option<String>,
58}
59
60#[async_trait]
61pub trait PasskeyRegistrationAfterVerification: Send + Sync {
62 async fn after_verification(
63 &self,
64 context: &PasskeyRegistrationContext<'_>,
65 verification: &VerifiedPasskeyRegistration,
66 user: &PasskeyRegistrationUser,
67 client_data: &JsValue,
68 stored_context: Option<&str>,
69 ) -> AuthResult<Option<PasskeyRegistrationOverride>>;
70}
71
72#[derive(Clone)]
73pub struct PasskeyRegistrationConfig {
74 pub extensions: Option<super::PasskeyExtensions>,
75 pub require_session: bool,
76 pub resolve_user: Option<Arc<dyn PasskeyUserResolver>>,
77 pub after_verification: Option<Arc<dyn PasskeyRegistrationAfterVerification>>,
78}
79impl Default for PasskeyRegistrationConfig {
80 fn default() -> Self {
81 Self {
82 extensions: None,
83 require_session: true,
84 resolve_user: None,
85 after_verification: None,
86 }
87 }
88}
89impl std::fmt::Debug for PasskeyRegistrationConfig {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 f.debug_struct("PasskeyRegistrationConfig")
92 .field("extensions", &self.extensions)
93 .field("require_session", &self.require_session)
94 .field("resolve_user", &self.resolve_user.is_some())
95 .field("after_verification", &self.after_verification.is_some())
96 .finish()
97 }
98}
99
100pub(super) fn trim_name(value: &str) -> &str {
101 value.trim_matches(|character| {
102 matches!(character,
103 '\u{0009}'..='\u{000D}' | '\u{0020}' | '\u{00A0}' | '\u{1680}' |
104 '\u{2000}'..='\u{200A}' | '\u{2028}' | '\u{2029}' | '\u{202F}' |
105 '\u{205F}' | '\u{3000}' | '\u{FEFF}')
106 })
107}
108
109pub(super) const fn is_application_error(error: &alibi_core::AuthError) -> bool {
111 matches!(
112 error,
113 alibi_core::AuthError::Upstream { .. } | alibi_core::AuthError::Api { .. }
114 ) || error.status_code() < 500
115}