1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4use serde_json::{json, Value};
5
6use crate::webauthn::{PasskeyWebAuthnBackend, RealPasskeyWebAuthnBackend};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct PasskeyAdvancedOptions {
11 pub webauthn_challenge_cookie: String,
12}
13
14impl Default for PasskeyAdvancedOptions {
15 fn default() -> Self {
16 Self {
17 webauthn_challenge_cookie: "better-auth-passkey".to_owned(),
18 }
19 }
20}
21
22#[derive(Clone)]
24pub struct PasskeyOptions {
25 pub rp_id: Option<String>,
26 pub rp_name: Option<String>,
27 pub origin: Vec<String>,
28 pub passkey_table: String,
29 pub authenticator_selection: AuthenticatorSelection,
30 pub registration: PasskeyRegistrationOptions,
31 pub authentication: PasskeyAuthenticationOptions,
32 pub advanced: PasskeyAdvancedOptions,
33 pub backend: Arc<dyn PasskeyWebAuthnBackend>,
34}
35
36impl Default for PasskeyOptions {
37 fn default() -> Self {
38 Self {
39 rp_id: None,
40 rp_name: None,
41 origin: Vec::new(),
42 passkey_table: "passkeys".to_owned(),
43 authenticator_selection: AuthenticatorSelection::default(),
44 registration: PasskeyRegistrationOptions::default(),
45 authentication: PasskeyAuthenticationOptions::default(),
46 advanced: PasskeyAdvancedOptions::default(),
47 backend: Arc::new(RealPasskeyWebAuthnBackend),
48 }
49 }
50}
51
52impl PasskeyOptions {
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 #[must_use]
58 pub fn rp_id(mut self, rp_id: impl Into<String>) -> Self {
59 self.rp_id = Some(rp_id.into());
60 self
61 }
62
63 #[must_use]
64 pub fn rp_name(mut self, rp_name: impl Into<String>) -> Self {
65 self.rp_name = Some(rp_name.into());
66 self
67 }
68
69 #[must_use]
70 pub fn origin(mut self, origin: impl Into<String>) -> Self {
71 self.origin.push(origin.into());
72 self
73 }
74
75 #[must_use]
76 pub fn passkey_table(mut self, table: impl Into<String>) -> Self {
77 self.passkey_table = table.into();
78 self
79 }
80
81 #[must_use]
82 pub fn authenticator_selection(mut self, selection: AuthenticatorSelection) -> Self {
83 self.authenticator_selection = selection;
84 self
85 }
86
87 #[must_use]
88 pub fn registration(mut self, registration: PasskeyRegistrationOptions) -> Self {
89 self.registration = registration;
90 self
91 }
92
93 #[must_use]
94 pub fn authentication(mut self, authentication: PasskeyAuthenticationOptions) -> Self {
95 self.authentication = authentication;
96 self
97 }
98
99 #[must_use]
100 pub fn advanced(mut self, advanced: PasskeyAdvancedOptions) -> Self {
101 self.advanced = advanced;
102 self
103 }
104
105 #[must_use]
106 pub fn backend(mut self, backend: Arc<dyn PasskeyWebAuthnBackend>) -> Self {
107 self.backend = backend;
108 self
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114pub enum AuthenticatorAttachment {
115 Platform,
116 CrossPlatform,
117}
118
119impl AuthenticatorAttachment {
120 pub(crate) fn from_query(value: &str) -> Option<Self> {
121 match value {
122 "platform" => Some(Self::Platform),
123 "cross-platform" => Some(Self::CrossPlatform),
124 _ => None,
125 }
126 }
127
128 pub(crate) fn as_str(self) -> &'static str {
129 match self {
130 Self::Platform => "platform",
131 Self::CrossPlatform => "cross-platform",
132 }
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
138pub enum ResidentKeyRequirement {
139 Discouraged,
140 Preferred,
141 Required,
142}
143
144impl ResidentKeyRequirement {
145 pub(crate) fn as_str(self) -> &'static str {
146 match self {
147 Self::Discouraged => "discouraged",
148 Self::Preferred => "preferred",
149 Self::Required => "required",
150 }
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
156pub enum UserVerificationRequirement {
157 Discouraged,
158 Preferred,
159 Required,
160}
161
162impl UserVerificationRequirement {
163 pub(crate) fn as_str(self) -> &'static str {
164 match self {
165 Self::Discouraged => "discouraged",
166 Self::Preferred => "preferred",
167 Self::Required => "required",
168 }
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
174pub struct AuthenticatorSelection {
175 pub resident_key: ResidentKeyRequirement,
176 pub user_verification: UserVerificationRequirement,
177 pub authenticator_attachment: Option<AuthenticatorAttachment>,
178}
179
180impl Default for AuthenticatorSelection {
181 fn default() -> Self {
182 Self {
183 resident_key: ResidentKeyRequirement::Preferred,
184 user_verification: UserVerificationRequirement::Preferred,
185 authenticator_attachment: None,
186 }
187 }
188}
189
190impl AuthenticatorSelection {
191 pub fn new() -> Self {
192 Self::default()
193 }
194
195 #[must_use]
196 pub fn resident_key(mut self, resident_key: ResidentKeyRequirement) -> Self {
197 self.resident_key = resident_key;
198 self
199 }
200
201 #[must_use]
202 pub fn user_verification(mut self, user_verification: UserVerificationRequirement) -> Self {
203 self.user_verification = user_verification;
204 self
205 }
206
207 #[must_use]
208 pub fn authenticator_attachment(mut self, attachment: AuthenticatorAttachment) -> Self {
209 self.authenticator_attachment = Some(attachment);
210 self
211 }
212
213 pub(crate) fn with_attachment_override(
214 &self,
215 attachment: Option<AuthenticatorAttachment>,
216 ) -> Self {
217 let mut selection = self.clone();
218 if attachment.is_some() {
219 selection.authenticator_attachment = attachment;
220 }
221 selection
222 }
223
224 pub fn to_json(&self) -> Value {
225 let mut value = json!({
226 "residentKey": self.resident_key.as_str(),
227 "userVerification": self.user_verification.as_str(),
228 });
229 if let Some(attachment) = self.authenticator_attachment {
230 value["authenticatorAttachment"] = json!(attachment.as_str());
231 }
232 value
233 }
234}
235
236#[derive(Debug, Clone, PartialEq)]
238pub struct RegistrationWebAuthnOptions {
239 pub authenticator_selection: AuthenticatorSelection,
240 pub extensions: Option<Value>,
241}
242
243impl RegistrationWebAuthnOptions {
244 pub(crate) fn new(
245 authenticator_selection: AuthenticatorSelection,
246 extensions: Option<Value>,
247 ) -> Self {
248 Self {
249 authenticator_selection,
250 extensions,
251 }
252 }
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
257pub struct PasskeyRegistrationUser {
258 pub id: String,
259 pub name: String,
260 pub display_name: Option<String>,
261}
262
263impl PasskeyRegistrationUser {
264 pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
265 Self {
266 id: id.into(),
267 name: name.into(),
268 display_name: None,
269 }
270 }
271
272 #[must_use]
273 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
274 self.display_name = Some(display_name.into());
275 self
276 }
277}
278
279pub type ResolveRegistrationUser =
280 Arc<dyn Fn(ResolveRegistrationUserInput) -> Option<PasskeyRegistrationUser> + Send + Sync>;
281
282pub type AfterRegistrationVerification =
283 Arc<dyn Fn(AfterRegistrationVerificationInput) -> Option<String> + Send + Sync>;
284
285pub type AfterAuthenticationVerification =
286 Arc<dyn Fn(AfterAuthenticationVerificationInput) + Send + Sync>;
287
288#[derive(Clone)]
289pub struct PasskeyRegistrationOptions {
290 pub require_session: bool,
291 pub resolve_user: Option<ResolveRegistrationUser>,
292 pub after_verification: Option<AfterRegistrationVerification>,
293 pub extensions: Option<Value>,
294}
295
296impl Default for PasskeyRegistrationOptions {
297 fn default() -> Self {
298 Self {
299 require_session: true,
300 resolve_user: None,
301 after_verification: None,
302 extensions: None,
303 }
304 }
305}
306
307impl PasskeyRegistrationOptions {
308 pub fn new() -> Self {
309 Self::default()
310 }
311
312 #[must_use]
313 pub fn require_session(mut self, require_session: bool) -> Self {
314 self.require_session = require_session;
315 self
316 }
317
318 #[must_use]
319 pub fn resolve_user<F>(mut self, resolver: F) -> Self
320 where
321 F: Fn(ResolveRegistrationUserInput) -> Option<PasskeyRegistrationUser>
322 + Send
323 + Sync
324 + 'static,
325 {
326 self.resolve_user = Some(Arc::new(resolver));
327 self
328 }
329
330 #[must_use]
331 pub fn after_verification<F>(mut self, callback: F) -> Self
332 where
333 F: Fn(AfterRegistrationVerificationInput) -> Option<String> + Send + Sync + 'static,
334 {
335 self.after_verification = Some(Arc::new(callback));
336 self
337 }
338
339 #[must_use]
340 pub fn extensions(mut self, extensions: Value) -> Self {
341 self.extensions = Some(extensions);
342 self
343 }
344}
345
346#[derive(Clone, Default)]
347pub struct PasskeyAuthenticationOptions {
348 pub after_verification: Option<AfterAuthenticationVerification>,
349 pub extensions: Option<Value>,
350}
351
352impl PasskeyAuthenticationOptions {
353 pub fn new() -> Self {
354 Self::default()
355 }
356
357 #[must_use]
358 pub fn after_verification<F>(mut self, callback: F) -> Self
359 where
360 F: Fn(AfterAuthenticationVerificationInput) + Send + Sync + 'static,
361 {
362 self.after_verification = Some(Arc::new(callback));
363 self
364 }
365
366 #[must_use]
367 pub fn extensions(mut self, extensions: Value) -> Self {
368 self.extensions = Some(extensions);
369 self
370 }
371}
372
373#[derive(Debug, Clone, PartialEq, Eq)]
374pub struct ResolveRegistrationUserInput {
375 pub context: Option<String>,
376}
377
378#[derive(Debug, Clone, PartialEq)]
379pub struct AfterRegistrationVerificationInput {
380 pub user: PasskeyRegistrationUser,
381 pub client_data: Value,
382 pub context: Option<String>,
383}
384
385#[derive(Debug, Clone, PartialEq)]
386pub struct AfterAuthenticationVerificationInput {
387 pub credential_id: String,
388 pub client_data: Value,
389}