@@ -36,6 +36,42 @@ pub struct SessionData {
3636 pub session_ptr : Qr ,
3737 /// The token for further interaction with the session
3838 pub token : SessionToken ,
39+ /// Information needed to drive the IRMA/Yivi frontend directly (e.g. for
40+ /// pairing). Present since irmago v0.14.0; `None` when the server does not
41+ /// return a `frontendRequest` block.
42+ #[ serde(
43+ rename = "frontendRequest" ,
44+ default ,
45+ skip_serializing_if = "Option::is_none"
46+ ) ]
47+ pub frontend_request : Option < FrontendRequest > ,
48+ }
49+
50+ /// The `frontendRequest` block returned by irmago on session start, used to
51+ /// communicate with the IRMA/Yivi frontend directly.
52+ ///
53+ /// Since pairing is mandatory by default for IRMA clients (irmago v0.13.0),
54+ /// the [`authorization`](Self::authorization) token is required to complete the
55+ /// pairing handshake.
56+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
57+ #[ cfg_attr( test, derive( PartialEq ) ) ]
58+ pub struct FrontendRequest {
59+ /// Authorization token used to authenticate to the frontend endpoints.
60+ pub authorization : String ,
61+ /// The lowest frontend protocol version the server supports, if reported.
62+ #[ serde(
63+ rename = "minProtocolVersion" ,
64+ default ,
65+ skip_serializing_if = "Option::is_none"
66+ ) ]
67+ pub min_protocol_version : Option < String > ,
68+ /// The highest frontend protocol version the server supports, if reported.
69+ #[ serde(
70+ rename = "maxProtocolVersion" ,
71+ default ,
72+ skip_serializing_if = "Option::is_none"
73+ ) ]
74+ pub max_protocol_version : Option < String > ,
3975}
4076
4177/// Token used to identify individual sessions on the server
@@ -188,3 +224,84 @@ impl IrmaClientBuilder {
188224 }
189225 }
190226}
227+
228+ #[ cfg( test) ]
229+ mod tests {
230+ use crate :: { FrontendRequest , SessionData } ;
231+
232+ #[ test]
233+ fn test_decode_session_data_with_frontend_request ( ) {
234+ let data = serde_json:: from_str :: < SessionData > (
235+ r#"
236+ {
237+ "token": "KzxuWKwL5KGLKr4uerws",
238+ "sessionPtr": {
239+ "u": "https://example.com/irma/session/abc",
240+ "irmaqr": "disclosing"
241+ },
242+ "frontendRequest": {
243+ "authorization": "O5Ld2vAr9pkz7ELzWqgM",
244+ "minProtocolVersion": "1.0",
245+ "maxProtocolVersion": "1.1"
246+ }
247+ }
248+ "# ,
249+ )
250+ . unwrap ( ) ;
251+
252+ assert_eq ! (
253+ data. frontend_request,
254+ Some ( FrontendRequest {
255+ authorization: "O5Ld2vAr9pkz7ELzWqgM" . into( ) ,
256+ min_protocol_version: Some ( "1.0" . into( ) ) ,
257+ max_protocol_version: Some ( "1.1" . into( ) ) ,
258+ } )
259+ ) ;
260+
261+ // Round-trips back to JSON without losing the frontend request.
262+ let reparsed =
263+ serde_json:: from_str :: < SessionData > ( & serde_json:: to_string ( & data) . unwrap ( ) ) . unwrap ( ) ;
264+ assert_eq ! ( reparsed. frontend_request, data. frontend_request) ;
265+ }
266+
267+ #[ test]
268+ fn test_decode_session_data_without_frontend_request ( ) {
269+ // Servers older than irmago v0.14.0 omit the frontendRequest block.
270+ let data = serde_json:: from_str :: < SessionData > (
271+ r#"
272+ {
273+ "token": "KzxuWKwL5KGLKr4uerws",
274+ "sessionPtr": {
275+ "u": "https://example.com/irma/session/abc",
276+ "irmaqr": "disclosing"
277+ }
278+ }
279+ "# ,
280+ )
281+ . unwrap ( ) ;
282+
283+ assert_eq ! ( data. frontend_request, None ) ;
284+
285+ // The field is skipped on serialization when absent.
286+ let json = serde_json:: to_string ( & data) . unwrap ( ) ;
287+ assert ! ( !json. contains( "frontendRequest" ) ) ;
288+ }
289+
290+ #[ test]
291+ fn test_decode_frontend_request_without_protocol_versions ( ) {
292+ // Only authorization is guaranteed to be useful; versions are optional.
293+ let request = serde_json:: from_str :: < FrontendRequest > (
294+ r#"{ "authorization": "O5Ld2vAr9pkz7ELzWqgM" }"# ,
295+ )
296+ . unwrap ( ) ;
297+
298+ assert_eq ! (
299+ request,
300+ FrontendRequest {
301+ authorization: "O5Ld2vAr9pkz7ELzWqgM" . into( ) ,
302+ min_protocol_version: None ,
303+ max_protocol_version: None ,
304+ }
305+ ) ;
306+ }
307+ }
0 commit comments