Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 94 additions & 14 deletions IETF-RFC.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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
Expand All @@ -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"`.
Expand All @@ -788,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)
Expand Down Expand Up @@ -916,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.
Expand Down Expand Up @@ -1085,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
Expand Down Expand Up @@ -1201,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 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
`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 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 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`.
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"`
Expand Down
23 changes: 23 additions & 0 deletions diagrams/code-flow-role-binding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```mermaid
flowchart TD
ET["exchange-token in discovery"]
TX["token-exchange in criteria"]
EP["tokenEndPoint in discovery"]
MS["must-exchange-token on the share"]

ET --> P["One provider-level code-flow capability"]
P --> S["Sending Server role"]
P --> R["Receiving Server role"]

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"]
```
15 changes: 15 additions & 0 deletions diagrams/receiver-discovery-policy.md
Original file line number Diff line number Diff line change
@@ -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"]
```
16 changes: 16 additions & 0 deletions diagrams/resource-access-paths.md
Original file line number Diff line number Diff line change
@@ -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
```
17 changes: 17 additions & 0 deletions diagrams/share-creation-preflight.md
Original file line number Diff line number Diff line change
@@ -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{"What is the sender policy for this share"}
I -- "Strict" --> J["Include must-exchange-token voluntarily"]
J --> F
I -- "Legacy" --> K["Send legacy share without must-exchange-token"]
```
7 changes: 4 additions & 3 deletions schemas/ocm-discovery.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand All @@ -43,7 +43,8 @@
},
"tokenEndPoint": {
"type": "string",
"format": "uri"
"format": "uri",
"description": "URL of the token endpoint hosted by the Sending Server."
}
},
"required": [
Expand Down
19 changes: 14 additions & 5 deletions spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -644,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
Expand Down
Loading