Skip to content

Commit aff8103

Browse files
authored
Merge pull request #17 from encryption4all/feat/expose-frontend-request
feat: expose frontendRequest block on SessionData
2 parents 3a6aa4d + 3cf6348 commit aff8103

3 files changed

Lines changed: 131 additions & 1 deletion

File tree

src/irmaclient.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ mod sessionresult;
55
mod util;
66

77
pub use error::Error;
8-
pub use irmaclient::{IrmaClient, IrmaClientBuilder, Qr, SessionData, SessionToken};
8+
pub use irmaclient::{
9+
FrontendRequest, IrmaClient, IrmaClientBuilder, Qr, SessionData, SessionToken,
10+
};
911
pub use sessionrequest::{
1012
AttributeRequest, ConDisCon, Credential, CredentialBuilder, DisclosureRequestBuilder,
1113
ExtendedIrmaRequest, IrmaRequest, IssuanceRequestBuilder, SignatureRequestBuilder,

tests/test_full_client_interaction.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ fn test_full_client_interaction() {
6969
.await
7070
.expect("Failed to start session");
7171

72+
// Since irmago v0.14.0 the server returns a frontendRequest block
73+
// with an authorization token; make sure we surface it.
74+
let frontend_request = session
75+
.frontend_request
76+
.as_ref()
77+
.expect("Expected a frontend_request on session start");
78+
assert!(
79+
!frontend_request.authorization.is_empty(),
80+
"frontend_request.authorization should not be empty"
81+
);
82+
7283
let status = client
7384
.status(&session.token)
7485
.await

0 commit comments

Comments
 (0)