From b3e31232a169c35090895aeb1981fb2d13c5a06c Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Tue, 7 Jul 2026 17:33:10 +0200 Subject: [PATCH 1/6] Add tokenExchange OAuth2 flow (RFC 8693), mirroring OAI/OpenAPI-Specification#5428 Adds tokenExchange to the pre-defined OAuth2 flow keys: tokenUrl required, scopes as for all flows. Includes the JSON Schema rule, a collection example, and a CHANGELOG entry. --- CHANGELOG.md | 7 +++++++ README.md | 10 ++++++++-- examples/collection.json | 12 ++++++++++++ json-schema/schema.json | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b8011e..9b08b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `tokenExchange` pre-defined OAuth2 flow key for OAuth 2.0 Token Exchange + ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)), with `tokenUrl` required, + mirroring the proposed OpenAPI addition + [OAI/OpenAPI-Specification#5428](https://github.com/OAI/OpenAPI-Specification/pull/5428) + ## [v1.1.0] - 2023-04-28 ### Changed diff --git a/README.md b/README.md index 745fc69..e13728c 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ library can be described, as well as a custom signed URL authentication scheme. | `name` | string | `apiKey` | **REQUIRED.** The name of the header, query, or cookie parameter to be used. | | `in` | string | `apiKey` | **REQUIRED.** The location of the API key (`query` \| `header` \| `cookie`). | | `scheme` | string | `http` | **REQUIRED.** The name of the HTTP Authorization scheme to be used in the [Authorization header as defined in RFC7235](https://tools.ietf.org/html/rfc7235#section-5.1). The values used SHOULD be registered in the [IANA Authentication Scheme registry](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml). (`basic` \| `bearer` \| `digest` \| `dpop` \| `hoba` \| `mutual` \| `negotiate` \| `oauth` (1.0) \| `privatetoken` \| `scram-sha-1` \| `scram-sha-256` \| `vapid`) | -| `flows` | Map | `oauth2`, `signedUrl` | **REQUIRED.** Scenarios an API client performs to get an access token from the authorization server. For `oauth2` the following keys are pre-defined for the corresponding OAuth flows: `authorizationCode` \| `implicit` \| `password ` \| `clientCredentials`. The OAuth2 Flow Object applies for `oauth2`, the Signed URL Object applies to `signedUrl`. | +| `flows` | Map | `oauth2`, `signedUrl` | **REQUIRED.** Scenarios an API client performs to get an access token from the authorization server. For `oauth2` the following keys are pre-defined for the corresponding OAuth flows: `authorizationCode` \| `implicit` \| `password ` \| `clientCredentials` \| `tokenExchange`. The OAuth2 Flow Object applies for `oauth2`, the Signed URL Object applies to `signedUrl`. | | `openIdConnectUrl` | string | `openIdConnect` | **REQUIRED.** OpenID Connect URL to discover OpenID configuration values. This MUST be in the form of a URL. | The column "Applies to" specifies for which values of `type` the fields only apply. @@ -89,11 +89,17 @@ They are also only required in this context. Based on the [OpenAPI OAuth Flow Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object). Allows configuration of the supported OAuth Flows. +The `tokenExchange` flow corresponds to OAuth 2.0 Token Exchange as defined in +[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) (proposed for the OpenAPI +OAuth Flows Object in [OAI/OpenAPI-Specification#5428](https://github.com/OAI/OpenAPI-Specification/pull/5428)): +the client presents a token obtained through another scheme or flow (for example an +OpenID Connect identity token) at the `tokenUrl` and receives a different token back, +such as short-lived, scoped credentials for direct data access. | Field Name | Type | Description | | ------------------ | ----------------------- | ------------------------------------------------------------ | | `authorizationUrl` | `string` | **REQUIRED** for parent keys: `"implicit"`, `"authorizationCode"`. The authorization URL to be used for this flow. This MUST be in the form of a URL. | -| `tokenUrl` | `string` | **REQUIRED** for parent keys: `"password"`, `"clientCredentials"`, `"authorizationCode"`. The token URL to be used for this flow. This MUST be in the form of a URL. | +| `tokenUrl` | `string` | **REQUIRED** for parent keys: `"password"`, `"clientCredentials"`, `"authorizationCode"`, `"tokenExchange"`. The token URL to be used for this flow. This MUST be in the form of a URL. | | `scopes` | Map<`string`, `string`> | **REQUIRED.** The available scopes for the authentication scheme. A map between the scope name and a short description for it. The map MAY be empty. | | `refreshUrl` | `string` | The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL. | diff --git a/examples/collection.json b/examples/collection.json index 42d8352..58cef32 100644 --- a/examples/collection.json +++ b/examples/collection.json @@ -45,6 +45,18 @@ } } }, + "token_exchange": { + "type": "oauth2", + "description": "exchange an identity token (e.g. from an openIdConnect scheme) for short-lived, scoped credentials (OAuth 2.0 Token Exchange, RFC 8693)", + "flows": { + "tokenExchange": { + "tokenUrl": "https://example.com/credentials/token", + "scopes": { + "read:example": "Read the example data" + } + } + } + }, "signed_url_auth": { "type": "signedUrl", "description": "Requires an authentication API", diff --git a/json-schema/schema.json b/json-schema/schema.json index cd051c9..95fca86 100644 --- a/json-schema/schema.json +++ b/json-schema/schema.json @@ -298,7 +298,7 @@ "authorizationUrl" ] }, - "^(password|clientCredentials|authorizationCode)*$": { + "^(password|clientCredentials|authorizationCode|tokenExchange)*$": { "required": [ "tokenUrl" ] From 877ee12f938aca22ad5c2ce0aae3992bf784f390 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 13 Jul 2026 19:02:14 +0200 Subject: [PATCH 2/6] Add subjectTokenScheme to the tokenExchange flow Links the exchange to the scheme that supplies its subject_token, so a generic client (e.g. STAC Browser) can resolve the order of the steps from the document instead of hardcoding it. Optional; RECOMMENDED when more than one scheme is declared. --- README.md | 3 +++ examples/collection.json | 3 ++- json-schema/schema.json | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e13728c..5a0e091 100644 --- a/README.md +++ b/README.md @@ -95,11 +95,14 @@ OAuth Flows Object in [OAI/OpenAPI-Specification#5428](https://github.com/OAI/Op the client presents a token obtained through another scheme or flow (for example an OpenID Connect identity token) at the `tokenUrl` and receives a different token back, such as short-lived, scoped credentials for direct data access. +The `subjectTokenScheme` field links the exchange to the scheme that supplies its input +token, making the multi-step flow machine-discoverable; clients MUST use it when present. | Field Name | Type | Description | | ------------------ | ----------------------- | ------------------------------------------------------------ | | `authorizationUrl` | `string` | **REQUIRED** for parent keys: `"implicit"`, `"authorizationCode"`. The authorization URL to be used for this flow. This MUST be in the form of a URL. | | `tokenUrl` | `string` | **REQUIRED** for parent keys: `"password"`, `"clientCredentials"`, `"authorizationCode"`, `"tokenExchange"`. The token URL to be used for this flow. This MUST be in the form of a URL. | +| `subjectTokenScheme` | `string` | Applies to the parent key `"tokenExchange"`. The key of the `auth:schemes` entry whose token the client presents as the RFC 8693 `subject_token` at the `tokenUrl`. RECOMMENDED whenever more than one scheme is declared, so that a generic client can resolve the order of the steps from the document instead of hardcoding it. | | `scopes` | Map<`string`, `string`> | **REQUIRED.** The available scopes for the authentication scheme. A map between the scope name and a short description for it. The map MAY be empty. | | `refreshUrl` | `string` | The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL. | diff --git a/examples/collection.json b/examples/collection.json index 58cef32..f65a4e6 100644 --- a/examples/collection.json +++ b/examples/collection.json @@ -47,10 +47,11 @@ }, "token_exchange": { "type": "oauth2", - "description": "exchange an identity token (e.g. from an openIdConnect scheme) for short-lived, scoped credentials (OAuth 2.0 Token Exchange, RFC 8693)", + "description": "exchange the token obtained via the `oauth` scheme for short-lived, scoped credentials (OAuth 2.0 Token Exchange, RFC 8693)", "flows": { "tokenExchange": { "tokenUrl": "https://example.com/credentials/token", + "subjectTokenScheme": "oauth", "scopes": { "read:example": "Read the example data" } diff --git a/json-schema/schema.json b/json-schema/schema.json index 95fca86..1a79bf1 100644 --- a/json-schema/schema.json +++ b/json-schema/schema.json @@ -375,6 +375,10 @@ "type": "string", "format": "uri" }, + "subjectTokenScheme": { + "title": "Key of the auth:schemes entry whose token is presented as the subject_token (tokenExchange flow)", + "type": "string" + }, "refreshUrl": { "title": "The URL to be used for obtaining refresh tokens", "type": "string", From 62049e5f84f9e8b6401a1c2a43890ab7d3b64869 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 13 Jul 2026 19:21:43 +0200 Subject: [PATCH 3/6] Reference the token_exchange scheme from an asset in the example Shows the full chain end-to-end: asset -> auth:refs -> token_exchange -> subjectTokenScheme -> oauth. Previously the scheme was declared but unreferenced. --- examples/collection.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/collection.json b/examples/collection.json index f65a4e6..8b2f39a 100644 --- a/examples/collection.json +++ b/examples/collection.json @@ -105,6 +105,17 @@ "auth:refs": [ "signed_url_auth" ] + }, + "example_exchanged": { + "href": "https://storage.example.com/private-bucket/file.xyz", + "title": "Asset read with credentials obtained via token exchange", + "type": "application/vnd.example", + "roles": [ + "data" + ], + "auth:refs": [ + "token_exchange" + ] } }, "item_assets": { From 4ee45da90c4e9fd747fb028ed92b795ac93dd528 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 13 Jul 2026 19:31:22 +0200 Subject: [PATCH 4/6] Move the token-exchange demo to a dedicated, focused example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts examples/collection.json to its pristine state and adds examples/collection-token-exchange.json: identity scheme (oidc) + exchange scheme linked via subjectTokenScheme + two gated assets sharing one exchange. The identity scheme is intentionally unreferenced by any auth:refs — the case that motivates the field. Follows the multi-example convention of other extensions (e.g. datacube). README points to it. --- README.md | 2 + examples/collection-token-exchange.json | 81 +++++++++++++++++++++++++ examples/collection.json | 24 -------- 3 files changed, 83 insertions(+), 24 deletions(-) create mode 100644 examples/collection-token-exchange.json diff --git a/README.md b/README.md index 5a0e091..5b2cccd 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,8 @@ Definition for a request parameter. ## Examples `auth:schemes` may be referenced identically in a STAC Asset or Link objects. Examples of these two use-cases are provided below. +A complete, focused example of the two-step token-exchange pattern (identity scheme + exchange scheme linked via `subjectTokenScheme`) +is provided in [examples/collection-token-exchange.json](examples/collection-token-exchange.json). ### Schema definitions diff --git a/examples/collection-token-exchange.json b/examples/collection-token-exchange.json new file mode 100644 index 0000000..95a1a25 --- /dev/null +++ b/examples/collection-token-exchange.json @@ -0,0 +1,81 @@ +{ + "type": "Collection", + "stac_version": "1.0.0", + "id": "collection-token-exchange", + "title": "Gated collection — two-step access via OAuth 2.0 Token Exchange", + "description": "Focused example of the two-step access pattern: the STAC metadata (this document) is public, while the data assets live in access-controlled object storage. Step 1: the client signs in via the `oidc` scheme and obtains an identity token. Step 2: it presents that token as the RFC 8693 `subject_token` at the `token_exchange` scheme's `tokenUrl` (declared by `subjectTokenScheme`) and receives short-lived, scoped credentials to read the assets directly. Note that the `oidc` scheme is intentionally not referenced by any `auth:refs`: it exists only as the exchange's input, which is exactly what `subjectTokenScheme` makes machine-discoverable.", + "license": "proprietary", + "stac_extensions": [ + "https://stac-extensions.github.io/authentication/v1.1.0/schema.json" + ], + "auth:schemes": { + "oidc": { + "type": "openIdConnect", + "description": "Step 1 — identity. Sign in via any OIDC client flow (e.g. Authorization Code + PKCE) to obtain an identity token.", + "openIdConnectUrl": "https://auth.example.com/.well-known/openid-configuration" + }, + "token_exchange": { + "type": "oauth2", + "description": "Step 2 — credentials (OAuth 2.0 Token Exchange, RFC 8693). POST the identity token obtained via the `oidc` scheme as `subject_token` to the `tokenUrl`; receive short-lived, scoped credentials for direct reads of this collection's assets. One exchanged credential covers all assets below.", + "flows": { + "tokenExchange": { + "tokenUrl": "https://credentials.example.com/oauth/token", + "subjectTokenScheme": "oidc", + "scopes": { + "read:data": "Read this collection's data assets" + } + } + } + } + }, + "extent": { + "spatial": { + "bbox": [ + [ + -3.9, + 40.3, + -3.5, + 40.6 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2025-01-01T00:00:00Z", + null + ] + ] + } + }, + "links": [ + { + "href": "https://example.com/examples/collection-token-exchange.json", + "rel": "self" + } + ], + "assets": { + "data": { + "href": "https://storage.example.com/private-bucket/data.parquet", + "title": "Data file (access-controlled)", + "type": "application/vnd.apache.parquet", + "roles": [ + "data" + ], + "auth:refs": [ + "token_exchange" + ] + }, + "metadata": { + "href": "https://storage.example.com/private-bucket/metadata.json", + "title": "Table metadata (access-controlled; same exchanged credentials)", + "type": "application/json", + "roles": [ + "metadata" + ], + "auth:refs": [ + "token_exchange" + ] + } + } +} diff --git a/examples/collection.json b/examples/collection.json index 8b2f39a..42d8352 100644 --- a/examples/collection.json +++ b/examples/collection.json @@ -45,19 +45,6 @@ } } }, - "token_exchange": { - "type": "oauth2", - "description": "exchange the token obtained via the `oauth` scheme for short-lived, scoped credentials (OAuth 2.0 Token Exchange, RFC 8693)", - "flows": { - "tokenExchange": { - "tokenUrl": "https://example.com/credentials/token", - "subjectTokenScheme": "oauth", - "scopes": { - "read:example": "Read the example data" - } - } - } - }, "signed_url_auth": { "type": "signedUrl", "description": "Requires an authentication API", @@ -105,17 +92,6 @@ "auth:refs": [ "signed_url_auth" ] - }, - "example_exchanged": { - "href": "https://storage.example.com/private-bucket/file.xyz", - "title": "Asset read with credentials obtained via token exchange", - "type": "application/vnd.example", - "roles": [ - "data" - ], - "auth:refs": [ - "token_exchange" - ] } }, "item_assets": { From 5720ec7bc271795b0ee52ae48a7c9a2ac484958f Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 24 Aug 2026 02:55:15 +0200 Subject: [PATCH 5/6] Require subjectTokenScheme for the tokenExchange flow The field is what makes the two-step exchange machine-discoverable: without it a client can find the tokenUrl but cannot tell which of the declared schemes produces the token it must present as the RFC 8693 subject_token. Left optional, every client has to invent its own fallback and the spec defines none, so make it required for the flow and enforce that in the schema. Also document the grant_type and the RFC 8693 response, and note that subject_token_type follows from the referenced scheme's type so it needs no field of its own. Drop the reference to OAI/OpenAPI-Specification#5428, which is parked pending the OpenAPI security spec split; RFC 8693 is the normative anchor. --- CHANGELOG.md | 7 ++++--- README.md | 27 +++++++++++++++++++-------- json-schema/schema.json | 5 +++++ 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b08b52..7f63f9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `tokenExchange` pre-defined OAuth2 flow key for OAuth 2.0 Token Exchange - ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)), with `tokenUrl` required, - mirroring the proposed OpenAPI addition - [OAI/OpenAPI-Specification#5428](https://github.com/OAI/OpenAPI-Specification/pull/5428) + ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)), with `tokenUrl` required +- `subjectTokenScheme` field on the `tokenExchange` flow, naming the `auth:schemes` + entry whose token the client presents as the RFC 8693 `subject_token`; required for + that flow, so the multi-step exchange is discoverable from the document ## [v1.1.0] - 2023-04-28 diff --git a/README.md b/README.md index 5b2cccd..b31189d 100644 --- a/README.md +++ b/README.md @@ -90,19 +90,30 @@ They are also only required in this context. Based on the [OpenAPI OAuth Flow Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object). Allows configuration of the supported OAuth Flows. The `tokenExchange` flow corresponds to OAuth 2.0 Token Exchange as defined in -[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) (proposed for the OpenAPI -OAuth Flows Object in [OAI/OpenAPI-Specification#5428](https://github.com/OAI/OpenAPI-Specification/pull/5428)): -the client presents a token obtained through another scheme or flow (for example an -OpenID Connect identity token) at the `tokenUrl` and receives a different token back, -such as short-lived, scoped credentials for direct data access. -The `subjectTokenScheme` field links the exchange to the scheme that supplies its input -token, making the multi-step flow machine-discoverable; clients MUST use it when present. +[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693). +The client presents a token obtained through another scheme (for example an OpenID +Connect identity token) at the `tokenUrl` as the `subject_token`, with +`grant_type=urn:ietf:params:oauth:grant-type:token-exchange`, and receives a different +security token back, such as short-lived, scoped credentials for direct data access. +The response is the one defined in +[RFC 8693, Section 2.2.1](https://datatracker.ietf.org/doc/html/rfc8693#section-2.2.1), +which reports the kind of token issued in `issued_token_type`. + +`subjectTokenScheme` names the scheme that supplies the input token, which is what makes +the multi-step flow machine-discoverable: without it a client can find the `tokenUrl` but +cannot tell which of the declared schemes produces the token it has to present. +Publishers MUST include it in a `tokenExchange` flow, and clients MUST use it when +present. +The RFC 8693 `subject_token_type` parameter follows from the referenced scheme's `type`, +so it needs no separate field: for example +`urn:ietf:params:oauth:token-type:id_token` for `openIdConnect`, or +`urn:ietf:params:oauth:token-type:access_token` for `oauth2`. | Field Name | Type | Description | | ------------------ | ----------------------- | ------------------------------------------------------------ | | `authorizationUrl` | `string` | **REQUIRED** for parent keys: `"implicit"`, `"authorizationCode"`. The authorization URL to be used for this flow. This MUST be in the form of a URL. | | `tokenUrl` | `string` | **REQUIRED** for parent keys: `"password"`, `"clientCredentials"`, `"authorizationCode"`, `"tokenExchange"`. The token URL to be used for this flow. This MUST be in the form of a URL. | -| `subjectTokenScheme` | `string` | Applies to the parent key `"tokenExchange"`. The key of the `auth:schemes` entry whose token the client presents as the RFC 8693 `subject_token` at the `tokenUrl`. RECOMMENDED whenever more than one scheme is declared, so that a generic client can resolve the order of the steps from the document instead of hardcoding it. | +| `subjectTokenScheme` | `string` | **REQUIRED** for parent key: `"tokenExchange"`. The key of the `auth:schemes` entry whose token the client presents as the RFC 8693 `subject_token` at the `tokenUrl`. Lets a generic client resolve the order of the steps from the document instead of hardcoding it. | | `scopes` | Map<`string`, `string`> | **REQUIRED.** The available scopes for the authentication scheme. A map between the scope name and a short description for it. The map MAY be empty. | | `refreshUrl` | `string` | The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL. | diff --git a/json-schema/schema.json b/json-schema/schema.json index 1a79bf1..f94db21 100644 --- a/json-schema/schema.json +++ b/json-schema/schema.json @@ -302,6 +302,11 @@ "required": [ "tokenUrl" ] + }, + "^tokenExchange$": { + "required": [ + "subjectTokenScheme" + ] } } } From 91c620edf9e9df644073ea8e85a7a9ef571bfe6c Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 24 Aug 2026 02:55:15 +0200 Subject: [PATCH 6/6] Trim the token-exchange example to foreground the two edges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The descriptions narrated the flow at such length that the fields carrying it — auth:refs on the asset and subjectTokenScheme on the flow — were the least prominent thing in the document, which invited reading the chain as prose-only. Cut the prose to what no field expresses and drop the asset titles; the auth structure itself is unchanged. --- examples/collection-token-exchange.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/collection-token-exchange.json b/examples/collection-token-exchange.json index 95a1a25..403226f 100644 --- a/examples/collection-token-exchange.json +++ b/examples/collection-token-exchange.json @@ -3,7 +3,7 @@ "stac_version": "1.0.0", "id": "collection-token-exchange", "title": "Gated collection — two-step access via OAuth 2.0 Token Exchange", - "description": "Focused example of the two-step access pattern: the STAC metadata (this document) is public, while the data assets live in access-controlled object storage. Step 1: the client signs in via the `oidc` scheme and obtains an identity token. Step 2: it presents that token as the RFC 8693 `subject_token` at the `token_exchange` scheme's `tokenUrl` (declared by `subjectTokenScheme`) and receives short-lived, scoped credentials to read the assets directly. Note that the `oidc` scheme is intentionally not referenced by any `auth:refs`: it exists only as the exchange's input, which is exactly what `subjectTokenScheme` makes machine-discoverable.", + "description": "Public STAC metadata, access-controlled assets. The asset points at the scheme that guards it (`auth:refs`); that scheme points at the identity whose token it exchanges (`subjectTokenScheme`). The `oidc` scheme guards nothing on its own: it is the exchange's input.", "license": "proprietary", "stac_extensions": [ "https://stac-extensions.github.io/authentication/v1.1.0/schema.json" @@ -11,12 +11,11 @@ "auth:schemes": { "oidc": { "type": "openIdConnect", - "description": "Step 1 — identity. Sign in via any OIDC client flow (e.g. Authorization Code + PKCE) to obtain an identity token.", "openIdConnectUrl": "https://auth.example.com/.well-known/openid-configuration" }, "token_exchange": { "type": "oauth2", - "description": "Step 2 — credentials (OAuth 2.0 Token Exchange, RFC 8693). POST the identity token obtained via the `oidc` scheme as `subject_token` to the `tokenUrl`; receive short-lived, scoped credentials for direct reads of this collection's assets. One exchanged credential covers all assets below.", + "description": "RFC 8693 token exchange: present the `oidc` identity token as the `subject_token` at the `tokenUrl` and receive short-lived, scoped credentials covering every asset below.", "flows": { "tokenExchange": { "tokenUrl": "https://credentials.example.com/oauth/token", @@ -57,7 +56,6 @@ "assets": { "data": { "href": "https://storage.example.com/private-bucket/data.parquet", - "title": "Data file (access-controlled)", "type": "application/vnd.apache.parquet", "roles": [ "data" @@ -68,7 +66,6 @@ }, "metadata": { "href": "https://storage.example.com/private-bucket/metadata.json", - "title": "Table metadata (access-controlled; same exchanged credentials)", "type": "application/json", "roles": [ "metadata"