@@ -138,6 +138,11 @@ pub struct BaseRequest {
138138 deserialize_with = "crate::util::de_int_key"
139139 ) ]
140140 pub labels : HashMap < usize , TranslatedString > ,
141+ /// Host to use in the session QR (`Qr.u`), overriding the IRMA server's default.
142+ /// The server validates this against the requestor's configured `host_perms` allowlist.
143+ /// Requires irmago v0.14.0 or newer on the server side.
144+ #[ serde( skip_serializing_if = "Option::is_none" , default ) ]
145+ pub host : Option < String > ,
141146}
142147
143148/// IRMA session requests
@@ -179,6 +184,7 @@ impl BaseRequestBuilder {
179184 return_url : None ,
180185 augment_return : false ,
181186 labels : HashMap :: new ( ) ,
187+ host : None ,
182188 } ,
183189 }
184190 }
@@ -215,6 +221,11 @@ impl BaseRequestBuilder {
215221 self . base . return_url = Some ( return_url) ;
216222 self . base . augment_return = true ;
217223 }
224+
225+ fn host ( & mut self , host : String ) {
226+ debug_assert ! ( self . base. host. is_none( ) ) ;
227+ self . base . host = Some ( host) ;
228+ }
218229}
219230
220231impl Default for BaseRequestBuilder {
@@ -276,6 +287,14 @@ impl DisclosureRequestBuilder {
276287 self . base . augmented_return_url ( return_url) ;
277288 self
278289 }
290+
291+ /// Set the host to use in the session QR, overriding the IRMA server's default.
292+ /// The server validates this against the requestor's configured `host_perms` allowlist
293+ /// (requires irmago v0.14.0 or newer).
294+ pub fn host ( mut self , host : String ) -> DisclosureRequestBuilder {
295+ self . base . host ( host) ;
296+ self
297+ }
279298}
280299
281300/// Build a signature request
@@ -335,6 +354,14 @@ impl SignatureRequestBuilder {
335354 self . base . augmented_return_url ( return_url) ;
336355 self
337356 }
357+
358+ /// Set the host to use in the session QR, overriding the IRMA server's default.
359+ /// The server validates this against the requestor's configured `host_perms` allowlist
360+ /// (requires irmago v0.14.0 or newer).
361+ pub fn host ( mut self , host : String ) -> SignatureRequestBuilder {
362+ self . base . host ( host) ;
363+ self
364+ }
338365}
339366
340367/// Build a request to issue one or more credentials
@@ -401,6 +428,14 @@ impl IssuanceRequestBuilder {
401428 self . base . augmented_return_url ( return_url) ;
402429 self
403430 }
431+
432+ /// Set the host to use in the session QR, overriding the IRMA server's default.
433+ /// The server validates this against the requestor's configured `host_perms` allowlist
434+ /// (requires irmago v0.14.0 or newer).
435+ pub fn host ( mut self , host : String ) -> IssuanceRequestBuilder {
436+ self . base . host ( host) ;
437+ self
438+ }
404439}
405440
406441/// An IRMA request extended with extra information for the server on how to execute it.
@@ -543,6 +578,53 @@ mod tests {
543578 ) ;
544579 }
545580
581+ #[ test]
582+ fn test_host_request ( ) {
583+ // host is omitted from the serialized request when not set
584+ let req1 = DisclosureRequestBuilder :: new ( )
585+ . add_discon ( vec ! [ vec![ AttributeRequest :: Simple ( "a.b.c.d" . into( ) ) ] ] )
586+ . build ( ) ;
587+ assert ! ( !serde_json:: to_string( & req1) . unwrap( ) . contains( "host" ) ) ;
588+
589+ // host is serialized as "host" when set, and survives a round-trip
590+ let req2 = DisclosureRequestBuilder :: new ( )
591+ . add_discon ( vec ! [ vec![ AttributeRequest :: Simple ( "a.b.c.d" . into( ) ) ] ] )
592+ . host ( "https://example.com" . into ( ) )
593+ . build ( ) ;
594+ assert_eq ! ( "{\" @context\" :\" https://irma.app/ld/request/disclosure/v2\" ,\" disclose\" :[[[\" a.b.c.d\" ]]],\" host\" :\" https://example.com\" }" , serde_json:: to_string( & req2) . unwrap( ) ) ;
595+ assert_eq ! (
596+ req2,
597+ serde_json:: from_str( & serde_json:: to_string( & req2) . unwrap( ) ) . unwrap( )
598+ ) ;
599+
600+ // the host setter is available on every public builder
601+ let req3 = SignatureRequestBuilder :: new ( "testmessage" . into ( ) )
602+ . add_discon ( vec ! [ vec![ AttributeRequest :: Simple ( "a.b.c.d" . into( ) ) ] ] )
603+ . host ( "https://signature.example.com" . into ( ) )
604+ . build ( ) ;
605+ assert_eq ! ( "{\" @context\" :\" https://irma.app/ld/request/signature/v2\" ,\" message\" :\" testmessage\" ,\" disclose\" :[[[\" a.b.c.d\" ]]],\" host\" :\" https://signature.example.com\" }" , serde_json:: to_string( & req3) . unwrap( ) ) ;
606+ assert_eq ! (
607+ req3,
608+ serde_json:: from_str( & serde_json:: to_string( & req3) . unwrap( ) ) . unwrap( )
609+ ) ;
610+
611+ let req4 = IssuanceRequestBuilder :: new ( )
612+ . add_credential ( Credential {
613+ credential : "a.b.c" . into ( ) ,
614+ validity : Some ( 123456789 ) ,
615+ attributes : hashmap ! [
616+ "d" . into( ) => "e" . into( ) ,
617+ ] ,
618+ } )
619+ . host ( "https://issuance.example.com" . into ( ) )
620+ . build ( ) ;
621+ assert_eq ! ( "{\" @context\" :\" https://irma.app/ld/request/issuance/v2\" ,\" credentials\" :[{\" credential\" :\" a.b.c\" ,\" validity\" :123456789,\" attributes\" :{\" d\" :\" e\" }}],\" host\" :\" https://issuance.example.com\" }" , serde_json:: to_string( & req4) . unwrap( ) ) ;
622+ assert_eq ! (
623+ req4,
624+ serde_json:: from_str( & serde_json:: to_string( & req4) . unwrap( ) ) . unwrap( )
625+ ) ;
626+ }
627+
546628 #[ test]
547629 fn test_signature_request ( ) {
548630 let req1 = SignatureRequestBuilder :: new ( "testmessage" . into ( ) )
0 commit comments