From 5f4b4506deca725e85d7022be5eb463ae029525b Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 10:09:09 +0000 Subject: [PATCH 1/8] fix(ocm-api): clarify discovery token roles Signed-off-by: Mahdi Baghbani --- IETF-RFC.md | 25 ++++++++++++++++--------- schemas/ocm-discovery.json | 7 ++++--- spec.yaml | 15 +++++++++++---- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/IETF-RFC.md b/IETF-RFC.md index a8d1dea..924813b 100644 --- a/IETF-RFC.md +++ b/IETF-RFC.md @@ -720,10 +720,11 @@ contain the following information about its OCM API: include one or more of the following items: - `"enforce-mfa"` - to indicate that this OCM Server can apply a Sending Server's MFA requirements for a Share on their behalf. - - `"exchange-token"` - to indicate that this OCM Server exposes a - [RFC6749]-compliant endpoint, which allows to exchange a secret - received in the protocol properties of a Share Creation Notification - for a short-lived bearer token. + - `"exchange-token"` - to indicate that this OCM Server supports the + OCM code flow via an [RFC6749]-compliant token endpoint. When this + OCM Server acts as Sending Server, it hosts `tokenEndPoint`. When it + acts as Receiving Server, it can honor inbound shares that require + token exchange. - `"http-sig"` - to indicate that this OCM Server supports [RFC9421] HTTP Message Signatures and advertises public keys in the format specified by [RFC7517] at the `/.well-known/jwks.json` @@ -751,9 +752,13 @@ contain the following information about its OCM API: for instance: - `"http-request-signatures"` - to indicate that API requests without http signatures will be rejected. - - `"token-exchange"` - to indicate that API requests without - token exchange will be rejected (see the [Code Flow](#code-flow) - section). + - `"token-exchange"` - to indicate that when this OCM Server acts + as Receiving Server, it requires the code flow for all inbound + shares. Shares that do not include `must-exchange-token` in + their `protocol.webdav.requirements` will be rejected. An + OCM Server advertising this criterium MUST also expose the + `exchange-token` capability. See the [Code Flow](#code-flow) + section. - `"denylist"` - some servers MAY be blocked based on their IP address - `"allowlist"` - unknown servers MAY be blocked based on their IP @@ -771,8 +776,10 @@ contain the following information about its OCM API: `"/index.php/apps/sciencemesh/accept"` is specified here then a WAYF Page SHOULD redirect the end-user to `/index.php/apps/sciencemesh/ accept?token=zi5kooKu3ivohr9a&providerDomain=cloud.example.org`. -* OPTIONAL: tokenEndPoint (string) - URL of the token endpoint where the - Sending Server can exchange a secret for a short-lived bearer token. +* OPTIONAL: tokenEndPoint (string) - URL of the token endpoint hosted by + this OCM Server. When this OCM Server acts as Sending Server, the + Receiving Server POSTs here to exchange a `sharedSecret` for a + short-lived bearer token. Implementations that offer the `"exchange-token"` capability MUST provide this URL as well. Example: `"https://cloud.example.org/ocm/token"`. diff --git a/schemas/ocm-discovery.json b/schemas/ocm-discovery.json index 4aeaf83..54a1578 100644 --- a/schemas/ocm-discovery.json +++ b/schemas/ocm-discovery.json @@ -22,14 +22,14 @@ }, "capabilities": { "type": "array", - "description": "Capabilities values of 'exchange-token', 'webdav-uri', 'protocol-object', 'invites', 'invite-wayf' defined in draft", + "description": "Capability values of 'enforce-mfa', 'exchange-token', 'http-sig', 'invites', 'invite-wayf', 'notifications', 'protocol-object', and 'webdav-uri' are defined in the draft", "items": { "type": "string" } }, "criteria": { "type": "array", - "description": "Criteria values of 'http-request-signatures', 'token-exchange', 'denlyist' and 'allowlist' are defined in draft", + "description": "Criteria values of 'http-request-signatures', 'token-exchange', 'denylist', 'allowlist', and 'invite' are defined in the draft", "items": { "type": "string" } @@ -43,7 +43,8 @@ }, "tokenEndPoint": { "type": "string", - "format": "uri" + "format": "uri", + "description": "URL of the token endpoint hosted by the Sending Server." } }, "required": [ diff --git a/spec.yaml b/spec.yaml index f1fa552..e406fe4 100644 --- a/spec.yaml +++ b/spec.yaml @@ -440,15 +440,22 @@ components: type: string format: uri description: > - Optional URL of the Token Exchange endpoint to obtain bearer tokens in exchange for codes. - If the `exchange-token` capability is exposed, the tokenEndPoint MUST be advertised in the discovery response. + Optional URL of the Token Exchange endpoint hosted by the + provider when acting as Sending Server. The Receiving Server + POSTs here to obtain bearer tokens in exchange for + authorization codes (shared secrets). If the `exchange-token` + capability is exposed, the tokenEndPoint MUST be advertised + in the discovery response. **Token Exchange API:** - This optional endpoint allows obtaining a (potentially short-lived) bearer token in exchange for a secret. + This optional endpoint allows the Receiving Server to obtain + a (potentially short-lived) bearer token in exchange for an + authorization code (shared secret). **HTTP Request:** - Method: POST - - URL: The URL discovered in this field + - URL: The URL hosted by the Sending Server and discovered in + this field - Content-Type: application/x-www-form-urlencoded - Body: TokenRequest schema (form-encoded, required) From 96a37455573994a1f4cea693737450f50b3e2c81 Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 10:21:00 +0000 Subject: [PATCH 2/8] fix(ocm-api): clarify share token flow Signed-off-by: Mahdi Baghbani --- IETF-RFC.md | 27 ++++++++++++++++++++++----- spec.yaml | 4 +++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/IETF-RFC.md b/IETF-RFC.md index 924813b..fa33a88 100644 --- a/IETF-RFC.md +++ b/IETF-RFC.md @@ -795,6 +795,21 @@ To create a Share, the Sending Server SHOULD make a HTTP POST request * using TLS * using httpsig [RFC9421] +Before constructing the notification, the Sending Server MUST query +the Receiving Server's OCM API Discovery endpoint. If the Receiving +Server advertises `token-exchange` in its `criteria` and the Sending +Server exposes the `exchange-token` capability with a `tokenEndPoint`, +the Sending Server MUST include `must-exchange-token` in +`protocol.webdav.requirements` and MUST NOT fall back to legacy +shared-secret access. If the Receiving Server advertises +`token-exchange` but the Sending Server does not expose the +`exchange-token` capability or does not have a `tokenEndPoint`, the +Sending Server MUST NOT create the share, as the Receiving Server +would reject any notification that lacks the code-flow requirement. +If the Receiving Server does not advertise `token-exchange` in its +`criteria`, the Sending Server MAY still include `must-exchange-token` +voluntarily. + ## Fields * REQUIRED shareWith (string) @@ -923,8 +938,10 @@ To create a Share, the Sending Server SHOULD make a HTTP POST request - `must-exchange-token` requires the recipient to exchange the given `sharedSecret` via a signed HTTPS request to the Sending Server's {tokenEndPoint} [RFC6749]. - This MAY be used if the recipient provider exposes the - `exchange-token` capability. + This MAY be used if the Sending Server exposes the + `exchange-token` capability and `tokenEndPoint`, and MUST be + included when the Receiving Server advertises `token-exchange` + in criteria. - OPTIONAL size (integer) The size of the resource to be transferred, useful especially in case of `datatx` access type. @@ -1092,9 +1109,9 @@ protocol required for access. The procedure is as follows: token for a short-lived bearer token, and only use that bearer token to access the Resource (See the [Code Flow](#code-flow) section). If the `must-exchange-token` requirement is not present - and the Discovery endpoint inspected at step 1. exposes the - `token-exchange` capability, the receiver MAY attempt to perform - the token exchange as above, but it MUST fall back to the following + and the discovery inspected at step 1 exposes the `exchange-token` + capability with a `tokenEndPoint`, the receiver MAY attempt the + token exchange as above, but it MUST fall back to the following steps should the process fail. 3.2. If it includes `must-use-mfa`, the Receiving Server MUST ensure that the Receiving Party has been authenticated with MFA, or prompt diff --git a/spec.yaml b/spec.yaml index e406fe4..69b878d 100644 --- a/spec.yaml +++ b/spec.yaml @@ -651,7 +651,9 @@ components: `sharedSecret` via a signed HTTPS request to tokenEndPoint at the Sending Server, in order to get a short-lived token to be used for subsequent access [RFC6749]. This requirement MAY be used if - the recipient provider exposes the `exchange-token` capability. + the Sending Server exposes the `exchange-token` capability and + `tokenEndPoint`, and MUST be included when the Receiving Server + advertises `token-exchange` in criteria. enum: - must-use-mfa - must-exchange-token From 94dca73fa6c0a8ac5745981a73e5a009c9413c8b Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 10:35:12 +0000 Subject: [PATCH 3/8] add(ocm-api): add code flow decision table Signed-off-by: Mahdi Baghbani --- IETF-RFC.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/IETF-RFC.md b/IETF-RFC.md index fa33a88..2f44697 100644 --- a/IETF-RFC.md +++ b/IETF-RFC.md @@ -1225,6 +1225,62 @@ response with a JSON object containing an OAuth 2.0 error code Permitted error codes are `invalid_request`, `invalid_client`, `invalid_grant`, `unauthorized_client` and `unsupported_grant_type`. +## Decision Table + +The following scenarios summarize the directional contract for code flow. +Scenarios 1-2 concern strict shares and depend on the Receiving +Server's capabilities. Scenarios 3-4 concern non-strict shares and +depend on the Sending Server's capabilities. + +1. If the Sending Server includes `must-exchange-token` in + `protocol.webdav.requirements` and the Receiving Server exposes the + `exchange-token` capability, strict token exchange is required + before the Resource is accessed. +2. If the Sending Server includes `must-exchange-token` and the + Receiving Server does not expose the `exchange-token` capability, + the Sending Server SHOULD NOT include that requirement, because the + Receiving Server may be unable to complete the exchange. +3. If the Sending Server omits `must-exchange-token` and exposes the + `exchange-token` capability with a `tokenEndPoint`, the Receiving + Server MAY attempt the token exchange described in step 3.1 of + [Resource Access](#resource-access) and MUST fall back to legacy + shared-secret access if that exchange fails. +4. If the Sending Server omits `must-exchange-token` and does not + expose the `exchange-token` capability, only legacy shared-secret + access is available. + +The following examples illustrate these outcomes end to end: + +1. Strict required code flow: Provider A acts as Sending Server and + exposes the `exchange-token` capability with a `tokenEndPoint`. + Provider B acts as Receiving Server and advertises both + `exchange-token` and `token-exchange`. After discovering B's + `token-exchange` criteria, A MUST include `must-exchange-token` in + `protocol.webdav.requirements`. B MUST exchange the + `sharedSecret` at A's `tokenEndPoint` and then use only the bearer + token to access the Resource. +2. Optional exchange with fallback: Provider A acts as Sending Server + and exposes the `exchange-token` capability with a `tokenEndPoint`. + Provider B does not advertise `token-exchange`, so A sends a share + without `must-exchange-token`. When B later accesses the Resource, + it MAY attempt the token exchange at A's `tokenEndPoint`, but if + that exchange fails it MUST fall back to the legacy + `sharedSecret`. +3. Legacy share to a code-flow-capable peer: Provider A does not + expose the `exchange-token` capability. Provider B does expose + `exchange-token`, so B is capable of honoring strict inbound shares + from other peers. Because A does not advertise a `tokenEndPoint`, + A can only send a legacy share and B can only use legacy + shared-secret access for that share. +4. Asymmetric role behavior: Provider A exposes `exchange-token` and + `token-exchange`, so it can require code flow for inbound shares + when it acts as Receiving Server. When A later acts as Sending + Server toward Provider B, and B does not advertise + `token-exchange`, A MAY omit `must-exchange-token`. B may then + attempt token exchange against A's `tokenEndPoint` or fall back to + legacy access. A therefore accepts strict inbound shares while + still choosing a legacy-compatible outbound share. + # Share Deletion A `"SHARE_ACCEPTED"` notification followed by a `"SHARE_UNSHARED"` From 0890fee1cfdb9480525fadf2af54dda18ec3fa06 Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 10:35:52 +0000 Subject: [PATCH 4/8] fix(ocm-api): tighten decision table prose Signed-off-by: Mahdi Baghbani --- IETF-RFC.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/IETF-RFC.md b/IETF-RFC.md index 2f44697..eb054f8 100644 --- a/IETF-RFC.md +++ b/IETF-RFC.md @@ -1227,10 +1227,11 @@ Permitted error codes are `invalid_request`, `invalid_client`, ## Decision Table -The following scenarios summarize the directional contract for code flow. -Scenarios 1-2 concern strict shares and depend on the Receiving -Server's capabilities. Scenarios 3-4 concern non-strict shares and -depend on the Sending Server's capabilities. +The directional contract depends first on whether the share is strict. +For strict shares, the Receiving Server's advertised behavior determines +whether the Sending Server can require code flow. For non-strict +shares, the Sending Server's advertised behavior determines whether +token exchange is available in addition to legacy access. 1. If the Sending Server includes `must-exchange-token` in `protocol.webdav.requirements` and the Receiving Server exposes the @@ -1242,14 +1243,13 @@ depend on the Sending Server's capabilities. Receiving Server may be unable to complete the exchange. 3. If the Sending Server omits `must-exchange-token` and exposes the `exchange-token` capability with a `tokenEndPoint`, the Receiving - Server MAY attempt the token exchange described in step 3.1 of - [Resource Access](#resource-access) and MUST fall back to legacy + Server MAY attempt token exchange first and MUST fall back to legacy shared-secret access if that exchange fails. 4. If the Sending Server omits `must-exchange-token` and does not expose the `exchange-token` capability, only legacy shared-secret access is available. -The following examples illustrate these outcomes end to end: +The following examples illustrate typical end-to-end outcomes: 1. Strict required code flow: Provider A acts as Sending Server and exposes the `exchange-token` capability with a `tokenEndPoint`. From fcee1e83da81bee2f5fefbaf818d77b369e6034b Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 12:04:22 +0000 Subject: [PATCH 5/8] add(ocm-api): introduce new diagrams for code flow and share creation processes Signed-off-by: Mahdi Baghbani --- diagrams/code-flow-role-binding.md | 26 ++++++++++++++++++++++++++ diagrams/resource-access-paths.md | 16 ++++++++++++++++ diagrams/share-creation-preflight.md | 17 +++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 diagrams/code-flow-role-binding.md create mode 100644 diagrams/resource-access-paths.md create mode 100644 diagrams/share-creation-preflight.md diff --git a/diagrams/code-flow-role-binding.md b/diagrams/code-flow-role-binding.md new file mode 100644 index 0000000..3c63f58 --- /dev/null +++ b/diagrams/code-flow-role-binding.md @@ -0,0 +1,26 @@ +```mermaid +flowchart LR + subgraph A["Terms used by the spec"] + A1["exchange-token in discovery"] + A2["token-exchange in criteria"] + A3["must-exchange-token on the share"] + A4["tokenEndPoint in discovery"] + end + + subgraph B["Reading this clarification proposes"] + B1["Provider supports the code flow"] + B2["As Sending Server, it hosts tokenEndPoint"] + B3["As Receiving Server, it can honor strict inbound shares"] + B4["Receiver side policy for inbound shares"] + B5["Per share strict contract"] + B6["Hosted by sender and called by receiver"] + end + + A1 --> B1 + B1 --> B2 + B1 --> B3 + A2 --> B4 + A3 --> B5 + A4 --> B6 + B4 --> B5 +``` diff --git a/diagrams/resource-access-paths.md b/diagrams/resource-access-paths.md new file mode 100644 index 0000000..a323d53 --- /dev/null +++ b/diagrams/resource-access-paths.md @@ -0,0 +1,16 @@ +```mermaid +flowchart TD + A["Receiving Server accesses the shared resource"] + A --> B["Inspect protocol.webdav.requirements"] + B --> C{"must-exchange-token present"} + + C -- "Yes" --> D["POST sharedSecret to the Sending Server tokenEndPoint"] + D --> E["Use only the bearer token for WebDAV access"] + + C -- "No" --> F{"Sender discovery exposes exchange-token and tokenEndPoint"} + F -- "Yes" --> G["Receiver may attempt token exchange"] + G --> H{"Exchange succeeds"} + H -- "Yes" --> E + H -- "No" --> I["Fall back to sharedSecret access"] + F -- "No" --> I +``` diff --git a/diagrams/share-creation-preflight.md b/diagrams/share-creation-preflight.md new file mode 100644 index 0000000..4065da5 --- /dev/null +++ b/diagrams/share-creation-preflight.md @@ -0,0 +1,17 @@ +```mermaid +flowchart TD + A["Sending Server wants to create a share"] + A --> B["Query Receiving Server discovery"] + B --> C{"Receiver advertises token-exchange"} + + C -- "Yes" --> D{"Sender exposes exchange-token and tokenEndPoint"} + D -- "Yes" --> E["Include must-exchange-token"] + E --> F["Create strict share"] + F --> G["Do not fall back to legacy access for that share"] + D -- "No" --> H["Do not create the share"] + + C -- "No" --> I{"Sender still wants strict code flow"} + I -- "Yes" --> J["Include must-exchange-token voluntarily"] + J --> F + I -- "No" --> K["Create legacy compatible share"] +``` From 1b13b9e35163c0ab61cbae1796ce0940a3ee57a3 Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 12:43:16 +0000 Subject: [PATCH 6/8] add: receiver side diagrams Signed-off-by: Mahdi Baghbani --- diagrams/receiver-discovery-policy.md | 15 +++++++++++++++ diagrams/receiver-share-handling.md | 17 +++++++++++++++++ diagrams/share-creation-preflight.md | 6 +++--- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 diagrams/receiver-discovery-policy.md create mode 100644 diagrams/receiver-share-handling.md diff --git a/diagrams/receiver-discovery-policy.md b/diagrams/receiver-discovery-policy.md new file mode 100644 index 0000000..5a5b91c --- /dev/null +++ b/diagrams/receiver-discovery-policy.md @@ -0,0 +1,15 @@ +```mermaid +flowchart TD + A["Provider acts as Receiving Server"] + A --> B{"Does it support the code flow"} + + B -- "Yes" --> C["Advertise exchange-token"] + C --> D{"Does it require code flow for all inbound shares"} + D -- "Yes" --> E["Advertise token-exchange in criteria"] + E --> F["Reject inbound shares that omit must-exchange-token"] + D -- "No" --> G["Do not advertise token-exchange"] + G --> H["Strict inbound shares may still be accepted share by share"] + + B -- "No" --> I["Do not advertise exchange-token"] + I --> J["Cannot honor strict inbound shares"] +``` diff --git a/diagrams/receiver-share-handling.md b/diagrams/receiver-share-handling.md new file mode 100644 index 0000000..995e54d --- /dev/null +++ b/diagrams/receiver-share-handling.md @@ -0,0 +1,17 @@ +```mermaid +flowchart TD + A["Receiving Server gets a share and later accesses the resource"] + A --> B["Inspect protocol.webdav.requirements"] + B --> C{"must-exchange-token present"} + + C -- "Yes" --> D["Exchange sharedSecret at the sender tokenEndPoint"] + D --> E["Use only the bearer token"] + + C -- "No" --> F["Inspect sender discovery"] + F --> G{"Sender exposes exchange-token and tokenEndPoint"} + G -- "Yes" --> H["Receiver may try token exchange"] + H --> I{"Exchange succeeds"} + I -- "Yes" --> E + I -- "No" --> J["Fall back to sharedSecret access"] + G -- "No" --> J +``` diff --git a/diagrams/share-creation-preflight.md b/diagrams/share-creation-preflight.md index 4065da5..1e060dc 100644 --- a/diagrams/share-creation-preflight.md +++ b/diagrams/share-creation-preflight.md @@ -10,8 +10,8 @@ flowchart TD F --> G["Do not fall back to legacy access for that share"] D -- "No" --> H["Do not create the share"] - C -- "No" --> I{"Sender still wants strict code flow"} - I -- "Yes" --> J["Include must-exchange-token voluntarily"] + C -- "No" --> I{"What is the sender policy for this share"} + I -- "Strict" --> J["Include must-exchange-token voluntarily"] J --> F - I -- "No" --> K["Create legacy compatible share"] + I -- "Legacy" --> K["Send legacy share without must-exchange-token"] ``` From 6139e8dea897be0f84ada2fd33366144c5031aaf Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 12:46:40 +0000 Subject: [PATCH 7/8] add: receiver side diagrams Signed-off-by: Mahdi Baghbani --- diagrams/receiver-share-handling.md | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 diagrams/receiver-share-handling.md diff --git a/diagrams/receiver-share-handling.md b/diagrams/receiver-share-handling.md deleted file mode 100644 index 995e54d..0000000 --- a/diagrams/receiver-share-handling.md +++ /dev/null @@ -1,17 +0,0 @@ -```mermaid -flowchart TD - A["Receiving Server gets a share and later accesses the resource"] - A --> B["Inspect protocol.webdav.requirements"] - B --> C{"must-exchange-token present"} - - C -- "Yes" --> D["Exchange sharedSecret at the sender tokenEndPoint"] - D --> E["Use only the bearer token"] - - C -- "No" --> F["Inspect sender discovery"] - F --> G{"Sender exposes exchange-token and tokenEndPoint"} - G -- "Yes" --> H["Receiver may try token exchange"] - H --> I{"Exchange succeeds"} - I -- "Yes" --> E - I -- "No" --> J["Fall back to sharedSecret access"] - G -- "No" --> J -``` From 84737cf15e718f9509d54030966a558f04971ec7 Mon Sep 17 00:00:00 2001 From: Mahdi Baghbani Date: Mon, 30 Mar 2026 13:33:26 +0000 Subject: [PATCH 8/8] docs(diagrams): add code flow clarification maps Signed-off-by: Mahdi Baghbani --- diagrams/code-flow-role-binding.md | 41 ++++++++++++++---------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/diagrams/code-flow-role-binding.md b/diagrams/code-flow-role-binding.md index 3c63f58..87b2cf9 100644 --- a/diagrams/code-flow-role-binding.md +++ b/diagrams/code-flow-role-binding.md @@ -1,26 +1,23 @@ ```mermaid -flowchart LR - subgraph A["Terms used by the spec"] - A1["exchange-token in discovery"] - A2["token-exchange in criteria"] - A3["must-exchange-token on the share"] - A4["tokenEndPoint in discovery"] - end +flowchart TD + ET["exchange-token in discovery"] + TX["token-exchange in criteria"] + EP["tokenEndPoint in discovery"] + MS["must-exchange-token on the share"] - subgraph B["Reading this clarification proposes"] - B1["Provider supports the code flow"] - B2["As Sending Server, it hosts tokenEndPoint"] - B3["As Receiving Server, it can honor strict inbound shares"] - B4["Receiver side policy for inbound shares"] - B5["Per share strict contract"] - B6["Hosted by sender and called by receiver"] - end + ET --> P["One provider-level code-flow capability"] + P --> S["Sending Server role"] + P --> R["Receiving Server role"] - A1 --> B1 - B1 --> B2 - B1 --> B3 - A2 --> B4 - A3 --> B5 - A4 --> B6 - B4 --> B5 + S --> S1["Hosts tokenEndPoint"] + EP --> S1 + + R --> R1["Can honor inbound strict shares"] + R --> R2["Receiver policy for inbound shares"] + TX --> R2 + R2 --> R3["If advertised, inbound shares must include must-exchange-token"] + R3 --> MS + + MS --> M1["Strict share contract"] + M1 --> M2["Receiver must exchange sharedSecret before access"] ```