@@ -32,22 +32,41 @@ pub struct WellKnownResponse {
3232/// Fetcher for `https://{rp_id}/.well-known/webauthn`, per WebAuthn L3 §5.11.1
3333/// step 2. Implementations MUST send no credentials, no Referer, refuse
3434/// non-`https://` redirects, cap the body size, and bound the request duration.
35- /// Implementations MUST return `Err(FetchFailed)` for any status code other
36- /// than 200 (after following redirects). Implementations MUST report the wire
37- /// `Content-Type` header value unmodified (or `None` if absent) and MUST NOT
38- /// synthesise an `application/json` content type for non-JSON responses.
35+ /// Implementations MUST return `Err(WellKnownFetchError::Status)` for any
36+ /// status code other than 200 (after following redirects). Implementations MUST
37+ /// report the wire `Content-Type` header value unmodified (or `None` if absent)
38+ /// and MUST NOT synthesise an `application/json` content type for non-JSON
39+ /// responses.
3940#[ async_trait]
4041pub trait RelatedOriginsHttpClient : Send + Sync {
4142 async fn fetch_well_known (
4243 & self ,
4344 rp_id : & RelyingPartyId ,
44- ) -> Result < WellKnownResponse , RelatedOriginsError > ;
45+ ) -> Result < WellKnownResponse , WellKnownFetchError > ;
46+ }
47+
48+ /// Failure modes for [`RelatedOriginsHttpClient::fetch_well_known`].
49+ #[ derive( thiserror:: Error , Debug , Clone ) ]
50+ pub enum WellKnownFetchError {
51+ /// Transport-level failure: TLS, DNS, timeout, rejected redirect, body
52+ /// stream interrupt, client build error, etc.
53+ #[ error( "transport error: {0}" ) ]
54+ Transport ( String ) ,
55+ /// Endpoint replied with a non-200 status (after following redirects).
56+ #[ error( "HTTP status {0}" ) ]
57+ Status ( u16 ) ,
58+ /// Body exceeded the implementation's configured size cap before completion.
59+ #[ error( "body exceeded configured size cap" ) ]
60+ BodyTooLarge ,
61+ /// Implementation does not perform fetches (see [`NoRelatedOriginsClient`]).
62+ #[ error( "client does not support related-origin fetches" ) ]
63+ NotSupported ,
4564}
4665
4766#[ derive( thiserror:: Error , Debug , Clone ) ]
4867pub enum RelatedOriginsError {
4968 #[ error( "well-known fetch failed: {0}" ) ]
50- FetchFailed ( String ) ,
69+ Fetch ( # [ from ] WellKnownFetchError ) ,
5170 #[ error( "unexpected content type: {0:?}" ) ]
5271 UnexpectedContentType ( Option < String > ) ,
5372 /// Step 2.b: body did not decode as JSON.
@@ -60,19 +79,6 @@ pub enum RelatedOriginsError {
6079 NoMatchingOrigin ,
6180}
6281
63- impl RelatedOriginsError {
64- /// Log-safe variant discriminant; `Debug`/`Display` may carry reqwest/serde text with IPs or body snippets.
65- pub fn kind ( & self ) -> & ' static str {
66- match self {
67- RelatedOriginsError :: FetchFailed ( _) => "fetch_failed" ,
68- RelatedOriginsError :: UnexpectedContentType ( _) => "unexpected_content_type" ,
69- RelatedOriginsError :: MalformedJson ( _) => "malformed_json" ,
70- RelatedOriginsError :: MalformedDocument ( _) => "malformed_document" ,
71- RelatedOriginsError :: NoMatchingOrigin => "no_matching_origin" ,
72- }
73- }
74- }
75-
7682pub type RelatedOriginsResult = Result < ( ) , RelatedOriginsError > ;
7783
7884#[ derive( Debug , Deserialize ) ]
@@ -192,10 +198,8 @@ impl RelatedOriginsHttpClient for NoRelatedOriginsClient {
192198 async fn fetch_well_known (
193199 & self ,
194200 _: & RelyingPartyId ,
195- ) -> Result < WellKnownResponse , RelatedOriginsError > {
196- Err ( RelatedOriginsError :: FetchFailed (
197- "this client does not support related origin requests" . into ( ) ,
198- ) )
201+ ) -> Result < WellKnownResponse , WellKnownFetchError > {
202+ Err ( WellKnownFetchError :: NotSupported )
199203 }
200204}
201205
@@ -205,15 +209,15 @@ mod tests {
205209 use super :: * ;
206210
207211 struct MockClient {
208- response : Result < WellKnownResponse , RelatedOriginsError > ,
212+ response : Result < WellKnownResponse , WellKnownFetchError > ,
209213 }
210214
211215 #[ async_trait]
212216 impl RelatedOriginsHttpClient for MockClient {
213217 async fn fetch_well_known (
214218 & self ,
215219 _: & RelyingPartyId ,
216- ) -> Result < WellKnownResponse , RelatedOriginsError > {
220+ ) -> Result < WellKnownResponse , WellKnownFetchError > {
217221 self . response . clone ( )
218222 }
219223 }
@@ -670,9 +674,9 @@ mod tests {
670674 }
671675
672676 #[ tokio:: test]
673- async fn fetch_error_propagates_as_fetch_failed ( ) {
677+ async fn fetch_error_propagates_as_fetch ( ) {
674678 let http = MockClient {
675- response : Err ( RelatedOriginsError :: FetchFailed ( "simulated" . into ( ) ) ) ,
679+ response : Err ( WellKnownFetchError :: Transport ( "simulated" . into ( ) ) ) ,
676680 } ;
677681 let res = validate_related_origins (
678682 & caller ( "https://example.com" ) ,
@@ -681,7 +685,12 @@ mod tests {
681685 & http,
682686 )
683687 . await ;
684- assert ! ( matches!( res, Err ( RelatedOriginsError :: FetchFailed ( _) ) ) ) ;
688+ assert ! ( matches!(
689+ res,
690+ Err ( RelatedOriginsError :: Fetch ( WellKnownFetchError :: Transport (
691+ _
692+ ) ) )
693+ ) ) ;
685694 }
686695
687696 #[ tokio:: test]
0 commit comments