@@ -13,87 +13,91 @@ use credentialsd_common::{
1313use crate :: model:: { GetAssertionResponseInternal , MakeCredentialResponseInternal } ;
1414use crate :: webauthn:: {
1515 self , GetAssertionRequest , GetPublicKeyCredentialUnsignedExtensionsResponse ,
16- MakeCredentialRequest , RelyingPartyId , WebAuthnIDL ,
16+ MakeCredentialRequest , NavigationContext , Origin , RelyingPartyId , WebAuthnIDL ,
1717} ;
1818
19- /// Parses a WebAuthn create credential request from D-Bus into a CTAP2 MakeCredentialRequest.
20- ///
21- /// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing, which handles:
22- /// - Challenge decoding from base64url
23- /// - User entity parsing with base64url-encoded user ID
24- /// - Relying party entity parsing
25- /// - Extension parsing (credProps, credBlob, largeBlobSupport, prf, etc.)
26- /// - Authenticator selection criteria (residentKey, userVerification)
27- /// - Excluded credentials list
28- /// - Public key credential parameters
19+ /// Reads the rpId from a create-credential request JSON (`rp.id`).
2920///
30- /// Returns the parsed request and the client data JSON (needed for response serialization).
31- pub ( super ) fn create_credential_request_try_into_ctap2 (
32- request : & CreateCredentialRequest ,
33- ) -> std:: result:: Result < ( MakeCredentialRequest , String ) , WebAuthnError > {
34- if request. public_key . is_none ( ) {
35- return Err ( WebAuthnError :: NotSupportedError ) ;
36- }
37- let options = request. public_key . as_ref ( ) . ok_or_else ( || {
38- tracing:: info!( "Invalid request: missing public_key" ) ;
21+ /// Used as a fallback when the origin is an AppId and the effective domain
22+ /// cannot be derived from the origin alone.
23+ // TODO(libwebauthn#185)
24+ fn peek_make_credential_rp_id ( request_json : & str ) -> Result < RelyingPartyId , WebAuthnError > {
25+ let value = serde_json:: from_str :: < serde_json:: Value > ( request_json) . map_err ( |err| {
26+ tracing:: info!( "Invalid request JSON: {err}" ) ;
3927 WebAuthnError :: TypeError
4028 } ) ?;
41-
42- // Get origin and determine relying party ID
43- let ( origin, _is_cross_origin) =
44- match ( request. origin . as_ref ( ) , request. is_same_origin . as_ref ( ) ) {
45- ( Some ( origin) , Some ( is_same_origin) ) => ( origin. to_string ( ) , !is_same_origin) ,
46- ( Some ( origin) , None ) => ( origin. to_string ( ) , true ) ,
47- ( None , _) => {
48- tracing:: info!( "Error reading origin from request." ) ;
49- return Err ( WebAuthnError :: TypeError ) ;
50- }
51- } ;
52-
53- // Extract rpId from JSON for RelyingPartyId construction
54- // libwebauthn validates that the rpId in the request matches this
55- let request_value =
56- serde_json:: from_str :: < serde_json:: Value > ( & options. request_json ) . map_err ( |err| {
57- tracing:: info!( "Invalid request JSON: {err}" ) ;
58- WebAuthnError :: TypeError
59- } ) ?;
60- let json = request_value. as_object ( ) . ok_or_else ( || {
61- tracing:: info!( "Invalid request JSON: not an object" ) ;
62- WebAuthnError :: TypeError
63- } ) ?;
64-
65- // Get rpId from the request, or derive from origin
66- let rp_id_str = json
29+ let rp_id_str = value
6730 . get ( "rp" )
6831 . and_then ( |rp| rp. get ( "id" ) )
6932 . and_then ( |id| id. as_str ( ) )
70- . map ( |s| s . to_string ( ) )
71- . unwrap_or_else ( || {
72- // Default to effective domain from origin
73- origin
74- . strip_prefix ( "https://" )
75- . map ( |rest| rest . split_once ( '/' ) . map ( | ( d , _ ) | d ) . unwrap_or ( rest ) )
76- . unwrap_or ( & origin )
77- . to_string ( )
78- } ) ;
33+ . ok_or_else ( || {
34+ tracing :: info! ( "RP ID required if using app ID as origin" ) ;
35+ WebAuthnError :: SecurityError
36+ } ) ? ;
37+ RelyingPartyId :: try_from ( rp_id_str ) . map_err ( |_| {
38+ tracing :: info! ( "Invalid relying party ID" ) ;
39+ WebAuthnError :: TypeError
40+ } )
41+ }
7942
80- let rp_id = RelyingPartyId :: try_from ( rp_id_str. as_str ( ) ) . map_err ( |_| {
43+ /// Reads the rpId from a get-credential request JSON (`rpId`).
44+ ///
45+ /// Used as a fallback when the origin is an AppId and the effective domain
46+ /// cannot be derived from the origin alone.
47+ // TODO(libwebauthn#185)
48+ fn peek_get_assertion_rp_id ( request_json : & str ) -> Result < RelyingPartyId , WebAuthnError > {
49+ let value = serde_json:: from_str :: < serde_json:: Value > ( request_json) . map_err ( |err| {
50+ tracing:: info!( "Invalid request JSON: {err}" ) ;
51+ WebAuthnError :: TypeError
52+ } ) ?;
53+ let rp_id_str = value
54+ . get ( "rpId" )
55+ . and_then ( |id| id. as_str ( ) )
56+ . ok_or_else ( || {
57+ tracing:: info!( "RP ID required if using app ID as origin" ) ;
58+ WebAuthnError :: SecurityError
59+ } ) ?;
60+ RelyingPartyId :: try_from ( rp_id_str) . map_err ( |_| {
8161 tracing:: info!( "Invalid relying party ID" ) ;
8262 WebAuthnError :: TypeError
63+ } )
64+ }
65+
66+ /// Parses a WebAuthn create credential request from D-Bus into a CTAP2 MakeCredentialRequest.
67+ ///
68+ /// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing. The relying party ID is derived
69+ /// from the request's origin; libwebauthn validates that any rpId in the JSON matches it.
70+ pub ( super ) fn create_credential_request_try_into_ctap2 (
71+ request : & CreateCredentialRequest ,
72+ request_environment : & NavigationContext ,
73+ ) -> std:: result:: Result < ( MakeCredentialRequest , String ) , WebAuthnError > {
74+ let options = request. public_key . as_ref ( ) . ok_or_else ( || {
75+ tracing:: info!( "Invalid request: missing public_key" ) ;
76+ WebAuthnError :: NotSupportedError
8377 } ) ?;
8478
85- // Use libwebauthn's JSON parsing
79+ let origin = request_environment. origin ( ) ;
80+ let rp_id = match origin {
81+ Origin :: Https { .. } => RelyingPartyId :: try_from ( origin) . map_err ( |err| {
82+ tracing:: info!( "Cannot derive relying party ID from origin: {err}" ) ;
83+ WebAuthnError :: SecurityError
84+ } ) ?,
85+ Origin :: AppId ( _) => peek_make_credential_rp_id ( & options. request_json ) ?,
86+ } ;
87+
8688 let mut make_cred_request = MakeCredentialRequest :: from_json ( & rp_id, & options. request_json )
8789 . map_err ( |err| {
8890 tracing:: info!( "Failed to parse MakeCredential request JSON: {err}" ) ;
8991 WebAuthnError :: TypeError
9092 } ) ?;
9193
92- // Set origin and cross_origin from D-Bus request context
93- make_cred_request. origin = origin;
94- make_cred_request. cross_origin = request. is_same_origin . as_ref ( ) . map ( |same| !same) ;
94+ // TODO(libwebauthn#185)
95+ make_cred_request. origin = origin. to_string ( ) ;
96+ make_cred_request. cross_origin = Some ( matches ! (
97+ request_environment,
98+ NavigationContext :: CrossOrigin ( _)
99+ ) ) ;
95100
96- // Get the client data JSON from the request for response serialization
97101 let client_data_json = make_cred_request. client_data_json ( ) ;
98102
99103 Ok ( ( make_cred_request, client_data_json) )
@@ -143,77 +147,39 @@ pub(super) fn create_credential_response_try_from_ctap2(
143147
144148/// Parses a WebAuthn get credential request from D-Bus into a CTAP2 GetAssertionRequest.
145149///
146- /// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing, which handles:
147- /// - Challenge decoding from base64url
148- /// - Allowed credentials list with transports
149- /// - Extension parsing (getCredBlob, largeBlob, prf, hmac-secret)
150- /// - User verification requirement
151- ///
152- /// Returns the parsed request and the client data JSON (needed for response serialization).
150+ /// Uses libwebauthn's `WebAuthnIDL::from_json()` for parsing. The relying party ID is derived
151+ /// from the request's origin; libwebauthn validates that any rpId in the JSON matches it.
153152pub ( super ) fn get_credential_request_try_into_ctap2 (
154153 request : & GetCredentialRequest ,
154+ request_environment : & NavigationContext ,
155155) -> std:: result:: Result < ( GetAssertionRequest , String ) , WebAuthnError > {
156- if request. public_key . is_none ( ) {
157- return Err ( WebAuthnError :: NotSupportedError ) ;
158- }
159156 let options = request. public_key . as_ref ( ) . ok_or_else ( || {
160157 tracing:: info!( "Invalid request: no \" publicKey\" options specified." ) ;
161- WebAuthnError :: TypeError
158+ WebAuthnError :: NotSupportedError
162159 } ) ?;
163160
164- // Get origin
165- let ( origin, _is_cross_origin) =
166- match ( request. origin . as_ref ( ) , request. is_same_origin . as_ref ( ) ) {
167- ( Some ( origin) , Some ( is_same_origin) ) => ( origin. to_string ( ) , !is_same_origin) ,
168- ( Some ( origin) , None ) => ( origin. to_string ( ) , true ) ,
169- ( None , _) => {
170- tracing:: info!( "Error reading origin from client request." ) ;
171- return Err ( WebAuthnError :: TypeError ) ;
172- }
173- } ;
174-
175- // Extract rpId from JSON for RelyingPartyId construction
176- let request_value =
177- serde_json:: from_str :: < serde_json:: Value > ( & options. request_json ) . map_err ( |err| {
178- tracing:: info!( "Invalid request JSON: {err}" ) ;
179- WebAuthnError :: TypeError
180- } ) ?;
181- let json = request_value. as_object ( ) . ok_or_else ( || {
182- tracing:: info!( "Invalid request JSON: not an object" ) ;
183- WebAuthnError :: TypeError
184- } ) ?;
185-
186- // Get rpId from the request, or derive from origin
187- let rp_id_str = json
188- . get ( "rpId" )
189- . and_then ( |id| id. as_str ( ) )
190- . map ( |s| s. to_string ( ) )
191- . unwrap_or_else ( || {
192- // Default to effective domain from origin
193- origin
194- . strip_prefix ( "https://" )
195- . map ( |rest| rest. split_once ( '/' ) . map ( |( d, _) | d) . unwrap_or ( rest) )
196- . unwrap_or ( & origin)
197- . to_string ( )
198- } ) ;
199-
200- let rp_id = RelyingPartyId :: try_from ( rp_id_str. as_str ( ) ) . map_err ( |_| {
201- tracing:: info!( "Invalid relying party ID" ) ;
202- WebAuthnError :: TypeError
203- } ) ?;
161+ let origin = request_environment. origin ( ) ;
162+ let rp_id = match origin {
163+ Origin :: Https { .. } => RelyingPartyId :: try_from ( origin) . map_err ( |err| {
164+ tracing:: info!( "Cannot derive relying party ID from origin: {err}" ) ;
165+ WebAuthnError :: SecurityError
166+ } ) ?,
167+ Origin :: AppId ( _) => peek_get_assertion_rp_id ( & options. request_json ) ?,
168+ } ;
204169
205- // Use libwebauthn's JSON parsing
206170 let mut get_assertion_request = GetAssertionRequest :: from_json ( & rp_id, & options. request_json )
207171 . map_err ( |err| {
208- tracing:: info!( "Failed to parse GetAssertion request JSON: {err}" ) ;
209- WebAuthnError :: TypeError
210- } ) ?;
172+ tracing:: info!( "Failed to parse GetAssertion request JSON: {err}" ) ;
173+ WebAuthnError :: TypeError
174+ } ) ?;
211175
212- // Set origin and cross_origin from D-Bus request context
213- get_assertion_request. origin = origin;
214- get_assertion_request. cross_origin = request. is_same_origin . as_ref ( ) . map ( |same| !same) ;
176+ // TODO(libwebauthn#185)
177+ get_assertion_request. origin = origin. to_string ( ) ;
178+ get_assertion_request. cross_origin = Some ( matches ! (
179+ request_environment,
180+ NavigationContext :: CrossOrigin ( _)
181+ ) ) ;
215182
216- // Get the client data JSON from the request for response serialization
217183 let client_data_json = get_assertion_request. client_data_json ( ) ;
218184
219185 Ok ( ( get_assertion_request, client_data_json) )
0 commit comments