Aggregator Specification

Living Document,

Previous Versions:
Issue Tracking:
GitHub
Inline In Spec
Editor:
(Ghent University - imec)

Abstract

This document has the specification of the aggregator.

1. Introduction

This specification defines the Aggregator Protocol, an HTTP-based interface that lets a client create and manage Aggregator Instances and configure Aggregator Services that execute data transformations. Transformations are described and discovered using the Function Ontology (FnO) [FNO]; a service is configured by referencing a transformation (or execution graph) and providing its inputs, after which the client can retrieve the derived result from the service’s advertised location.

TODO clarify whether Aggregator Services may reference transformations not advertised in the server/instance transformation catalogs, and how servers validate and authorize execution of arbitrary FnO graphs.

Clients start from the Aggregator Server Description at the server base URL to discover the registration endpoint, supported registration flows, and the server’s transformation catalog (§ 4 Aggregator Server Metadata). Using the registration endpoint, a client creates (or manages) an Aggregator Instance (§ 5.1 Aggregator Registration Endpoint) and then follows the instance’s Aggregator Description to find the instance’s service collection and transformations endpoint (§ 7.1 Aggregator Description and § 8 Aggregator Service Management).

All management operations are authenticated and authorized. This protocol integrates with OAuth 2.0 / OpenID Connect [OIDC-Core] for identity and uses UMA-style authorization [UMA] for protected resources, scopes, and tickets as described in § 6 Aggregator Security Model (Authentication & Authorization).

Servers MUST support JSON (application/json) where specified and MAY additionally provide semantically annotated RDF representations (e.g., JSON-LD or Turtle) via HTTP content negotiation. The Aggregator vocabulary (§ 9 Vocabulary) provides stable IRIs for classes and predicates used throughout the specification.

2. Definitions

This section defines terminology used throughout this specification. Where applicable, terms are aligned with external specifications such as WebID Profiles, OpenID Connect (OIDC) [OIDC-Core], OAuth 2.0 [RFC6749], and User-Managed Access (UMA) [UMA].

2.1. Core Roles and Components

2.2. Identity, Authorization, and Tokens

2.3. Namespaces

The following namespace prefixes are used throughout this specification and SHOULD appear in RDF serializations (e.g., Turtle, JSON-LD contexts):

3. Architecture & Resource Model

This section describes the high-level architecture of an Aggregator deployment and the resource model exposed by this specification. It connects the terminology in § 2 Definitions to the endpoint definitions in later sections.

3.1. Actors and Responsibilities

The protocol distinguishes the following parties:

3.2. Resource Types

This specification defines HTTP resources at three levels:

3.3. Base URLs and Addressing

An implementation MUST define a stable base URL for:

All paths in this specification are expressed relative to either the server base URL or the instance base URL:

An implementation MAY use either of the following instance addressing patterns:

If an implementation supports multiple patterns, it MUST ensure that URLs returned in representations are self-consistent for a given instance (i.e., all links for that instance share the same instance base URL). Clients SHOULD treat server-provided URLs as authoritative and avoid constructing URLs by string concatenation. Unless a path is explicitly fixed, the concrete URLs shown in this document serve as illustrative defaults; deployments MAY use different locations as long as the relevant metadata resources advertise the authoritative links.

3.4. Discovery and Entry Points

3.5. Security Boundaries

All security requirements are defined in § 6 Aggregator Security Model (Authentication & Authorization). At a high level:

TODO clarify the boundary between the Aggregator Instance (management API) and the Aggregator Service output resources (the location URLs), including whether outputs are always protected by the same Authorization Server and UMA policies.

3.6. Example URL Layout (Non-normative)

Given an Aggregator Server at https://aggregator.example/ and a path-based Aggregator instance at https://aggregator.example/abc123/:

4. Aggregator Server Metadata

This section describes the public endpoints exposed by the Aggregator Server for discovery and metadata retrieval. Except for the Aggregator Server Description at the server base URL, implementations MAY expose the remaining endpoints at deployment-specific URLs; the server description document MUST include absolute URLs for each resource so that clients can discover them.

4.1. Aggregator Server Description

The Aggregator Server Description is a public metadata document that allows clients to discover the Aggregator Server’s endpoints and capabilities. This document MUST be available at the server base URL (i.e., {aggregator-server-url}/), MUST be accessible without authentication, and MUST provide at least the following information. Each JSON member is paired with an RDF predicate from § 9 Vocabulary so the document can also be served as JSON-LD or other RDF formats using content negotiation based on [RFC9110]:

In semantically annotated representations, the Aggregator Server Description MUST state that the described resource has RDF type aggr:AggregatorServer (§ 9.1.3 aggr:AggregatorServer) (e.g., via @type in JSON-LD or a aggr:AggregatorServer in Turtle). Clients MAY rely on this type statement when consuming semantic representations.

registration_endpoint (REQUIRED):

The value is a string containing the absolute URL of the Aggregator Registration Endpoint (§ 5.1 Aggregator Registration Endpoint); in the RDF representations, this member maps to the predicate aggr:registrationEndpoint (§ 9.2.11 aggr:registrationEndpoint).

supported_registration_types (REQUIRED):

The value is a JSON array of strings identifying the supported registration flow tokens at registration_endpoint; in the RDF representations, each entry maps to an aggr:supportedRegistrationType triple (§ 9.2.12 aggr:supportedRegistrationType) whose object is the corresponding flow class IRI.

Each member MUST be one of the registration flow tokens defined in § 5.1 Aggregator Registration Endpoint:

version (REQUIRED):

The value is a string containing the version of the Aggregator specification that the server adheres to; in the RDF representations, this member maps to the predicate aggr:specVersion (§ 9.2.13 aggr:specVersion).

client_identifier (REQUIRED):

The value is a string containing the absolute URL of the Client Identifier Document (§ 4.2 Client Identifier Document); in the RDF representations, this member maps to the predicate aggr:clientIdentifier (§ 9.2.14 aggr:clientIdentifier).

transformation_catalog (REQUIRED):

The value is a string containing the absolute URL of the Public Transformation Catalog (§ 4.3 Public Transformation Catalog); in the RDF representations, this member maps to the predicate aggr:transformationCatalog (§ 9.2.15 aggr:transformationCatalog).

{
  "@context": {
    "aggr": "https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#",
    "provision": "aggr:ProvisionFlow",
    "authorization_code": "aggr:AuthorizationCodeFlow",
    "client_credentials": "aggr:ClientCredentialsFlow",
    "device_code": "aggr:DeviceCodeFlow",
    "registration_endpoint": {
      "@id": "aggr:registrationEndpoint",
      "@type": "@id"
    },
    "supported_registration_types": {
      "@id": "aggr:supportedRegistrationType",
      "@type": "@vocab"
    },
    "version": {
      "@id": "aggr:specVersion"
    },
    "client_identifier": {
      "@id": "aggr:clientIdentifier",
      "@type": "@id"
    },
    "transformation_catalog": {
      "@id": "aggr:transformationCatalog",
      "@type": "@id"
    }
  },
  "@id": "https://aggregator.example/",
  "@type": "aggr:AggregatorServer",
  "registration_endpoint": "https://aggregator.example/registration",
  "supported_registration_types": [
    "provision",
    "authorization_code"
  ],
  "version": "1.0.0",
  "client_identifier": "https://aggregator.example/client.json",
  "transformation_catalog": "https://aggregator.example/transformations"
}

4.2. Client Identifier Document

Endpoint that exposes the client identifier of the aggregator used for authorization. Servers MAY host this document at any URL; the client_identifier property in the Aggregator Server Description MUST contain the authoritative absolute URL. The Client Identifier Document MUST conform to the Solid specification [SOLID-OIDC] and uses the fields from the OpenID Connect Dynamic Client Registration specification [OIDC-Registration]. Only for the aggregator the redirect_uris property is OPTIONAL instead of required, as multiple clients can create an Aggregator on the same Aggregator Server. Adding this property can allow an Aggregator Server implementation to restrict which clients can create aggregators on the server.

4.3. Public Transformation Catalog

This endpoint returns a RDF document describing the transformations supported by the aggregator server on a server level. Servers MAY publish this catalog at any deployment-specific URL; the Aggregator Server Description MUST advertise it via the transformation_catalog field. This RDF document SHOULD be exposed using content negotiation based on HTTP. The transformations MUST be described using FnO [FNO].

@prefix aggr: <https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#> .
@prefix trans: <http://aggregator.example.org/transformations#> .
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<> a aggr:TransformationCollection ;
    dct:title "Aggregator transformations" ;
    aggr:hasTransformation trans:AggregateSources .
    aggr:hasTransformation trans:AggregateSources2 .

trans:AggregateSources
    a                   fno:Function ;
    fno:name            "A Aggregator Service that combines a list of sources"^^xsd:string ;
    fno:expects         ( trans:Sources ) ;
    fno:returns         ( trans:Result ) .

trans:Sources
    a             fno:Parameter ;
    fno:predicate trans:sources ;
    fno:type      rdf:List ;
    fno:required  "true"^^xsd:boolean.

trans:Result
    a             fno:Output ;
    fno:predicate trans:result ;
    fno:type      xsd:string .

The specificity of the transformation is up to the server owner. The transformations CAN have a link to a implementation with fno:Implementation if needed. External FnO functions MAY be used using rdfs:seeAlso. Pre defined pipelines MAY be used using fno:Compositions.

TODO how do we allow an aggregator that can run any transformation?

5. Aggregator Management

This section describes how Aggregator Instances are managed at the Aggregator Server level. Deployments define their own authorization policy for these endpoints, but they MUST require authenticated requests so the Aggregator provider can authorize a user to manage their aggregators. The tokens used in these requests (IDP_client_token) prove the identity of the user and the client (Client Identifier) used to access the Aggregator Server.

All representations defined in this section MUST be accessible as JSON using application/json. Servers MAY additionally expose semantically annotated RDF representations (for example JSON-LD or Turtle) using HTTP content negotiation based on [RFC9110].

Deployments MAY expose the same functionality at different paths, provided that the Aggregator Server Description advertises the authoritative URLs.

5.1. Aggregator Registration Endpoint

This section specifies the behavior of the registration_endpoint advertised by the Aggregator Server Description (§ 4.1 Aggregator Server Description). The endpoint supports creating Aggregator Instances, replacing the stored token set for an existing instance, and deleting an instance.

The endpoint MUST reject unauthenticated requests with 401 Unauthorized.

TODO specify whether and how IDP_client_token can be sender-constrained (e.g., DPoP).

POST

Creates an Aggregator Instance or replaces the stored token set for an existing instance. The request body MUST be a JSON object with Content-Type: application/json and MUST include:

  • registration_type (REQUIRED): The value is a string token; it MUST be one of the supported_registration_types advertised in the Aggregator Server Description (§ 4.1 Aggregator Server Description). The following string tokens are defined, each corresponding to an RDF class in the Aggregator vocabulary (§ 9 Vocabulary) for semantically annotated representations:

  • aggregator_id (string, OPTIONAL): When present, the request targets an existing Aggregator Instance and the server MUST replace its stored access token and refresh token with a new set obtained from the Identity Provider (IdP). This is not a refresh-token grant; it is a full re-authentication to obtain a fresh access token and refresh token.

Depending on registration_type, additional members are defined:

  • registration_type: "provision"

    • No additional members are required. ISSUE: TODO specify that the Aggregator Server should register an account at the IdP for the new WebID, and a account at an Authorization Server for UMA to manage aggregator resource permissions.

  • registration_type: "authorization_code"

    • authorization_server (string, REQUIRED): URL of the UMA Authorization Server that governs the user’s resources.

    The authorization_code flow has two POST messages:

    • Start request (bootstraps PKCE [RFC7636]): The request MUST NOT include code, redirect_uri, or state. The server MUST respond with 201 Created and a JSON object containing:

      • client_id (string, REQUIRED): The client identifier (dereferenceable client metadata document).

      • code_challenge (string, REQUIRED)

      • code_challenge_method (string, REQUIRED)

      • state (string, REQUIRED) The server MAY also include IdP discovery hints (for example issuer or authorization_endpoint) if the client cannot determine them through other means.

    • Finish request (redeems the authorization code): The request MUST include:

      • code (string, REQUIRED): The authorization code issued by the IdP.

      • redirect_uri (string, REQUIRED): The redirect URI used in the authorization request.

      • state (string, REQUIRED): The state returned by the start request.

  • registration_type: "client_credentials"

    • authorization_server (string, REQUIRED): URL of the UMA Authorization Server that governs the user’s resources.

    • webid (string, REQUIRED): The WebID that the Aggregator Instance should act as.

    • client_id (string, REQUIRED)

    • client_secret (string, REQUIRED)

  • registration_type: "device_code"

    • Not specified.

For successful POST requests that create/update an Aggregator Instance (i.e., provision, client_credentials, and the authorization_code finish request), the server MUST respond with:

  • 201 Created when it created a new Aggregator Instance (i.e., no aggregator_id was provided).

  • 200 OK when it replaced the token set for an existing Aggregator Instance (i.e., aggregator_id was provided).

For successful POST requests that create/update an Aggregator Instance (i.e., all types except the authorization_code start request), the response MUST be a JSON object and MUST include:

  • aggregator_id (string, REQUIRED): Opaque identifier for the Aggregator Instance (used for subsequent management operations).

  • authorization_server (string, OPTIONAL): Included when relevant for the registration type.

The response SHOULD also include:

  • aggregator (string, OPTIONAL): Absolute URL of the Aggregator Instance base URL that dereferences to the Aggregator Description (§ 7.1 Aggregator Description).

The server MUST NOT return any IdP access tokens, refresh tokens, or client credentials to the client.

DELETE

Deletes an existing Aggregator Instance. The request MUST include Authorization: Bearer <IDP_client_token>. The request body MUST be a JSON object with Content-Type: application/json and MUST include:

  • aggregator_id (string, REQUIRED): Opaque identifier for the Aggregator Instance to delete.

If deletion succeeds the server MUST respond with 204 No Content.

For error conditions, the server MUST respond with:

5.2. Aggregator Management Flows (Non-normative)

This section gives non-normative examples of how a client can use the registration_endpoint to create, delete, and re-authenticate an Aggregator Instance.

5.2.1. Creation provision Flow

The provision flow allows clients to create an Aggregator with its own identity. This lets resource owners target access-control policies at the aggregator’s dedicated WebID instead of having the aggregator impersonate another user’s WebID.

1. Client starts flow with Aggregator Server

The client calls the registration endpoint authenticated with its IDP_client_token.

POST /registration HTTP/1.1
Authorization: Bearer <IDP_client_token>
Content-Type: application/json

{
  "registration_type": "provision"
}

2. Aggregator Server provisions a WebID and registers it at the IDP

The Aggregator Server creates a WebID document that conforms to the WebID Profile specification [WEBID-PROFILE] and registers a new account with an IDP for that WebID. Using the credentials of this new account the Aggregator Server CAN perform a client credentials flow to obtain the IDP_aggregator_token (and accompanying refresh token) to authorize the aggregator acting under its own WebID.

3. Aggregator Server creates an aggregator

Using the obtained tokens, the Aggregator Server creates an aggregator linked to the user, and returns the aggregator description (§ 7.1 Aggregator Description). The aggregator SHOULD NOT give these tokens or client credentials to the client.

5.2.2. Creation authorization_code Flow

The authorization_code flow allows clients to create an aggregator that acts on behalf of the end-user, but with a token that is scoped specifically for the aggregator.

PlantUML Diagram

1. Client starts flow with Aggregator Server

The client begins by asking the Aggregator to bootstrap an authorization_code registration and indicate which authorization server should be used. The Aggregator responds with the public parameters required for the OIDC authorization request.

POST /registration HTTP/1.1
Authorization: Bearer <IDP_client_token>
Content-Type: application/json

{
  "registration_type": "authorization_code",
  "authorization_server": "https://as.example"
}

1.1 Aggregator dereferences the WebID to discover the IDP

Using the UMA authorization context, the Aggregator dereferences the user’s WebID profile document to locate the Solid/OIDC issuer metadata and determine which IDP endpoints must be used for the next steps.

1.2 Aggregator responds with public parameters

After finishing WebID discovery and storing the PKCE verifier/state, the Aggregator returns the public parameters required for the IDP authorization request.

HTTP/1.1 201 Created
Content-Type: application/json

{
  "client_id": "https://aggregator.example/client.jsonld",
  "code_challenge": "1uLSZp2...",
  "code_challenge_method": "S256",
  "state": "1eb7c8f5..."
}

The Aggregator generates the PKCE verifier/challenge pair plus a random state, persists them together with the pending registration, and returns only the public portions (client_id, code_challenge, state) to the client application. The authorization_server value identifies the UMA Authorization Server (AS) that governs resource policies; once the Aggregator evaluates the user’s authorization token it can dereference its WebID and determine which Identity Provider (IDP) must be used for the subsequent OIDC exchange.

2. Client sends the end-user through the IDP authorization endpoint

Using the information supplied by the Aggregator, the client constructs an authorization request against the Identity Provider. The redirect_uri is under control of the client application and MUST already be registered in the JSON-LD client metadata document hosted at the dereferenceable client_id.

GET https://idp.example/authorize?
    response_type=code&
    client_id=https%3A%2F%2Faggregator.example%2Fclient.jsonld&
    redirect_uri=https%3A%2F%2Fapp.example%2Fcallback&
    scope=openid%20webid%20offline_access&
    code_challenge=1uLSZp2...&
    code_challenge_method=S256&
    state=1eb7c8f5...

3. User authenticates and consents at the IDP

The IDP performs its usual login and consent screens, after which it issues an authorization_code tied to the Aggregator’s confidential client.

4. IDP redirects the user agent back to the client’s redirect_uri

HTTP/1.1 302 Found
Location: https://app.example/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=1eb7c8f5...

5. Client posts the authorization code back to the Aggregator

The client sends the code, redirect URI, and echoed state to the registration endpoint so the Aggregator can finish the flow.

POST /registration HTTP/1.1
Authorization: Bearer <IDP_client_token>
Content-Type: application/json

{
  "registration_type": "authorization_code",
  "code": "SplxlOBeZQQYbYS6WxSbIA",
  "redirect_uri": "https://app.example/callback",
  "state": "1eb7c8f5..."
}

5.1 Aggregator dereferences the client metadata

The Aggregator dereferences the client_id JSON-LD document to confirm the registered redirect URIs, contact metadata, and other security requirements, then verifies that the supplied redirect_uri belongs to that set and that the returned state matches the stored nonce.

5.2 Aggregator redeems the authorization code at the IDP token endpoint

POST /token HTTP/1.1
Host: idp.example
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <aggregator-client-auth>

grant_type=authorization_code&
code=SplxlOBeZQQYbYS6WxSbIA&
redirect_uri=https%3A%2F%2Fapp.example%2Fcallback&
client_id=https%3A%2F%2Faggregator.example%2Fclient.jsonld&
code_verifier=Hjs8...stored...

The IDP verifies the authorization_code, ensures the redirect_uri matches the original authorization request, and recomputes the PKCE challenge from the supplied code_verifier. If everything matches, it returns:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "<IDP_aggregator_token>",
  "refresh_token": "<refresh_token>",
  "token_type": "Bearer",
  "expires_in": 3600
}

5.3 Aggregator finalizes the account and responds

Using the issued tokens, the Aggregator creates the aggregator account linked to the user and returns the aggregator description (§ 7.1 Aggregator Description).

HTTP/1.1 201 Created
Content-Type: application/json

{
  "aggregator_id": "agg-7890",
  "authorization_server": "https://as.example",
  "aggregator": "https://aggregator.example/aggregators/agg-7890/"
}

5.2.3. Creation client_credentials Flow

The client_credentials flow allows clients to create an aggregator by using the OAuth2 Client Credentials Flow to obtain an OIDC token from an Identity Provider (IDP) server.

NOTE: This flow gives the aggregator complete access to the user’s resources and policies without user consent. This flow should only be used in trusted environments, or when a user creates an account for the aggregator.

1. Client starts flow with Aggregator Server

The client explicitly asks the Aggregator to act with full credentials for the provided WebID, indicating which UMA Authorization Server manages the protected resources. Because this flow hands the Aggregator long-lived client credentials, it should only be used when the user intentionally provisions the Aggregator as a trusted service account.

POST /registration HTTP/1.1
Authorization: Bearer <IDP_client_token>
Content-Type: application/json

{
  "registration_type": "client_credentials",
  "authorization_server": "https://as.example",
  "webid": "https://user.example/webid#me",
  "client_id": "aggregator-service-account",
  "client_secret": "s3cr3t-client-secret"
}

2. Aggregator Server performs client credentials grant with the IDP

The Aggregator uses the supplied client_id/client_secret (plus the WebID context) to authenticate against the Identity Provider’s token endpoint and runs an OAuth 2.0 client_credentials grant to mint an IDP_aggregator_token scoped to the user’s WebID.

POST /token HTTP/1.1
Host: idp.example
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <aggregator-client-auth>

grant_type=client_credentials&
webid=https%3A%2F%2Fuser.example%2Fwebid%23me&
scope=openid%20webid%20offline_access

The IDP issues access and refresh tokens that grant the Aggregator the same capabilities as the user’s own credentials.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "<IDP_aggregator_token>",
  "refresh_token": "<refresh_token>",
  "token_type": "Bearer",
  "expires_in": 7200
}

3. Aggregator Server creates (or updates) the aggregator account

Armed with the IDP_aggregator_token, the Aggregator persists the account for the specified WebID, associates it with the requesting user, and returns the aggregator description (§ 7.1 Aggregator Description) to the client.

5.2.4. Creation device_code Flow

This flow is not yet specified.

5.2.5. Token Update Flow

This flow allows users to replace the stored access token and refresh token for an existing Aggregator Instance. This is not a refresh-token grant: even refresh tokens can expire, so the client repeats the original registration flow to obtain a new set of tokens (and refresh tokens) for the Aggregator Instance.

The flow is the same as creating an Aggregator Instance but an aggregator_id member is provided in the start request. The exact steps depend on the registration_type used when creating the Aggregator Instance. For example, for the authorization_code flow:

POST /registration HTTP/1.1
Authorization: Bearer <IDP_client_token>
Content-Type: application/json
{
    "registration_type": "authorization_code",
    "aggregator_id": "agg-7890"
}

5.2.6. Aggregator Deletion Flow

This flow allows users to delete an existing Aggregator Instance by sending a DELETE request to the registration_endpoint with the aggregator_id member.

DELETE /registration HTTP/1.1
Authorization: Bearer <IDP_client_token>
Content-Type: application/json
{
    "aggregator_id": "agg-7890"
}

6. Aggregator Security Model (Authentication & Authorization)

This section describes how the Aggregator handles authentication and authorization for:

The Aggregator relies on the Authorization for Data Spaces (A4DS) specification [A4DS] to authenticate Clients and authorize access to resources. For streaming or non-HTTP interfaces, the Aggregator MAY additionally use the Service Authorization for Data Spaces (SA4DS) specification [SA4DS]. All Aggregator endpoints are protected using User-Managed Access (UMA) [UMA].

NOTE: The following behavior extends the A4DS specification and is intended to be incorporated in a future version of A4DS.

TODO we should maybe split this up into a normative section and a non-normative explanation with sequence diagrams.

6.1. Upstream Access (Requesting)

TODO: how does the Aggregator tell the Authorization Server (AS) which transformation it intends to perform on the data?

Equipped with an identity token (ID token) from an Identity Provider (IdP) as described in § 5.1 Aggregator Registration Endpoint, the Aggregator Service follows the Authorization for Data Spaces (A4DS) specification to obtain access tokens for upstream Resource Servers.

PlantUML Diagram

1. Requesting the upstream resource without token The Aggregator Service requests the upstream resource without an access token. If the resource is protected with UMA, the Upstream Resource Server responds with a 401 Unauthorized status and a UMA ticket.

GET /resource/123 HTTP/1.1
Host: upstream.example.org

1.1 Upstream Resource Server requests ticket from Upstream Authorization Server

The Upstream Resource Server requests a UMA ticket from its Upstream Authorization Server.

1.2 Upstream Resource Server returns ticket

The Upstream Resource Server returns the UMA ticket to the Aggregator Service.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="solid", as_uri="https://upstream.as.example.org/uma", ticket="tkt-URS"

2. Requesting an access token from the Upstream Authorization Server

Using the ID token obtained from the IdP during registration, the Aggregator Service requests an access token from the Upstream Authorization Server (of the Upstream Resource Server). In this request, the Aggregator Service SHOULD request the additional scope urn:knows:uma:scopes:derivation-creation to enable the Aggregator functionality. The Upstream Authorization Server MAY require additional claims to issue the access token.

TODO: if this resource has already been requested, the Upstream Authorization Server already has a derivation_resource_id for this Aggregator. Does the Aggregator need to request a new one, or can it reuse the existing one?

POST /token HTTP/1.1
Host: upstream.as.example.org
Content-Type: application/json
{
    "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
    "ticket": "tkt-URS",
    "scope": "urn:knows:uma:scopes:derivation-creation",
    "claim_tokens": [
      {
        "claim_token": "<IDP_aggregator_token>",
        "claim_token_format": "http://openid.net/specs/openid-connect-core-1_0.html#IDToken"
      }
    ]
}

2.1 Upstream Authorization Server validates the ID token

The Upstream Authorization Server validates the ID token with the Identity Provider that issued it.

2.2 Upstream Authorization Server returns access token and derivation resource identifier

Adding the derivation-creation scope signals to the Authorization Server that the Aggregator intends to create a derived resource based on the requested upstream resource. The Authorization Server MUST then include a derivation_resource_id in the response. The derivation_resource_id is a unique identifier that the Aggregator will use to reference this upstream resource when creating derived resources. The Authorization Server can link this identifier to the Aggregator’s identity to manage and track derived resources.

HTTP/1.1 200 OK
Content-Type: application/json
{
    "access_token": "<upstream_access_token>",
    "derivation_resource_id": "handle-id-1"
}

3. Accessing the upstream resource with token

The Aggregator Service then requests the resource from the Upstream Resource Server using the access token obtained from the Upstream Authorization Server. This access token MAY be used multiple times until it expires or is revoked.

4. Resource registration of the Aggregator Service

Finally, the Aggregator Service MUST update the resource registration at its own Authorization Server to signal that it used this derivation_resource_id to create derived resources. The Authorization Server MUST expire all previous access tokens for this resource.

PUT /resource-registration/agg-service-123 HTTP/1.1
Host: as.example.org
Content-Type: application/json
{
  "resource_id": "agg-service-123",
  "resource_relations": {
    "prov:wasDerivedFrom": [
      {
        "issuer": "https://as.example.org",
        "derivation_resource_id": "handle-id-1"
      }
    ]
  }
}

If a resource isn’t used anymore by the Aggregator Service, the Aggregator Service SHOULD update the resource registration to remove the derivation_resource_id from the prov:wasDerivedFrom relations, and MAY expire all previous access tokens for this resource. Also the Aggregator Service SHOULD delete the resource from its Upstream Resource Server, by following the Resource ID deletion procedure.

6.2. Client Access (Serving)

When a Client requests access to resources from the Aggregator Service, the Aggregator Service acts as a Resource Server and relies on UMA to authorize access. For each incoming request:

  1. The Client sends a request for a (derived) resource to the Aggregator Service, with or without a valid access token.

  2. The Aggregator Service requests a UMA ticket for that resource from its Authorization Server and, if necessary, returns a 401 Unauthorized response containing the ticket to the Client.

  3. The Client interacts with the Aggregator Authorization Server (and, if required, upstream Authorization Servers) to obtain the access tokens needed to access the derived resource.

  4. Once authorization succeeds, the Client retries the request to the Aggregator Service with a valid access token, and the Aggregator Service returns the derived resource.

PlantUML Diagram

1. Client requests derived resource from Aggregator Service without token The Client sends a request to the Aggregator Service for a derived resource without including a valid Requesting Party Token (RPT), or with an RPT that does not grant sufficient permission.

GET /aggregator/derived-resource-123 HTTP/1.1
Host: agg.example.org

When a Client requests access to a derived resource from the Aggregator Service, a normal UMA flow follows.

1.1 Aggregator Service requests UMA ticket from its Authorization Server The Aggregator Service, acting as a UMA Resource Server, requests a UMA ticket for the requested derived resource from its Authorization Server. The Aggregator includes the internal identifier of the derived resource in the permission request. During ticket creation, the Aggregator SHOULD validate that the derivation_resource_id used to create the derived resource is still valid. This validation can be delegated to the Authorization Server by including the derivation_resource_id (and its issuer) in the permission request, so that the Authorization Server can verify it with the upstream Authorization Server.

If the derivation_resource_id is still valid, the Authorization Server returns a UMA ticket as usual. If it is no longer valid (for example, because the upstream Authorization Server has removed the referenced resource), the Aggregator MUST treat the derived resource as invalid and MUST recreate the Aggregator Service (including its resource registrations).

1.2 Aggregator Service returns 401 Unauthorized with ticket If the Client did not present a valid RPT, or if the RPT does not cover the requested permission, the Aggregator Service returns a 401 Unauthorized response containing the UMA ticket issued by its Authorization Server.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="aggregator", as_uri="https://agg-as.example.org/uma", ticket="tkt-A2"

2. Client presents ticket to Aggregator Authorization Server The Client discovers the Aggregator Authorization Server (for example, via the as_uri parameter in the WWW-Authenticate header) and sends a UMA grant request to exchange the ticket for an RPT. The Client includes any claim tokens it already has (for example, its ID token) in the request.

{
  "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
  "ticket": "tkt-A2",
  "claim_tokens": [
    {
      "claim_token": "<ID_token>",
      "claim_token_format": "http://openid.net/specs/openid-connect-core-1_0.html#IDToken"
    }
  ]
}

2.1 Aggregator Authorization Server introspects Client access tokens The Aggregator Authorization Server validates the claim tokens (for example, by introspecting access tokens and/or verifying ID tokens) and evaluates its authorization policies for the requested derived resource.

2.2 Aggregator Authorization Server returns need_info (if upstream access is required) If access to the derived resource depends on access to upstream resources, and the Client has not yet presented suitable upstream access tokens, the Aggregator Authorization Server MAY respond with a need_info error requesting additional claim tokens for the upstream resources. In that case, the Authorization Server MUST add the issuer, derivation_resource_id, and resource_scopes entries to the required_claims array to indicate which upstream Authorization Server and which resource the Client must obtain access to.

{
  "error": "need_info",
  "ticket": "tkt-A2",
  "required_claims": [
    {
      "claim_token_format": "urn:ietf:params:oauth:token-type:access_token",
      "details": {
        "issuer": "https://a.example.org",
        "derivation_resource_id": "handle-id-1",
        "resource_scopes": [ "urn:knows:uma:scopes:derivation-read" ]
      }
    }
  ]
}

3. Client requests upstream access tokens The Client SHOULD then request access tokens from the Authorization Server of the upstream Resource Server with the required scopes, including the derivation scope indicated in the required_claims.

{
  "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
  "permissions": [
    {
      "resource_id": "handle-id-1",
      "resource_scopes": [ "urn:knows:uma:scopes:derivation-read" ]
    }
  ],
  "claim_tokens": [
    {
      "claim_token": "<ID_token>",
      "claim_token_format": "http://openid.net/specs/openid-connect-core-1_0.html#IDToken"
    }
  ]
}

4. Client presents upstream tokens to Aggregator Authorization Server Once the Client has obtained the required upstream access tokens, it sends another UMA grant request to the Aggregator Authorization Server, presenting both its ID token and the upstream access tokens as claim tokens together with the original Aggregator ticket.

{
  "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
  "ticket": "tkt-A2",
  "claim_tokens": [
    {
      "claim_token": "<ID_token>",
      "claim_token_format": "http://openid.net/specs/openid-connect-core-1_0.html#IDToken"
    },
    {
      "claim_token_format": "urn:ietf:params:oauth:token-type:access_token",
      "claim_token": "<upstream_access_token>"
    }
  ]
}

The Aggregator Authorization Server MUST verify the provided access tokens with the upstream Authorization Servers to confirm that the Client is authorized to access the upstream resources. If all policy checks succeed, the Aggregator Authorization Server issues an RPT for the requested derived resource.

5. Client requests derived resource from Aggregator Service with RPT Finally, the Client retries the original request to the Aggregator Service, this time including the RPT it obtained from the Aggregator Authorization Server. The Aggregator Service validates or introspects the RPT and, if it is active and grants the required permissions, returns the derived resource to the Client.

GET /aggregator/derived-resource-123 HTTP/1.1
Host: agg.example.org
Authorization: Bearer RPT-agg-1

The Aggregator MAY implement the SA4DS specification to guard endpoints with streaming or other non-HTTP based interfaces.

7. Aggregator Metadata

This endpoint provides metadata about the Aggregator Instance. Except for the Aggregator Description (the instance base URL), deployments MAY choose arbitrary paths for other instance-level endpoints. The Aggregator Metadata representation MUST include absolute URLs for those resources (e.g., the transformation_catalog and service_collection fields) so clients can discover the deployment-specific layout.

7.1. Aggregator Description

The Aggregator Metadata resource ({aggregator-url}) allows clients to retrieve the current status of their aggregator. This endpoint MUST be guarded by the authentication and authorization mechanisms described in the § 6 Aggregator Security Model (Authentication & Authorization).

It MUST be accessible as JSON using application/json and MAY additionally expose semantically annotated RDF representations (for example JSON-LD or Turtle) using HTTP content negotiation based on [RFC9110].

The endpoint MUST return at least the following information about the aggregator, but additional fields MAY be included as needed. Each field SHOULD be expressed using the RDF properties defined in § 9 Vocabulary so the document MAY be served as JSON-LD or other RDF formats:

In semantically annotated representations, the Aggregator Description MUST state that the described resource has RDF type aggr:Aggregator (§ 9.1.1 aggr:Aggregator) (e.g., via @type in JSON-LD or a aggr:Aggregator in Turtle). Clients MAY rely on this type statement when consuming semantic representations.

id (OPTIONAL):

The value is a string containing the absolute URL that identifies the Aggregator Instance (typically the instance base URL itself); in the RDF representations, this is the RDF subject (i.e., @id) of the aggr:Aggregator resource (§ 9.1.1 aggr:Aggregator).

created_at (REQUIRED):

The value is a string timestamp (recommended: xsd:dateTime lexical form, e.g., RFC 3339 [RFC3339]); in the RDF representations, this member maps to the predicate aggr:createdAt (§ 9.2.1 aggr:createdAt).

login_status (REQUIRED):

The value is a boolean that indicates whether the stored token set for the aggregator is currently valid; in the RDF representations, this member maps to the predicate aggr:loginStatus (§ 9.2.2 aggr:loginStatus).

token_expiry (OPTIONAL):

The value is a string timestamp indicating when the aggregator’s access token will expire (recommended: xsd:dateTime lexical form, e.g., RFC 3339 [RFC3339]); in the RDF representations, this member maps to the predicate aggr:tokenExpiry (§ 9.2.3 aggr:tokenExpiry).

transformation_catalog (REQUIRED):

The value is a string containing the absolute URL of the instance’s Transformations Endpoint (§ 7.2 Transformations Endpoint); in the RDF representations, this member maps to the predicate aggr:transformationsEndpoint (§ 9.2.4 aggr:transformationsEndpoint).

service_collection (REQUIRED):

The value is a string containing the absolute URL of the instance’s Service Collection (§ 8.1 Service Collection); in the RDF representations, this member maps to the predicate aggr:servicesEndpoint (§ 9.2.5 aggr:servicesEndpoint).

This document CAN be the WebID of the Aggregator Instance when the provision flow § 5.2.1 Creation provision Flow was used. In that case this document MUST be an RDF document that conforms to the WebID Profile specification [WEBID-PROFILE].

{
  "@context": {
    "id": "@id",
    "aggr": "https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#",
    "solid": "http://www.w3.org/ns/solid/terms#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",

    "created_at": {
      "@id": "aggr:createdAt",
      "@type": "xsd:dateTime"
    },
    "login_status": {
      "@id": "aggr:loginStatus",
      "@type": "xsd:boolean"
    },
    "token_expiry": {
      "@id": "aggr:tokenExpiry",
      "@type": "xsd:dateTime"
    },
    "transformation_catalog": {
      "@id": "aggr:transformationsEndpoint",
      "@type": "@id"
    },
    "service_collection": {
      "@id": "aggr:servicesEndpoint",
      "@type": "@id"
    }
  },
  "id": "https://aggregator.example/aggregators/agg-7890/",
  "@type": "aggr:Aggregator",
  "created_at": "2025-12-17T17:20:00Z",
  "login_status": true,
  "token_expiry": "2025-12-17T18:20:00Z",
  "transformation_catalog": "https://aggregator.example/aggregators/agg-7890/transformations",
  "service_collection": "https://aggregator.example/aggregators/agg-7890/config/services",
  "solid:oidcIssuer": "https://issuer.example/"
}

7.2. Transformations Endpoint

This endpoint is the instance-level extension on the public Transformation Catalog defined in § 4.3 Public Transformation Catalog. Implementations MAY expose it at any URL, but the Aggregator Description MUST advertise the correct location via its transformation_catalog field (aggr:transformationsEndpoint). It allows aggregator implementations to expose user-specific transformations, or protected/private transformations. This endpoint, contrasting to the public Transformation Catalog, MAY be user specific and MUST require authentication. The endpoint MAY return a 404 Not Found if no user-specific transformations are available. A client SHOULD combine the information from this endpoint with the public Transformation Catalog to get a complete view on the available transformations. The endpoint follows the same content negotiation rules and other requirements as the public Transformation Catalog.

8. Aggregator Service Management

The Aggregator Management API gives an authenticated client a complete view on the Aggregator: it exposes the transformations that can be used to assemble a pipeline and it lets the client create, inspect, and remove concrete Aggregator Services. Implementations MAY use different URLs as long as the Aggregator Metadata document (described in § 7.1 Aggregator Description) links to the concrete entry points. All configuration routes:

8.1. Service Collection

This resource represents the collection of configured Aggregator Services. The location of this resource is advertised in the Aggregator Description (§ 7.1 Aggregator Description) via the service_collection field. Clients MUST treat that advertised URL as authoritative and MUST NOT assume a fixed path (the examples in this section use /services purely for illustration). The Aggregator MUST register this UMA resource with the Authorization Server and advertise the read and create scopes so that clients can both inspect and add members.

HEAD

Returns the collection metadata. The server MUST respond with 200 OK, Content-Type: application/json, and an ETag header whose value increases whenever a service is added or removed. The ETag allows clients to detect collection changes without re-downloading it.

GET

Returns the current list of service resources. The server MUST set the same ETag value as the HEAD response. The server MAY additionally expose semantically annotated RDF representations (for example JSON-LD or Turtle) using HTTP content negotiation based on [RFC9110]. The payload MUST be a JSON object and MUST include at least the following fields:

services (REQUIRED):

The value is a JSON array of strings.

Each member MUST be an absolute URL (IRI) of a Service Resource that can be dereferenced by the client.

In semantically annotated representations, this member maps to the predicate aggr:service (§ 9.2.6 aggr:service).

id (OPTIONAL):

The value is a string containing the absolute URL that identifies this Service Collection (typically the same URL as the request target).

In semantically annotated representations, this is the RDF subject (i.e., @id) of the aggr:ServiceCollection resource (§ 9.1.4 aggr:ServiceCollection).

{
  "@context": {
    "id": "@id",
    "services": {
      "@id": "aggr:service",
      "@container": "@set",
      "@type": "@id"
    },
    "aggr": "https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#"
  },
  "id": "https://aggregator.example.org/services",
  "services": [
    "https://aggregator.example.org/services/410b093c-04b3-4fac-87be-4d393f40b2e5",
    "https://aggregator.example.org/services/42"
  ]
}
POST

Creates a new service. The request body MUST contain a pipeline description that references the available transformations (implementations MAY decide on the exact media type using HTTP content negotiation based on [RFC9110]).

  1. Persist the new service and increment the collection ETag.

  2. Register a new UMA resource for the created Service Resource URL (e.g., /services/{service_id}) with the read and delete scopes so the creator—or any other party with an RPT containing those scopes—can manage the service.

  3. Return 201 Created, set Content-Type: application/json, and include the full JSON representation of the service in the response body.

If the request body is invalid the server MUST respond with 400 Bad Request. Failures while instantiating the service MUST result in 500 Internal Server Error.

8.2. Service Resource

Operations on an individual service require the read and delete scopes on the Service Resource URL (e.g., /services/{service_id}). The service URL MUST be one of the URLs returned by the collection resource; a request for a non-existent service MUST return 404 Not Found, while malformed service URLs MUST yield 400 Bad Request.

HEAD

Returns the service metadata. The server MUST respond with 200 OK and ETag, and Content-Type headers whose value MUST change whenever the service state changes.

GET

A JSON representation of the service SHOULD be returned with 200 OK and with a Content-Type. These representations MAY include a JSON-LD context that maps the aggr: terms defined in § 9 Vocabulary so clients can consume typed data, and content negotiation SHOULD be supported. The representation MUST include at least the following fields:

id (REQUIRED):

The value is a string containing the absolute URL of this Service Resource.

In semantically annotated representations, this is the RDF subject (i.e., @id) of the aggr:Service resource (§ 9.1.2 aggr:Service).

status (REQUIRED):

The value is a string indicating the current status of the service (e.g., "running", "restarting", "stopped", or "errored").

In semantically annotated representations, this member maps to the predicate aggr:status (§ 9.2.8 aggr:status).

transformation (REQUIRED):

The value is either a string containing an IRI of the transformation/execution graph, or a JSON object embedding an FnO execution description.

In semantically annotated representations, this member maps to the predicate aggr:transformation (§ 9.2.9 aggr:transformation).

created_at (REQUIRED):

The value is a string timestamp (recommended: xsd:dateTime lexical form, e.g., RFC 3339 [RFC3339]).

In semantically annotated representations, this member maps to the predicate aggr:createdAt (§ 9.2.1 aggr:createdAt).

location (REQUIRED):

The value is a string containing an absolute URL where the service output can be accessed.

In semantically annotated representations, this member maps to the predicate aggr:location (§ 9.2.10 aggr:location).

DELETE

Stops and removes the service. The Aggregator MUST stop the running pipeline, delete the persisted service entry, increment the collection ETag, unregister the service’s UMA resource (e.g., /services/{service_id}), and respond with 200 OK. Clients that held the service identifier MUST treat it as invalid after receiving the success response.

8.3. Service Management Flows (Non-normative)

This section gives some examples on how a client can create, find, use and delete services on the Aggregator. This section is non-normative, and is only meant to illustrate the usage of the various endpoints defined in this specification. This section assumes the client has already created an Aggregator using the Aggregator Registration API (§ 5.1 Aggregator Registration Endpoint) and is able to authenticate using the mechanisms defined in § 6 Aggregator Security Model (Authentication & Authorization).

8.3.1. Creating a Service

To create a new Aggregator Service, a client starts by doing a POST request to the Service Collection endpoint (i.e., the URL advertised via service_collection in the Aggregator Description; this section uses /services as an example). The body of the post is an execution of an FnO function [FNO]; the details will be provided later as this request fails due to missing authentication.

POST /services HTTP/1.1
Host: aggregator.example.org
Content-Type: text/turtle

@prefix trans: <http://aggregator.example.org/transformations>
@prefix fno: <https://w3id.org/function/ontology#>

_:execution a fno:Execution ;
    fno:executes trans:AggregateSources ;
    trans:sources ( <http://example.org/source/1> <http://example.org/source/2> ) .

The Aggregator fetches a ticket from the Authorization Server with the resource_id 1a2b-creation-endpoint it got during asset creation.

HTTP/1.1 /ticket
Host: as.example.org
Content-Type: application/json
{
    "resource_id": "1a2b-creation-endpoint",
    "resource_scopes": ["https://example.org/modes/create"]
}

This returns a ticket that discribes the request done to the RS (the Aggregator in this case).

HTTP/1.1 201 Created
Content-Type: application/json
{
    "ticket": "ticket-1"
}

This ticket is then returned to the client in a 401 Unauthorized response.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="example", as_uri="https://as.example.org", ticket="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

The client then requests an RPT from the AS using the ticket, as defined in § 6 Aggregator Security Model (Authentication & Authorization). The original request can then be retried, this time including the RPT in the Authorization header.

POST /services HTTP/1.1
Host: aggregator.example.org
Authorization: Bearer ey...
Content-Type: text/turtle

@prefix trans: <http://aggregator.example.org/transformations>
@prefix fno: <https://w3id.org/function/ontology#>

_:execution a fno:Execution ;
    fno:executes trans:AggregateSources ;
    trans:sources ( <http://example.org/source/1> <http://example.org/source/2> ) .

If the request is valid, the Aggregator will create a new service, register the appropriate UMA resource, and return a 201 Created response with the service representation in the body.

HTTP/1.1 200 OK
Content-Type: text/turtle

@prefix aggr: <https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#> .
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix trans: <http://aggregator.example.org/transformations#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://aggregator.example.org/services/410b093c-04b3-4fac-87be-4d393f40b2e5>
    a aggr:Service ;
    aggr:status "running" ;
    aggr:createdAt "2024-01-01T12:00:00Z"^^xsd:dateTime ;
    aggr:location <https://example.org/410b093c-04b3-4fac-87be-4d393f40b2e5> ;
    aggr:transformation [
        a fno:Execution ;
        fno:executes trans:AggregateSources ;
        trans:sources ( <http://example.org/source/1> <http://example.org/source/2> )
    ] .

8.3.2. Discovering Services

To discover the services currently registered on the Aggregator, a client can do a GET request to the Service Collection endpoint (this section uses /services as an example). After authenticating using the mechanisms defined in § 6 Aggregator Security Model (Authentication & Authorization), the Aggregator will return a list with the registered service from § 8.3.1 Creating a Service.

HTTP/1.1 200 OK
Content-Type: text/turtle

@prefix aggr: <https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<> a aggr:ServiceCollection ;
    aggr:service <https://aggregator.example.org/services/410b093c-04b3-4fac-87be-4d393f40b2e5> .

Dereferencing this URL will return the full service representation.

HTTP/1.1 201 Created
Content-Type: text/turtle

@prefix aggr: <https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#> .
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix trans: <http://aggregator.example.org/transformations#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://aggregator.example.org/services/410b093c-04b3-4fac-87be-4d393f40b2e5>
    a aggr:Service ;
    aggr:status "running" ;
    aggr:createdAt "2024-01-01T12:00:00Z"^^xsd:dateTime ;
    aggr:location <https://example.org/410b093c-04b3-4fac-87be-4d393f40b2e5> ;
    aggr:transformation [
        a fno:Execution ;
        fno:executes trans:AggregateSources ;
        trans:sources ( <http://example.org/source/1> <http://example.org/source/2> )
    ] .

8.3.3. Using a Simple Service

After discovering the service, the client can use the location URL to access the output of the service. For example, if the service produces a JSON output, the client can do a GET request to the location URL. This will again require authentication using the mechanisms defined in § 6 Aggregator Security Model (Authentication & Authorization) to access this resource. This time the client might need to gather multiple access claims from the AS’s of http://example.org/source/1 and http://example.org/source/2.

8.3.4. Using a Complex Service

As a complex example the resulting service could be an aggregator that combines multiple data sources into a single output stream available at the location URL. For example we assume this results is a kafka stream.

TODO add the example flow here

9. Vocabulary

TODO ensure all example predicates/classes are defined or referenced.

The Aggregator vocabulary is defined in the aggr: namespace (https://spec.knows.idlab.ugent.be/aggregator-protocol/latest/#). The following classes and properties are used throughout this specification.

9.1. Classes

9.1.1. aggr:Aggregator

Describes an Aggregator Instance (its base URL is the Aggregator Description resource).

type: rdfs:Class
subClassOf: schema:Service
subClassOf: foaf:Agent

9.1.2. aggr:Service

Represents a configured Aggregator pipeline that can be created, inspected, and removed via the Service Management API (e.g., /config/services/{service_id}).

type: rdfs:Class
subClassOf: prov:Activity

9.1.3. aggr:AggregatorServer

Describes the Aggregator Server Description document that advertises discovery metadata.

type: rdfs:Class
subClassOf: schema:Service

9.1.4. aggr:ServiceCollection

Describes the service collection resource (e.g., /config/services) and the metadata returned when listing configured services.

type: rdfs:Class
subClassOf: schema:Collection, hydra:Collection

9.1.5. aggr:TransformationCollection

Describes a transformation catalog resource that lists the transformations supported by an Aggregator Server (and optionally instance-specific transformations).

type: rdfs:Class
subClassOf: schema:Collection, hydra:Collection

9.1.6. aggr:RegistrationFlow

Describes a registration flow supported by an Aggregator Server. This specification models each flow as an RDF class so that aggr:supportedRegistrationType can be semantically annotated by referencing the relevant flow class (e.g., aggr:ClientCredentialsFlow).

type: rdfs:Class
subClassOf: rdfs:Class

9.1.7. aggr:ProvisionFlow

Registration flow where the Aggregator Server provisions an Aggregator Instance with its own identity.

type: rdfs:Class
subClassOf: aggr:RegistrationFlow

9.1.8. aggr:AuthorizationCodeFlow

Registration flow based on OAuth 2.0 Authorization Code [RFC6749] (via OpenID Connect), where the Aggregator acts on behalf of an end-user with a token scoped to the Aggregator.

type: rdfs:Class
subClassOf: aggr:RegistrationFlow

9.1.9. aggr:ClientCredentialsFlow

Registration flow based on OAuth 2.0 Client Credentials [RFC6749], where the Aggregator obtains a token using user-provided long-lived client credentials.

type: rdfs:Class
subClassOf: aggr:RegistrationFlow

9.1.10. aggr:DeviceCodeFlow

Registration flow based on OAuth 2.0 Device Authorization Grant [RFC8628].

type: rdfs:Class
subClassOf: aggr:RegistrationFlow

9.2. Properties

9.2.1. aggr:createdAt

Timestamp when an aggr:Aggregator or aggr:Service was created.

type: rdf:Property
domain: aggr:Aggregator, aggr:Service
range: xsd:dateTime

9.2.2. aggr:loginStatus

Indicates whether the stored token set for an aggr:Aggregator is currently valid.

type: rdf:Property
domain: aggr:Aggregator
range: xsd:boolean

9.2.3. aggr:tokenExpiry

Timestamp when the current access token for an aggr:Aggregator will expire.

type: rdf:Property
domain: aggr:Aggregator
range: xsd:dateTime

9.2.4. aggr:transformationsEndpoint

Links an aggr:Aggregator to its (possibly private) transformations endpoint.

type: rdf:Property
domain: aggr:Aggregator
range: xsd:anyURI

9.2.5. aggr:servicesEndpoint

Links an aggr:Aggregator to its services endpoint.

type: rdf:Property
domain: aggr:Aggregator
range: xsd:anyURI

9.2.6. aggr:service

Links an aggr:ServiceCollection to the aggr:Service instances it advertises.

type: rdf:Property
domain: aggr:ServiceCollection
range: aggr:Service

9.2.7. aggr:hasTransformation

Links an aggr:TransformationCollection to the transformations it advertises.

type: rdf:Property
domain: aggr:TransformationCollection
range: fno:Function

9.2.8. aggr:status

Provides the lifecycle phase of an aggr:Service (values such as running, stopped, or error).

type: rdf:Property
domain: aggr:Service
range: xsd:string

9.2.9. aggr:transformation

Associates an aggr:Service with the FnO execution graph that describes how inputs are transformed.

type: rdf:Property
domain: aggr:Service
range: fno:Execution

9.2.10. aggr:location

Links an aggr:Service to the URL where its output can be accessed.

type: rdf:Property
domain: aggr:Service
range: xsd:anyURI

9.2.11. aggr:registrationEndpoint

Links an aggr:AggregatorServer to its registration endpoint.

type: rdf:Property
domain: aggr:AggregatorServer
range: xsd:anyURI

9.2.12. aggr:supportedRegistrationType

Lists the registration flows advertised by an aggr:AggregatorServer.

type: rdf:Property
domain: aggr:AggregatorServer
range: rdfs:Class (expected to be an aggr:RegistrationFlow class)

9.2.13. aggr:specVersion

States which version of this specification an aggr:AggregatorServer implements.

type: rdf:Property
domain: aggr:AggregatorServer
range: xsd:string

9.2.14. aggr:clientIdentifier

Links an aggr:AggregatorServer to the client metadata document that contains the client_id value.

type: rdf:Property
domain: aggr:AggregatorServer
range: xsd:anyURI

9.2.15. aggr:transformationCatalog

References the Aggregator Server’s public transformation catalog.

type: rdf:Property
domain: aggr:AggregatorServer
range: xsd:anyURI

Conformance

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

References

Normative References

[A4DS]
Authorization for Data Spaces (A4DS). URL: https://solid.github.io/authorization-for-data-spaces/
[FNO]
Function Ontology (FnO). URL: https://w3id.org/function/spec/
[OIDC-Core]
OpenID Connect Core 1.0. URL: https://openid.net/specs/openid-connect-core-1_0.html
[OIDC-Registration]
OpenID Connect Dynamic Client Registration 1.0. URL: https://openid.net/specs/openid-connect-registration-1_0.html
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SA4DS]
Service Authorization for Data Spaces (SA4DS). URL: https://solid.github.io/service-authorization-for-data-spaces/
[SOLID-OIDC]
Solid OIDC. URL: https://solidproject.org/TR/oidc
[UMA]
User-Managed Access (UMA) 2.0. URL: https://docs.kantarainitiative.org/uma/rec-uma-core.html
[WEBID-PROFILE]
WebID Profile. URL: https://solid.github.io/webid-profile/

Informative References

[RFC3339]
G. Klyne; C. Newman. Date and Time on the Internet: Timestamps. July 2002. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc3339
[RFC6749]
D. Hardt, Ed.. The OAuth 2.0 Authorization Framework. October 2012. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6749
[RFC6750]
M. Jones; D. Hardt. The OAuth 2.0 Authorization Framework: Bearer Token Usage. October 2012. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6750
[RFC7636]
N. Sakimura, Ed.; J. Bradley; N. Agarwal. Proof Key for Code Exchange by OAuth Public Clients. September 2015. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc7636
[RFC8628]
W. Denniss; et al. OAuth 2.0 Device Authorization Grant. August 2019. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8628
[RFC9110]
R. Fielding, Ed.; M. Nottingham, Ed.; J. Reschke, Ed.. HTTP Semantics. June 2022. Internet Standard. URL: https://httpwg.org/specs/rfc9110.html

Issues Index

TODO clarify whether Aggregator Services may reference transformations not advertised in the server/instance transformation catalogs, and how servers validate and authorize execution of arbitrary FnO graphs.
TODO clarify the boundary between the Aggregator Instance (management API) and the Aggregator Service output resources (the location URLs), including whether outputs are always protected by the same Authorization Server and UMA policies.
TODO how do we allow an aggregator that can run any transformation?
TODO specify whether and how IDP_client_token can be sender-constrained (e.g., DPoP).
This flow is not yet specified.
TODO we should maybe split this up into a normative section and a non-normative explanation with sequence diagrams.
TODO: how does the Aggregator tell the Authorization Server (AS) which transformation it intends to perform on the data?
TODO: if this resource has already been requested, the Upstream Authorization Server already has a derivation_resource_id for this Aggregator. Does the Aggregator need to request a new one, or can it reuse the existing one?
TODO add the example flow here
TODO ensure all example predicates/classes are defined or referenced.