Skip to content

Commit fafe9ae

Browse files
authored
Merge pull request #19 from encryption4all/feat/next-session-chained
feat: support chained sessions (nextSession) on ExtendedIrmaRequest
2 parents 8cf18dd + 6399751 commit fafe9ae

3 files changed

Lines changed: 89 additions & 3 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@
33
This crate provides a rust wrapper around the irma server rest API. It can be used to interface with an irma server for disclosure, signatures and issuance. Although its concept is inspired by the original irma crate (https://github.com/Wassasin/irmars) it was constructed from the ground up to provide a consistent interface with support for all of the major session types.
44

55
For documentation on the IRMA ecosystem, see [the IRMA documentation](https://irma.app/docs)
6+
7+
## Chained sessions
8+
9+
`ExtendedIrmaRequest` supports [chained (next) sessions](https://irma.app/docs/chained-sessions): set a follow-up requestor URL with `.next_session(url)` (serialized as `nextSession`), and the server starts the linked session immediately after the current one succeeds.
10+
11+
When using chained sessions in production, run an irmago server of **at least v0.19.0**. That release ships [GHSA-pv8v-c99h-c5q4](https://github.com/privacybydesign/irmago/security/advisories/GHSA-pv8v-c99h-c5q4), which tightens permission handling for next-session requests.

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ pub use irmaclient::{
1010
};
1111
pub use sessionrequest::{
1212
AttributeRequest, ConDisCon, Credential, CredentialBuilder, DisclosureRequestBuilder,
13-
ExtendedIrmaRequest, IrmaRequest, IssuanceRequestBuilder, SignatureRequestBuilder,
13+
ExtendedIrmaRequest, IrmaRequest, IssuanceRequestBuilder, NextSessionData,
14+
SignatureRequestBuilder,
1415
};
1516
pub use sessionresult::{
1617
AttributeStatus, DisclosedAttribute, ProofStatus, SessionResult, SessionStatus, SessionType,

src/sessionrequest.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,24 @@ impl IssuanceRequestBuilder {
438438
}
439439
}
440440

441+
/// Data describing a chained ("next") session to start after the current one
442+
/// succeeds. Mirrors the `NextSessionData` struct on irmago's `RequestorBaseRequest`
443+
/// (introduced in irmago v0.10.0); as of irmago v0.19.2 it carries a single `url`
444+
/// field. See <https://irma.app/docs/chained-sessions>.
445+
#[derive(Serialize, Deserialize, Debug, Clone)]
446+
#[cfg_attr(test, derive(PartialEq))]
447+
pub struct NextSessionData {
448+
/// URL from which to get the next session request once this session succeeds.
449+
pub url: String,
450+
}
451+
452+
impl NextSessionData {
453+
/// Create the data for a chained session pointing at the given requestor URL.
454+
pub fn new(url: String) -> NextSessionData {
455+
NextSessionData { url }
456+
}
457+
}
458+
441459
/// An IRMA request extended with extra information for the server on how to execute it.
442460
/// (Note: this interface is unstable, and might change significantly in the future)
443461
#[derive(Serialize, Deserialize, Debug, Clone)]
@@ -452,10 +470,38 @@ pub struct ExtendedIrmaRequest {
452470
/// URL on which to recieve updates as the session status changes
453471
#[serde(rename = "callbackUrl", skip_serializing_if = "Option::is_none")]
454472
pub callback_url: Option<String>,
473+
/// A chained session to start once this session succeeds. Serialized as
474+
/// `nextSession` to match irmago's `RequestorBaseRequest` and omitted when `None`.
475+
/// Recommended only with irmago server v0.19.0 or newer (see GHSA-pv8v-c99h-c5q4).
476+
#[serde(rename = "nextSession", skip_serializing_if = "Option::is_none")]
477+
pub next_session: Option<NextSessionData>,
455478
/// Inner request
456479
pub request: IrmaRequest,
457480
}
458481

482+
impl ExtendedIrmaRequest {
483+
/// Wrap an inner request without any extra server instructions.
484+
pub fn new(request: IrmaRequest) -> ExtendedIrmaRequest {
485+
ExtendedIrmaRequest {
486+
validity: None,
487+
timeout: None,
488+
callback_url: None,
489+
next_session: None,
490+
request,
491+
}
492+
}
493+
494+
/// Chain a follow-up session to be started by the server immediately after this
495+
/// one succeeds, pointing at the given requestor URL (irmago `nextSession`).
496+
///
497+
/// For production use this requires an irmago server of at least v0.19.0, which
498+
/// ships the tightened next-session permission handling from GHSA-pv8v-c99h-c5q4.
499+
pub fn next_session(mut self, url: String) -> ExtendedIrmaRequest {
500+
self.next_session = Some(NextSessionData::new(url));
501+
self
502+
}
503+
}
504+
459505
#[cfg(test)]
460506
mod tests {
461507
use std::time::Duration;
@@ -465,8 +511,8 @@ mod tests {
465511
use crate::CredentialBuilder;
466512

467513
use super::{
468-
AttributeRequest, Credential, DisclosureRequestBuilder, IssuanceRequestBuilder,
469-
SignatureRequestBuilder, TranslatedString,
514+
AttributeRequest, Credential, DisclosureRequestBuilder, ExtendedIrmaRequest,
515+
IssuanceRequestBuilder, NextSessionData, SignatureRequestBuilder, TranslatedString,
470516
};
471517

472518
#[test]
@@ -774,4 +820,37 @@ mod tests {
774820
serde_json::from_str(&serde_json::to_string(&req6).unwrap()).unwrap()
775821
);
776822
}
823+
824+
#[test]
825+
fn test_next_session() {
826+
let inner = DisclosureRequestBuilder::new()
827+
.add_discon(vec![vec![AttributeRequest::Simple("a.b.c.d".into())]])
828+
.build();
829+
830+
// Omitted entirely when not set.
831+
let req1 = ExtendedIrmaRequest::new(inner.clone());
832+
assert_eq!(
833+
"{\"request\":{\"@context\":\"https://irma.app/ld/request/disclosure/v2\",\"disclose\":[[[\"a.b.c.d\"]]]}}",
834+
serde_json::to_string(&req1).unwrap()
835+
);
836+
assert_eq!(
837+
req1,
838+
serde_json::from_str(&serde_json::to_string(&req1).unwrap()).unwrap()
839+
);
840+
841+
// Serialized as `nextSession` with a single `url` sub-field, matching irmago.
842+
let req2 = ExtendedIrmaRequest::new(inner).next_session("https://example.com/next".into());
843+
assert_eq!(
844+
"{\"nextSession\":{\"url\":\"https://example.com/next\"},\"request\":{\"@context\":\"https://irma.app/ld/request/disclosure/v2\",\"disclose\":[[[\"a.b.c.d\"]]]}}",
845+
serde_json::to_string(&req2).unwrap()
846+
);
847+
assert_eq!(
848+
req2.next_session,
849+
Some(NextSessionData::new("https://example.com/next".into()))
850+
);
851+
assert_eq!(
852+
req2,
853+
serde_json::from_str(&serde_json::to_string(&req2).unwrap()).unwrap()
854+
);
855+
}
777856
}

0 commit comments

Comments
 (0)