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
110 changes: 101 additions & 9 deletions IETF-RFC.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ contain the following information about its OCM API:
to the more secure (and possibly required) invite flow.
_ `"receive-code"` - to indicate that this OCM Server can receive a
`code` as part of a Share Creation Notification, and exchange it
for a bearer token at the Sending Server's `/token` API endpoint.
for a bearer token at the Sending Server's tokenEndPoint.
_ `"invite-wayf"` - to indicate that this OCM Server exposes a WAYF
Page to facilitate the Invite flow.
* OPTIONAL: criteria (array of string) - The criteria for accepting a
Expand Down Expand Up @@ -687,6 +687,11 @@ 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=example.com`.
* OPTIONAL: tokenEndPoint (string) - URL of the token endpoint where
the Sending Server can exchange a `code` for a bearer token.
Implementations that offer the `"receive-code"` capability MUST
provide this URL as well.
Example: `"https://my-cloud-storage.org/ocm/token"`.

# Share Creation Notification

Expand Down Expand Up @@ -759,7 +764,7 @@ To create a Share, the Sending Server SHOULD make a HTTP POST request
that the share does not expire.
* OPTIONAL code (string)
A nonce to be exchanged for a (potentially short-lived)
bearer token at the Sending Server's /token endpoint.
bearer token at the Sending Server's tokenEndPoint [RFC6749]
* REQUIRED protocol (object)
JSON object with specific options for each protocol.
The supported protocols are: - `webdav`, to access the data -
Expand Down Expand Up @@ -961,9 +966,10 @@ is as follows:
`resourceTypes[0].protocols.webdav` value is the
`<sender-ocm-path>` to be used in step 3.
2. If `code` is not empty, the receiver SHOULD make a signed POST
request to the `/token` path inside the Sending Server's OCM API, to
request to the path in the Sending Server’s tokenEndPoint, to
exchange the code for a short-lived bearer token, and then use that
bearer token to access the Resource.
bearer token to access the Resource (See the [Code Flow](
#code-flow) section).
3. If `protocol.name` = `webdav`, the receiver SHOULD inspect the
`protocol.options` property. If it contains a `sharedSecret`, as in
the [legacy example](
Expand Down Expand Up @@ -994,6 +1000,85 @@ Additionally, if `protocol.<protocolname>.requirements` includes
Party has been authenticated with MFA, or prompt the consumer in order
to elevate their session, if applicable.


# Code Flow

This section defines the procedure for issuing short-lived bearer access
tokens for use by the Receiving Server when accessing a resource shared
through OCM. The mechanism is aligned with the OAuth 2.0
*authorization_code* grant type but is performed entirely as a
server to server interaction between the Sending and Receiving Servers.
No user interaction or redirect is involved. [RFC6749]

## Token Request

To obtain an access token, the Receiving Server MUST send an HTTP POST
request to the Sending Server’s tokenEndPoint as discovered in the
OCM provider metadata, following section 4.4.2 of [RFC6749]. Here
follows an example of such POST request:

```
POST {tokenEndPoint} HTTP/1.1
Host: my-cloud-storage.org
Date: Wed, 05 Nov 2025 14:00:00 GMT
Content-Type: application/x-www-form-urlencoded
Digest: SHA-256=ok6mQ3WZzKc8nb7s/Jt2yY1uK7d2n8Zq7dhl3Q0s1xk=
Content-Length: 101
Signature-Input:
sig1=("@method" "@target-uri" "content-digest" "date"); \
created=1730815200; keyid="receiver.example.org#2025"; \
alg="rsa-sha256"
Signature: sig1=:
bM2sV2a4oM8pWc4Q8r9Zb8bQ7a2vH1kR9xT0yJ3uE4wO5lV6bZ1cP2rN3qD4tR5hC=:

grant_type=authorization_code&
client_id=receiver.example.org&
code=my_secret_code
```

The request MUST be signed using an HTTP Message Signature
[RFC9421]. The `client_id` identifies the Receiving Server and MUST be
set to its fully qualified domain name. The `code` parameter carries
the authorization code that was issued by the Sending Server in the
Share Creation Notification. It is allowed to send the additional
parameters defined in [RFC6749] for the `authorization_code` grant type,
but they MUST be ignored.

## Token Response

If the request is valid and the code is accepted, the Sending Server
MUST respond with HTTP 200 OK and a OAuth-compliant JSON object
containing the issued token:

```
{
"access_token": "8f3d3f26-f1e6-4b47-9e3e-9af6c0d4ad8b",
"token_type": "Bearer",
"expires_in": 300
}
```

The `access_token` is an opaque bearer credential with no internal
structure visible to the Receiving Server. The token authorizes the
Receiving Server to access the shared resource using the appropriate
transport protocol (e.g., WebDAV). The `expires_in` value indicates
the token lifetime in seconds. No `refresh_token` is issued, instead
the same request to the tokenEndPoint MUST be repeated before the
`access_token` has expired, to recieve a new `access_token` that can
then be used in the same manner.

## Error Responses

If the request is invalid, the Sending Server MUST return an HTTP 400
response with a JSON object containing an OAuth 2.0 error code
[RFC6749]:
```
{ "error": "invalid_request" }
```

Permitted error codes are `invalid_request`, `invalid_client`,
`invalid_grant`, `unauthorized_client` and `unsupported_grant_type`.

# Share Deletion

A `"SHARE_ACCEPTED"` notification followed by a `"SHARE_UNSHARED"`
Expand Down Expand Up @@ -1067,6 +1152,15 @@ The legacy format of an OCM Share Notification with shared secrets is
only provided for backwards compatibility with existing implementations.
Implementers SHOULD NOT use it and prefer short-lived tokens instead.

## Code Flow

All `{tokenEndPoint}` requests MUST be transmitted over HTTPS and
signed using HTTP Signatures. Bearer tokens MUST be treated as
confidential and never logged, persisted beyond their lifetime, or
transmitted over unsecured channels.



# References

## Normative References
Expand All @@ -1089,15 +1183,13 @@ Signatures](https://tools.ietf.org/html/rfc9421)", February 2024.
"[Uniform Resource Identifier (URI): Generic Syntax
](https://datatracker.ietf.org/doc/html/rfc3986)", January 2005

[RFC6749] Hardt, D. (ed), "[The OAuth 2.0 Authorization Framework](
https://datatracker.ietf.org/html/rfc6749)", October 2012.

[RFC8615] Nottingham, M. "[Well-Known Uniform Resource Identifiers
(URIs)](https://datatracker.ietf.org/doc/html/rfc8615)", May 2019


## Informative References

[RFC6749] Hardt, D. (ed), "[The OAuth 2.0 Authorization Framework](
https://datatracker.ietf.org/html/rfc6749)", October 2012.

# Appendix A: Multi-factor Authentication

If a Receiving Server exposes the capability `enforce-mfa`, it
Expand Down
49 changes: 31 additions & 18 deletions spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,20 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/token:
/{tokenEndPoint}:
post:
summary: Token Exchange endpoint
description: >
This optional endpoint allows to obtain a (potentially short-lived) bearer token in exchange for a code.
See [Resource Access](https://github.com/cs3org/OCM-API/blob/develop/IETF-RFC.md#resource-access)
for more details.
for more details. The actual endpoint URL is discovered via OCM provider metadata
(tokenEndPoint).
requestBody:
content:
application/json:
application/x-www-form-urlencoded:
schema:
$ref: "#/components/schemas/TokenRequest"
description: The JSON request body.
description: Form-encoded request body.
required: true
responses:
"200":
Expand All @@ -252,7 +253,7 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/TokenResponse"
"403":
"400":
description: Token denied.
content:
application/json:
Expand Down Expand Up @@ -466,6 +467,12 @@ components:
-----BEGIN PUBLIC KEY-----
MII...QDD
-----END PUBLIC KEY-----
tokenEndPoint:
type: string
description: >
Optional URL path of the Token Exchange endpoint to obtain bearer tokens in exchange for codes.
If the `receive-code` capability is exposed, the tokenEndPoint MUST be advertised in the discovery response.
example: /index.php/apps/sciencemesh/token
inviteAcceptDialog:
type: string
description: >
Expand Down Expand Up @@ -554,7 +561,7 @@ components:
type: string
description: |
A nonce to be exchanged for a (potentially short-lived) bearer token
at the Sending Server's `/token` endpoint.
at the Sending Server's {tokenEndPoint}.
protocol:
type: object
description: |
Expand Down Expand Up @@ -637,9 +644,9 @@ components:
MFA-authenticated. This requirement MAY be used if the
recipient provider exposes the `enforce-mfa` capability.
- `use-code` requires the recipient to exchange the given
`code` via a signed HTTPS request to `/token` at the Sending
Server, in order to get a short-lived token to be used for
subsequent access. This requirement MAY be used if the
`code` 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. This requirement MAY be used if the
recipient provider exposes the `receive-code` capability.
enum:
- mfa-enforced
Expand Down Expand Up @@ -842,6 +849,10 @@ components:
example: John Doe
TokenRequest:
type: object
required:
- grant_type
- client_id
- code
properties:
client_id:
type: string
Expand All @@ -854,8 +865,14 @@ components:
example: xyz
grant_type:
type: string
description: Must be set to 'ocm_authorization_code'
example: ocm_authorization_code
description: Must be set to 'authorization_code'
enum: ["authorization_code"]
example: authorization_code
redirect_uri:
type: string
description: >
Optional parameter that MUST be ignored by the server.
example: https://receiver.org/ocm/callback
TokenResponse:
type: object
properties:
Expand All @@ -865,13 +882,9 @@ components:
example: asdfgh
token_type:
type: string
description: Must be set to 'bearer'
example: bearer
description: Must be set to 'Bearer'
example: Bearer
expires_in:
type: number
description: Number of seconds before this access_token will need to be refreshed.
example: 3600
refresh_token:
type: string
description: A refresh token
example: qwertyuiop
example: 300
Loading