@ -21,12 +21,12 @@ use std::sync::{Arc, LazyLock}; 
				
			 
			
		
	
		
		
			
				
					 
					 
					use   std ::time ::Duration ;  
					 
					 
					use   std ::time ::Duration ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					use   url ::Url ;  
					 
					 
					use   url ::Url ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					use   uuid ::Uuid ;  
					 
					 
					use   uuid ::Uuid ;  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					use   webauthn_rs ::prelude ::{ Base64UrlSafeData ,   SecurityKey ,   SecurityKeyAuthentication ,   SecurityK eyRegistration} ;  
					 
					 
					use   webauthn_rs ::prelude ::{ Base64UrlSafeData ,   Credential ,   Passkey ,   PasskeyAuthentication ,   Passk eyRegistration} ;  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					use   webauthn_rs ::{ Webauthn ,   WebauthnBuilder } ;  
					 
					 
					use   webauthn_rs ::{ Webauthn ,   WebauthnBuilder } ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					use   webauthn_rs_proto ::{  
					 
					 
					use   webauthn_rs_proto ::{  
				
			 
			
		
	
		
		
			
				
					 
					 
					     AuthenticationExtensionsClientOutputs ,   AuthenticatorAssertionResponseRaw ,   AuthenticatorAttestationResponseRaw ,  
					 
					 
					     AuthenticationExtensionsClientOutputs ,   AuthenticatorAssertionResponseRaw ,   AuthenticatorAttestationResponseRaw ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					     PublicKeyCredential ,   RegisterPublicKeyCredential ,   RegistrationExtensionsClientOutputs ,  
					 
					 
					     PublicKeyCredential ,   RegisterPublicKeyCredential ,   RegistrationExtensionsClientOutputs ,  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     RequestAuthenticationExtensions ,  
					 
					 
					     RequestAuthenticationExtensions ,   UserVerificationPolicy ,  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					} ;  
					 
					 
					} ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					pub   static   WEBAUTHN_2FA_CONFIG : LazyLock < Arc < Webauthn > >   =   LazyLock ::new ( | |   {  
					 
					 
					pub   static   WEBAUTHN_2FA_CONFIG : LazyLock < Arc < Webauthn > >   =   LazyLock ::new ( | |   {  
				
			 
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
							 
						
					 
					@ -38,8 +38,7 @@ pub static WEBAUTHN_2FA_CONFIG: LazyLock<Arc<Webauthn>> = LazyLock::new(|| { 
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   webauthn   =   WebauthnBuilder ::new ( & rp_id ,   & rp_origin )  
					 
					 
					     let   webauthn   =   WebauthnBuilder ::new ( & rp_id ,   & rp_origin )  
				
			 
			
		
	
		
		
			
				
					 
					 
					         . expect ( "Creating WebauthnBuilder failed" )  
					 
					 
					         . expect ( "Creating WebauthnBuilder failed" )  
				
			 
			
		
	
		
		
			
				
					 
					 
					         . rp_name ( & domain )  
					 
					 
					         . rp_name ( & domain )  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					         . timeout ( Duration ::from_millis ( 60000 ) )  
					 
					 
					         . timeout ( Duration ::from_millis ( 60000 ) ) ;  
				
			 
			
				
				
			
		
	
		
		
			
				
					 
					 
					         . danger_set_user_presence_only_security_keys ( true ) ;  
					 
					 
					 
				
			 
			
		
	
		
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     Arc ::new ( webauthn . build ( ) . expect ( "Building Webauthn failed" ) )  
					 
					 
					     Arc ::new ( webauthn . build ( ) . expect ( "Building Webauthn failed" ) )  
				
			 
			
		
	
		
		
			
				
					 
					 
					} ) ;  
					 
					 
					} ) ;  
				
			 
			
		
	
	
		
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
					@ -78,7 +77,7 @@ pub struct WebauthnRegistration { 
				
			 
			
		
	
		
		
			
				
					 
					 
					     pub   name : String ,  
					 
					 
					     pub   name : String ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					     pub   migrated : bool ,  
					 
					 
					     pub   migrated : bool ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     pub   credential : SecurityK ey,  
					 
					 
					     pub   credential : Passk ey,  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					}  
					 
					 
					}  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					impl   WebauthnRegistration   {  
					 
					 
					impl   WebauthnRegistration   {  
				
			 
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
							 
						
					 
					@ -89,6 +88,24 @@ impl WebauthnRegistration { 
				
			 
			
		
	
		
		
			
				
					 
					 
					             "migrated" : self . migrated ,  
					 
					 
					             "migrated" : self . migrated ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					         } )  
					 
					 
					         } )  
				
			 
			
		
	
		
		
			
				
					 
					 
					     }  
					 
					 
					     }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     fn  set_backup_eligible ( & mut   self ,   backup_eligible : bool ,   backup_state : bool )   -> bool  {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         let   mut   changed   =   false ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         let   mut   cred : Credential   =   self . credential . clone ( ) . into ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         if   cred . backup_state   ! =   backup_state   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             cred . backup_state   =   backup_state ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             changed   =   true ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         if   backup_eligible   & &   ! cred . backup_eligible   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             cred . backup_eligible   =   true ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             changed   =   true ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         self . credential   =   cred . into ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         changed  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     }  
				
			 
			
		
	
		
		
			
				
					 
					 
					}  
					 
					 
					}  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					#[ post( " /two-factor/get-webauthn " , data =  " <data> " ) ]  
					 
					 
					#[ post( " /two-factor/get-webauthn " , data =  " <data> " ) ]  
				
			 
			
		
	
	
		
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
					@ -131,18 +148,27 @@ async fn generate_webauthn_challenge( 
				
			 
			
		
	
		
		
			
				
					 
					 
					         . map ( | r |   r . credential . cred_id ( ) . to_owned ( ) )   // We return the credentialIds to the clients to avoid double registering
  
					 
					 
					         . map ( | r |   r . credential . cred_id ( ) . to_owned ( ) )   // We return the credentialIds to the clients to avoid double registering
  
				
			 
			
		
	
		
		
			
				
					 
					 
					         . collect ( ) ;  
					 
					 
					         . collect ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     let   ( challenge ,   state )   =   webauthn . start_security key_registration (  
					 
					 
					     let   ( mut   challenge ,   state )   =   webauthn . start_pass key_registration (  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					         Uuid ::from_str ( & user . uuid ) . expect ( "Failed to parse UUID" ) ,   // Should never fail
  
					 
					 
					         Uuid ::from_str ( & user . uuid ) . expect ( "Failed to parse UUID" ) ,   // Should never fail
  
				
			 
			
		
	
		
		
			
				
					 
					 
					         & user . email ,  
					 
					 
					         & user . email ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					         & user . name ,  
					 
					 
					         & user . name ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					         Some ( registrations ) ,  
					 
					 
					         Some ( registrations ) ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					         None ,  
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					 
					 
					         None ,  
					 
					 
					 
				
			 
			
		
	
		
		
			
				
					 
					 
					     ) ? ;  
					 
					 
					     ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     let   mut   state   =   serde_json ::to_value ( & state ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     state [ "rs" ] [ "policy" ]   =   Value ::String ( "discouraged" . to_string ( ) ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     state [ "rs" ] [ "extensions" ] . as_object_mut ( ) . unwrap ( ) . clear ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   type_   =   TwoFactorType ::WebauthnRegisterChallenge ;  
					 
					 
					     let   type_   =   TwoFactorType ::WebauthnRegisterChallenge ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     TwoFactor ::new ( user . uuid . clone ( ) ,   type_ ,   serde_json ::to_string ( & state ) ? ) . save ( & mut   conn ) . await ? ;  
					 
					 
					     TwoFactor ::new ( user . uuid . clone ( ) ,   type_ ,   serde_json ::to_string ( & state ) ? ) . save ( & mut   conn ) . await ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     // Because for this flow we abuse the passkeys as 2FA, and use it more like a securitykey
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     // we need to modify some of the default settings defined by `start_passkey_registration()`.
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     challenge . public_key . extensions   =   None ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     if   let   Some ( asc )   =   challenge . public_key . authenticator_selection . as_mut ( )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         asc . user_verification   =   UserVerificationPolicy ::Discouraged_DO_NOT_USE ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   mut   challenge_value   =   serde_json ::to_value ( challenge . public_key ) ? ;  
					 
					 
					     let   mut   challenge_value   =   serde_json ::to_value ( challenge . public_key ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     challenge_value [ "status" ]   =   "ok" . into ( ) ;  
					 
					 
					     challenge_value [ "status" ]   =   "ok" . into ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     challenge_value [ "errorMessage" ]   =   "" . into ( ) ;  
					 
					 
					     challenge_value [ "errorMessage" ]   =   "" . into ( ) ;  
				
			 
			
		
	
	
		
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
					@ -253,7 +279,7 @@ async fn activate_webauthn( 
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   type_   =   TwoFactorType ::WebauthnRegisterChallenge   as   i32 ;  
					 
					 
					     let   type_   =   TwoFactorType ::WebauthnRegisterChallenge   as   i32 ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   state   =   match   TwoFactor ::find_by_user_and_type ( & user . uuid ,   type_ ,   & mut   conn ) . await   {  
					 
					 
					     let   state   =   match   TwoFactor ::find_by_user_and_type ( & user . uuid ,   type_ ,   & mut   conn ) . await   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					         Some ( tf )   = >   {  
					 
					 
					         Some ( tf )   = >   {  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					             let   state : SecurityK eyRegistration  =   serde_json ::from_str ( & tf . data ) ? ;  
					 
					 
					             let   state : Passk eyRegistration  =   serde_json ::from_str ( & tf . data ) ? ;  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					             tf . delete ( & mut   conn ) . await ? ;  
					 
					 
					             tf . delete ( & mut   conn ) . await ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					             state  
					 
					 
					             state  
				
			 
			
		
	
		
		
			
				
					 
					 
					         }  
					 
					 
					         }  
				
			 
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
							 
						
					 
					@ -261,7 +287,7 @@ async fn activate_webauthn( 
				
			 
			
		
	
		
		
			
				
					 
					 
					     } ;  
					 
					 
					     } ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     // Verify the credentials with the saved state
  
					 
					 
					     // Verify the credentials with the saved state
  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     let   credential   =   webauthn . finish_security key_registration ( & data . device_response . into ( ) ,   & state ) ? ;  
					 
					 
					     let   credential   =   webauthn . finish_pass key_registration ( & data . device_response . into ( ) ,   & state ) ? ;  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   mut   registrations : Vec < _ >   =   get_webauthn_registrations ( & user . uuid ,   & mut   conn ) . await ? . 1 ;  
					 
					 
					     let   mut   registrations : Vec < _ >   =   get_webauthn_registrations ( & user . uuid ,   & mut   conn ) . await ? . 1 ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     // TODO: Check for repeated ID's
  
					 
					 
					     // TODO: Check for repeated ID's
  
				
			 
			
		
	
	
		
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
					@ -372,21 +398,25 @@ pub async fn generate_webauthn_login( 
				
			 
			
		
	
		
		
			
				
					 
					 
					     conn : & mut   DbConn ,  
					 
					 
					     conn : & mut   DbConn ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					)   -> JsonResult   {  
					 
					 
					)   -> JsonResult   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					     // Load saved credentials
  
					 
					 
					     // Load saved credentials
  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     let   creds : Vec < _ >   =   get_webauthn_registrations ( user_id ,   conn ) . await ? . 1. into_iter ( ) . map ( | r |   r . credential ) . collect ( ) ;  
					 
					 
					     let   creds : Vec < Passkey >   =  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         get_webauthn_registrations ( user_id ,   conn ) . await ? . 1. into_iter ( ) . map ( | r |   r . credential ) . collect ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     if   creds . is_empty ( )   {  
					 
					 
					     if   creds . is_empty ( )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					         err ! ( "No Webauthn devices registered" )  
					 
					 
					         err ! ( "No Webauthn devices registered" )  
				
			 
			
		
	
		
		
			
				
					 
					 
					     }  
					 
					 
					     }  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     // Generate a challenge based on the credentials
  
					 
					 
					     // Generate a challenge based on the credentials
  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     let   ( mut   response ,   state )   =   webauthn . start_security key_authentication ( & creds ) ? ;  
					 
					 
					     let   ( mut   response ,   state )   =   webauthn . start_pass key_authentication ( & creds ) ? ;  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     // Modify to discourage user verification
  
					 
					 
					     // Modify to discourage user verification
  
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   mut   state   =   serde_json ::to_value ( & state ) ? ;  
					 
					 
					     let   mut   state   =   serde_json ::to_value ( & state ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     state [ "ast" ] [ "policy" ]   =   Value ::String ( "discouraged" . to_string ( ) ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     // Add appid, this is only needed for U2F compatibility, so maybe it can be removed as well
  
					 
					 
					     // Add appid, this is only needed for U2F compatibility, so maybe it can be removed as well
  
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   app_id   =   format ! ( "{}/app-id.json" ,   & CONFIG . domain ( ) ) ;  
					 
					 
					     let   app_id   =   format ! ( "{}/app-id.json" ,   & CONFIG . domain ( ) ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     state [ "ast" ] [ "appid" ]   =   Value ::String ( app_id . clone ( ) ) ;  
					 
					 
					     state [ "ast" ] [ "appid" ]   =   Value ::String ( app_id . clone ( ) ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     response . public_key . user_verification   =   UserVerificationPolicy ::Discouraged_DO_NOT_USE ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					     response  
					 
					 
					     response  
				
			 
			
		
	
		
		
			
				
					 
					 
					         . public_key  
					 
					 
					         . public_key  
				
			 
			
		
	
		
		
			
				
					 
					 
					         . extensions  
					 
					 
					         . extensions  
				
			 
			
		
	
	
		
		
			
				
					
						
							
								 
							 
						
						
							
								 
							 
						
						
					 
					@ -413,9 +443,9 @@ pub async fn validate_webauthn_login( 
				
			 
			
		
	
		
		
			
				
					 
					 
					     conn : & mut   DbConn ,  
					 
					 
					     conn : & mut   DbConn ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					)   -> EmptyResult   {  
					 
					 
					)   -> EmptyResult   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   type_   =   TwoFactorType ::WebauthnLoginChallenge   as   i32 ;  
					 
					 
					     let   type_   =   TwoFactorType ::WebauthnLoginChallenge   as   i32 ;  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     let   state   =   match   TwoFactor ::find_by_user_and_type ( user_id ,   type_ ,   conn ) . await   {  
					 
					 
					     let   mut   state   =   match   TwoFactor ::find_by_user_and_type ( user_id ,   type_ ,   conn ) . await   {  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					         Some ( tf )   = >   {  
					 
					 
					         Some ( tf )   = >   {  
				
			 
			
		
	
		
		
			
				
					
					 
					 
					             let   state : SecurityK eyAuthentication  =   serde_json ::from_str ( & tf . data ) ? ;  
					 
					 
					             let   state : Passk eyAuthentication  =   serde_json ::from_str ( & tf . data ) ? ;  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					             tf . delete ( conn ) . await ? ;  
					 
					 
					             tf . delete ( conn ) . await ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					             state  
					 
					 
					             state  
				
			 
			
		
	
		
		
			
				
					 
					 
					         }  
					 
					 
					         }  
				
			 
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
							 
						
					 
					@ -432,7 +462,12 @@ pub async fn validate_webauthn_login( 
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     let   mut   registrations   =   get_webauthn_registrations ( user_id ,   conn ) . await ? . 1 ;  
					 
					 
					     let   mut   registrations   =   get_webauthn_registrations ( user_id ,   conn ) . await ? . 1 ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					
					 
					 
					     let   authentication_result   =   webauthn . finish_securitykey_authentication ( & rsp ,   & state ) ? ;  
					 
					 
					     // We need to check for and update the backup_eligible flag when needed.
  
				
			 
			
				
				
			
		
	
		
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     // Vaultwarden did not have knowledge of this flag prior to migrating to webauthn-rs v0.5.x
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     // Because of this we check the flag at runtime and update the registrations and state when needed
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     check_and_update_backup_eligible ( user_id ,   & rsp ,   & mut   registrations ,   & mut   state ,   conn ) . await ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     let   authentication_result   =   webauthn . finish_passkey_authentication ( & rsp ,   & state ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					
 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					     for   reg   in   & mut   registrations   {  
					 
					 
					     for   reg   in   & mut   registrations   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					         if   ct_eq ( reg . credential . cred_id ( ) ,   authentication_result . cred_id ( ) )   {  
					 
					 
					         if   ct_eq ( reg . credential . cred_id ( ) ,   authentication_result . cred_id ( ) )   {  
				
			 
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
							 
						
					 
					@ -454,3 +489,66 @@ pub async fn validate_webauthn_login( 
				
			 
			
		
	
		
		
			
				
					 
					 
					         }  
					 
					 
					         }  
				
			 
			
		
	
		
		
			
				
					 
					 
					     )  
					 
					 
					     )  
				
			 
			
		
	
		
		
			
				
					 
					 
					}  
					 
					 
					}  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					async   fn  check_and_update_backup_eligible (  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     user_id : & UserId ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     rsp : & PublicKeyCredential ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     registrations : & mut   Vec < WebauthnRegistration > ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     state : & mut   PasskeyAuthentication ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     conn : & mut   DbConn ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					)   -> EmptyResult   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     // The feature flags from the response
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     // For details see: https://www.w3.org/TR/webauthn-3/#sctn-authenticator-data
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     const   FLAG_BACKUP_ELIGIBLE : u8  =   0b0000_1000 ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     const   FLAG_BACKUP_STATE : u8  =   0b0001_0000 ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     if   let   Some ( bits )   =   rsp . response . authenticator_data . get ( 32 )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         let   backup_eligible   =   0   ! =   ( bits   &   FLAG_BACKUP_ELIGIBLE ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         let   backup_state   =   0   ! =   ( bits   &   FLAG_BACKUP_STATE ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         // If the current key is backup eligible, then we probably need to update one of the keys already stored in the database
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         // This is needed because Vaultwarden didn't store this information when using the previous version of webauthn-rs since it was a new addition to the protocol
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         // Because we store multiple keys in one json string, we need to fetch the correct key first, and update its information before we let it verify
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         if   backup_eligible   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             let   rsp_id   =   rsp . raw_id . as_slice ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             for   reg   in   & mut   * registrations   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                 if   ct_eq ( reg . credential . cred_id ( ) . as_slice ( ) ,   rsp_id )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                     // Try to update the key, and if needed also update the database, before the actual state check is done
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                     if   reg . set_backup_eligible ( backup_eligible ,   backup_state )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         TwoFactor ::new (  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             user_id . clone ( ) ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             TwoFactorType ::Webauthn ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             serde_json ::to_string ( & registrations ) ? ,  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         . save ( conn )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         . await ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         // We also need to adjust the current state which holds the challenge used to start the authentication verification
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         // Because Vaultwarden supports multiple keys, we need to loop through the deserialized state and check which key to update
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         let   mut   raw_state   =   serde_json ::to_value ( & state ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         if   let   Some ( credentials )   =   raw_state  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             . get_mut ( "ast" )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             . and_then ( | v |   v . get_mut ( "credentials" ) )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             . and_then ( | v |   v . as_array_mut ( ) )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             for   cred   in   credentials . iter_mut ( )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                 if   cred . get ( "cred_id" ) . is_some_and ( | v |   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                     // Deserialize to a [u8] so it can be compared using `ct_eq` with the `rsp_id`
  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                     let   cred_id_slice : Base64UrlSafeData   =   serde_json ::from_value ( v . clone ( ) ) . unwrap ( ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                     ct_eq ( cred_id_slice ,   rsp_id )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                 } )   {  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                     cred [ "backup_eligible" ]   =   Value ::Bool ( backup_eligible ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                     cred [ "backup_state" ]   =   Value ::Bool ( backup_state ) ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                                 }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                             }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					
 
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                         * state   =   serde_json ::from_value ( raw_state ) ? ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                     }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                     break ;  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					                 }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					             }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					         }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     }  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					     Ok ( ( ) )  
				
			 
			
		
	
		
		
			
				
					 
					 
					 
					 
					 
					}