<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE rfc [ ]>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     category="std" docName="draft-kushwaha-scim-attr-cursor-pagination-01"
     ipr="trust200902" updates="7643, 7644" obsoletes="" submissionType="IETF"
     consensus="true" version="3" xml:lang="en"
     tocInclude="true" tocDepth="3" symRefs="true" sortRefs="true">
  <front>
    <title abbrev="SCIM Attribute Pagination">Cursor-Based Pagination and Deferred Retrieval for Multi-Valued Attributes in SCIM 2.0</title>
    <seriesInfo name="Internet-Draft" value="draft-kushwaha-scim-attr-cursor-pagination-01" status="standard"/>
    <author initials="S." surname="Kushwaha" fullname="Saurabh Kushwaha">
      <organization>Oracle Corporation</organization>
      <address>
        <postal>
          <city>Pleasanton</city>
          <region>CA</region>
          <country>United States of America</country>
        </postal>
        <email>saurabh.kushwaha@oracle.com</email>
      </address>
    </author>
    <area>Applications and Real-Time</area>
    <workgroup>System for Cross-domain Identity Management</workgroup>
    <keyword>SCIM</keyword><keyword>pagination</keyword><keyword>provisioning</keyword>
    <abstract>
      <t><xref target="RFC7643"/> defines <tt>Group.members</tt> with the <tt>returned: default</tt> characteristic, so a conformant service provider is required to return the attribute in response to <tt>GET /Groups/{id}</tt>. <xref target="RFC7644"/> defines no bound on the number of values that attribute may contain. A service provider holding a group with millions of members therefore has no conformant and interoperable way to answer a request that a client is entitled to make. In practice providers diverge: they truncate silently, reject the request, omit the attribute, or fail. A client cannot discover in advance which behavior it will encounter.</t>
      <t>This document defines that missing behavior. It specifies discovery so a client can learn how a service provider treats a high-cardinality attribute, a bounded response with a defined continuation contract, and rules preventing a partial representation from being mistaken for complete resource state.</t>
      <t>The mechanism has two forms. A request for one resource returns a bounded page of one protected multi-valued attribute with an opaque cursor when more values exist. A collection search returns parent resources without loading protected attributes, each carrying an authoritative link for retrieving that attribute. The attribute remains part of its parent resource; this document does not create a top-level resource for each attribute value, and it is not a substitute for doing so where independent relationship lifecycle or cross-collection query is required (Section 16.4).</t>
      <t>Although the mechanism is defined generally and applies to any complex multi-valued attribute a service provider designates as protected, the attributes that reach problematic sizes in deployed SCIM services are predominantly <tt>Group.members</tt> and <tt>User.groups</tt> among those defined in <xref target="RFC7643"/>, together with implementation-specific assignment attributes (Section 3).</t>
      <t>This document updates <xref target="RFC7643"/> and <xref target="RFC7644"/>. It adds attributes to existing structures defined by those documents and defines attribute-return behavior that replaces the requirements of Section 3.9 of <xref target="RFC7644"/> within a negotiated scope. It does not change the behavior of deployments that do not implement it: the modified attribute-selection behavior described in Section 4 applies only between a service provider that has advertised this capability and a client for which deferred retrieval has been established through the negotiation mechanisms defined in this document. It defines discovery metadata, the <tt>attributeCount</tt> and <tt>attributeCursor</tt> query parameters, response metadata, processing and compatibility rules, mutation safety, error handling, cursor security, and operational limits.</t>
    </abstract>
  </front>
  <middle>
<section anchor="introduction"><name>Introduction</name>
<section anchor="the-conformance-gap"><name>The Conformance Gap</name>
<t><xref target="RFC7644"/> supports pagination of resources in a SCIM <tt>ListResponse</tt>. <xref target="RFC9865"/> adds cursor-based pagination for those resource collections. Neither mechanism bounds the values contained inside one resource.</t>
<t><xref target="RFC7643"/> defines <tt>Group.members</tt> with <tt>returned: default</tt>. A client may therefore issue:</t>
<sourcecode type="http-message"><![CDATA[GET /Groups/2819c223-7f76-453a-919d-413861904646]]></sourcecode>
<t>and a conformant service provider is required to return the <tt>members</tt> attribute. <xref target="RFC7644"/> places no bound on how many values that attribute contains, and defines no mechanism by which a service provider may return fewer than all of them while remaining conformant.</t>
<t>For a group with two million members this leaves a service provider with no correct option. Every available behavior is either non-conformant or operationally unsafe:</t>
<table><thead><tr><th>Behavior</th><th>Problem</th></tr></thead><tbody>
<tr><td>Return all values</td><td>Unbounded database work, heap consumption, serialization cost, and request duration (Section 1.2)</td></tr>
<tr><td>Truncate silently</td><td>Non-conformant, and indistinguishable to the client from complete state</td></tr>
<tr><td>Return an empty array</td><td>Non-conformant, and destructive if the client subsequently issues PUT</td></tr>
<tr><td>Omit the attribute</td><td>Non-conformant under Section 3.9 of <xref target="RFC7644"/></td></tr>
<tr><td>Reject the request</td><td>Conformant client receives an error for a valid request, with no defined remedy</td></tr>
</tbody></table>
<t>Deployed service providers do all of these. A client has no way to determine which it will encounter, and no defined recovery when it encounters one. This is an interoperability defect in the protocol rather than a deficiency in any implementation.</t>
<t>Approaches that expose memberships through a separate address — an inverse filter over the counterpart resource (Section 16.8), a relationship resource type, or a per-attribute sub-resource endpoint (Section 16.4) — give clients good ways to <em>enumerate</em> memberships. None of them closes this gap, because <tt>Group.members</tt> remains defined with <tt>returned: default</tt> regardless of what other retrieval paths a provider offers, and clients continue to request it.</t>
</section>
<section anchor="operational-consequences"><name>Operational Consequences</name>
<t>The same problem occurs for implementation-specific complex multi-valued attributes, particularly assignment attributes such as application grants and accounts, which represent access relationships rather than descriptive properties and grow with the size of the deployment rather than with the size of an individual record.</t>
<t>The problem has two distinct forms:</t>
<ol><li><t>A collection search can return many parent resources, each of which can trigger an unbounded child or reference-derived query.</t></li><li><t>A resource-by-identifier request can return one attribute containing an unbounded number of values.</t></li></ol>
<t>Applying a page size only after discovering or hydrating all values does not protect the system. The page boundary has to be applied to the first query that discovers the attribute values.</t>
<t>This document therefore defines two complementary behaviors:</t>
<ul><li><t><strong>Deferred collection retrieval:</strong> a collection search returns parent resources without loading protected multi-valued attributes. Each affected resource contains metadata with a link to retrieve the attribute from that parent.</t></li><li><t><strong>Cursor-paged resource retrieval:</strong> a request for one parent and one protected multi-valued attribute returns a bounded first page and an opaque cursor when more values exist.</t></li></ul>
<t>This model keeps the multi-valued attribute on its parent resource. It avoids introducing a new top-level resource type and avoids maintaining two independently mutable representations of the same relationship.</t>
<t>Attribute pagination does not imply snapshot isolation. Unless a service provider advertises stronger consistency, clients MUST tolerate values being added, removed, or reordered while a traversal is in progress.</t>
</section>
</section>
<section anchor="conventions-and-terminology"><name>Conventions and Terminology</name>
<t>The key words <strong>MUST</strong>, <strong>MUST NOT</strong>, <strong>REQUIRED</strong>, <strong>SHALL</strong>, <strong>SHALL NOT</strong>, <strong>SHOULD</strong>, <strong>SHOULD NOT</strong>, <strong>RECOMMENDED</strong>, <strong>NOT RECOMMENDED</strong>, <strong>MAY</strong>, and <strong>OPTIONAL</strong> in this document are to be interpreted as described in BCP 14 when, and only when, they appear in all capitals.</t>
<t><strong>Attribute page</strong></t>
<t>A bounded subset of the values of one multi-valued attribute on one SCIM resource.</t>
<t><strong>Attribute cursor</strong></t>
<t>An opaque continuation value issued by the service provider for retrieving another page of the same attribute.</t>
<t><strong>Protected attribute</strong></t>
<t>A complex or primitive multi-valued attribute for which the service provider advertises attribute pagination and may enforce deferred or bounded retrieval.</t>
<t><strong>Deferred attribute</strong></t>
<t>A selected protected attribute whose values were intentionally not loaded into the current representation. Its <tt>attributePagination</tt> entry has state <tt>deferred</tt> and contains a <tt>firstPage</tt> URI-reference.</t>
<t><strong>Hydrated attribute</strong></t>
<t>A protected attribute for which a bounded page of values is present in the current representation. Its <tt>attributePagination</tt> entry has state <tt>page</tt>.</t>
<t><strong>Effective page size</strong></t>
<t>The maximum number of values that the service provider will attempt to return after applying the requested <tt>attributeCount</tt>, provider defaults, maximums, authorization policy, response-byte budgets, and other safety limits.</t>
<t><strong>Reference-derived attribute</strong></t>
<t>A multi-valued attribute whose values are computed from, or hydrated using, another persisted resource or relationship rather than stored directly on the parent resource.</t>
<t><strong>Compatibility profile</strong></t>
<t>A client, tenant, endpoint, or deployment policy establishing whether automatic deferred retrieval can be used for requests that do not explicitly contain <tt>attributeCount</tt> or <tt>attributeCursor</tt>.</t>
</section>
<section anchor="design-goals-and-scope"><name>Design Goals and Scope</name>
<t>This specification has the following goals:</t>
<ul><li><t>Bound database, memory, serialization, and response-size work before hydration.</t></li><li><t>Support both directly persisted and reference-derived multi-valued attributes.</t></li><li><t>Keep each attribute value within the existing parent-resource contract.</t></li><li><t>Permit more than one protected attribute to be advertised in a collection result without loading any of them.</t></li><li><t>Hydrate at most one protected attribute in a single response.</t></li><li><t>Use opaque, context-bound cursor continuation rather than increasing offsets.</t></li><li><t>Preserve a controlled migration path for existing SCIM clients.</t></li></ul>
<t>This version does not define:</t>
<ul><li><t>Previous-page traversal.</t></li><li><t>Client-defined filtering or sorting within a multi-valued attribute.</t></li><li><t>A top-level resource type for an attribute value or relationship.</t></li><li><t>Snapshot isolation across pages.</t></li><li><t>Mutation of a partial attribute page.</t></li><li><t>Attribute cursors spanning more than one parent resource or more than one attribute.</t></li></ul>
<t>Root-resource pagination and attribute pagination are independent. A resource collection MAY use <tt>startIndex</tt> pagination from <xref target="RFC7644"/> or cursor pagination from <xref target="RFC9865"/> while protected attributes inside each returned resource remain deferred.</t>
</section>
<section anchor="compatibility-model-and-update-to-rfc-7644-secti"><name>Compatibility Model and Update to RFC 7644 Section 3.9</name>
<t>Section 3.9 of <xref target="RFC7644"/> requires a resource to contain attributes explicitly selected by the <tt>attributes</tt> query parameter. <xref target="RFC7643"/> also defines <tt>Group.members</tt> as <tt>returned: default</tt>. Omitting such values is therefore a protocol behavior change, not a transparent implementation optimization.</t>
<t>This document therefore updates <xref target="RFC7643"/> and <xref target="RFC7644"/>. It updates <xref target="RFC7643"/> by adding attributes to existing structures, and it updates <xref target="RFC7644"/> by defining attribute-return rules that replace those of Section 3.9 for parties that have established this capability. This mirrors <xref target="RFC9865"/>, which likewise updates <xref target="RFC7643"/> and <xref target="RFC7644"/> while remaining an opt-in capability discovered through ServiceProviderConfig.</t>
<t>Updating those documents does not make this behavior mandatory or unconditional. A service provider and client that have not established this capability retain unmodified <xref target="RFC7644"/> semantics, and a client that never negotiates deferred retrieval cannot observe any change. The negotiation mechanisms that establish the capability are defined in Sections 4.1 and 4.2.</t>
<t>Between parties for which deferred mode has been established, the following rules replace the attribute-return requirements of <xref target="RFC7644"/> Section 3.9:</t>
<ul><li><t>A selected protected attribute MAY be omitted from a returned resource when an <tt>attributePagination</tt> entry for that exact attribute is included with state <tt>deferred</tt>.</t></li><li><t>The omission MUST NOT be represented as an empty array or <tt>null</tt>, because either value can be mistaken for complete attribute state.</t></li><li><t>The <tt>firstPage</tt> URI-reference in the metadata is the authoritative way to retrieve the omitted values.</t></li><li><t>An attribute with schema characteristic <tt>returned: always</tt> MUST NOT be deferred.</t></li><li><t>An attribute excluded by <tt>excludedAttributes</tt> MUST be omitted normally and MUST NOT produce an <tt>attributePagination</tt> entry.</t></li></ul>
<section anchor="explicit-and-automatic-modes"><name>Explicit and Automatic Modes</name>
<t>A service provider MAY support either or both of these modes:</t>
<t><strong>Explicit mode</strong></t>
<t>Attribute pagination is used only when the request contains <tt>attributeCount</tt> or <tt>attributeCursor</tt>. Other requests retain <xref target="RFC7644"/> behavior, subject to existing provider limits.</t>
<t><strong>Automatic deferred mode</strong></t>
<t>The service provider can defer protected attributes from collection results and apply a default page size to a resource-by-identifier request even when <tt>attributeCount</tt> is absent. This is analogous to <xref target="RFC9865"/> permitting a service provider to choose cursor pagination and a default page size when a resource-collection request omits <tt>cursor</tt> and <tt>count</tt>.</t>
</section>
<section anchor="deployment-compatibility"><name>Deployment Compatibility</name>
<t>A new service provider MAY advertise automatic deferred mode as its default. An existing service provider adding this extension SHOULD preserve its legacy behavior until a compatibility profile has been established for affected clients or tenants.</t>
<t>ServiceProviderConfig advertisement alone does not prove that every existing client understands a partial attribute representation. Existing deployments SHOULD use a staged tenant or client profile, the in-band preference below, an out-of-band capability agreement, or another deployment-specific negotiation mechanism before enabling automatic deferred mode.</t>
<t>An extension-aware client MAY explicitly opt in by sending this <xref target="RFC7240"/> preference:</t>
<sourcecode type="http-message"><![CDATA[Prefer: scim-attribute-pagination=deferred]]></sourcecode>
<t>A provider that applies the preference SHOULD return:</t>
<sourcecode type="http-message"><![CDATA[Preference-Applied: scim-attribute-pagination=deferred]]></sourcecode>
<t>The absence of this preference does not request unsafe or unbounded behavior. A provider MAY still apply deferred mode through its advertised default and compatibility profile. An existing provider that has not established such a profile SHOULD retain legacy behavior or fail closed as described below.</t>
<t>If a provider cannot safely return an unbounded attribute and automatic deferred behavior has not been established for the client, it SHOULD fail closed with <tt>attributePaginationRequired</tt> rather than return a silently truncated representation or attempt unsafe unbounded retrieval.</t>
<t>This specification does not claim that automatic deferred mode is wire-compatible with clients that require a complete default attribute. For example, some deployed provisioning clients require a bare <tt>GET /Groups/{id}</tt> to return all <tt>members</tt>, or construct a subsequent <tt>PUT</tt> from a retrieved representation. Such clients require explicit migration, a legacy compatibility profile, or targeted PATCH behavior before automatic deferred mode is enabled.</t>
</section>
</section>
<section anchor="discovery"><name>Discovery</name>
<t>A service provider supporting this specification SHOULD include the following extension in <tt>/ServiceProviderConfig</tt>:</t>
<sourcecode><![CDATA[urn:ietf:params:scim:schemas:extension:attributePagination:2.0:ServiceProviderConfig]]></sourcecode>
<t>Example:</t>
<sourcecode type="json"><![CDATA[{
  "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:ServiceProviderConfig": {
    "supported": true,
    "cursor": true,
    "defaultMode": "deferred",
    "defaultPageSize": 100,
    "maxPageSize": 1000,
    "maxHydratedAttributesPerResponse": 1,
    "cursorTimeout": 3600,
    "totalResults": "optional",
    "resourceTypes": [
      {
        "resourceType": "Group",
        "attributes": ["members"]
      },
      {
        "resourceType": "App",
        "attributes": ["grants", "accounts"]
      }
    ]
  }
}]]></sourcecode>
<t>The fields have the following meaning:</t>
<table><thead><tr><th>Field</th><th>Requirement</th><th>Description</th></tr></thead><tbody>
<tr><td><tt>supported</tt></td><td>REQUIRED</td><td>Indicates support for this specification.</td></tr>
<tr><td><tt>cursor</tt></td><td>REQUIRED</td><td>Indicates support for attribute cursor continuation.</td></tr>
<tr><td><tt>defaultMode</tt></td><td>OPTIONAL</td><td><tt>explicit</tt> or <tt>deferred</tt>. Absence does not imply that no provider policy exists.</td></tr>
<tr><td><tt>defaultPageSize</tt></td><td>OPTIONAL</td><td>Provider-selected page size when <tt>attributeCount</tt> is absent.</td></tr>
<tr><td><tt>maxPageSize</tt></td><td>OPTIONAL</td><td>Maximum permitted effective page size.</td></tr>
<tr><td><tt>maxHydratedAttributesPerResponse</tt></td><td>OPTIONAL</td><td>Maximum protected attributes whose values can be loaded in one representation. This version defines a maximum of one.</td></tr>
<tr><td><tt>cursorTimeout</tt></td><td>OPTIONAL</td><td>Minimum advertised cursor lifetime in seconds.</td></tr>
<tr><td><tt>totalResults</tt></td><td>OPTIONAL</td><td><tt>unsupported</tt>, <tt>optional</tt>, or <tt>supported</tt>.</td></tr>
<tr><td><tt>resourceTypes</tt></td><td>RECOMMENDED</td><td>Identifies protected attribute paths by resource type.</td></tr>
</tbody></table>
<t>A provider MAY withhold numeric defaults or limits when they vary by resource type, authorization context, response-byte budget, or other policy. A client MUST NOT interpret an omitted value as unlimited.</t>
<t>Protected attributes SHOULD also be identifiable through provider schema or resource-type metadata. Attribute paths MUST use canonical SCIM attribute notation. Multiple requested sub-attributes of the same top-level multi-valued attribute count as one protected attribute.</t>
</section>
<section anchor="query-parameters"><name>Query Parameters</name>
<section anchor="attributecount"><name><tt>attributeCount</tt></name>
<t><tt>attributeCount</tt> specifies the client's preferred maximum number of values from the selected protected attribute.</t>
<ul><li><t>The value MUST be a non-negative integer.</t></li><li><t>The parameter is OPTIONAL for the first page.</t></li><li><t>If omitted, the provider selects an effective page size.</t></li><li><t>A provider MUST NOT return more values than the effective page size.</t></li><li><t>A provider MAY return fewer values because of authorization, response-byte, query-time, or other safety limits.</t></li><li><t>A value greater than the provider maximum MUST either be reduced to the maximum or rejected with <tt>invalidAttributeCount</tt>; the behavior SHOULD be advertised consistently.</t></li><li><t>A value of zero requests no values. The provider MAY return an inexpensive <tt>totalResults</tt>, but a zero-size response does not create a traversable cursor.</t></li></ul>
<t>When a client supplies <tt>attributeCount</tt> on a continuation request, it MUST match the effective page size bound into the cursor. A continuation request MAY omit <tt>attributeCount</tt> because the cursor already identifies that size.</t>
</section>
<section anchor="attributecursor"><name><tt>attributeCursor</tt></name>
<t><tt>attributeCursor</tt> contains an opaque continuation value returned in the previous response.</t>
<ul><li><t>It MUST be omitted for the first page.</t></li><li><t>It MUST NOT be interpreted or constructed by the client.</t></li><li><t>It MUST contain URL-safe characters or be percent encoded.</t></li><li><t>It MUST be scoped to the original request context.</t></li><li><t>It SHOULD expire after a service-defined interval.</t></li><li><t>It MUST NOT be accepted on a resource-collection endpoint.</t></li></ul>
</section>
<section anchor="selecting-an-attribute"><name>Selecting an Attribute</name>
<t>The protected attribute is selected through the existing SCIM <tt>attributes</tt> parameter or through the default attribute-selection rules in <xref target="RFC7644"/>.</t>
<t>Example with an explicit client preference:</t>
<sourcecode type="http-message"><![CDATA[GET /Groups/group-123?attributes=id,displayName,members&attributeCount=100]]></sourcecode>
<t>Example using the provider's default page size:</t>
<sourcecode type="http-message"><![CDATA[GET /Groups/group-123?attributes=id,displayName,members]]></sourcecode>
<t><tt>attributeCursor</tt> applies to exactly one canonical protected attribute on exactly one parent resource. If a continuation request selects a different attribute, parent, projection, or other bound context, the provider MUST reject it.</t>
</section>
</section>
<section anchor="response-metadata"><name>Response Metadata</name>
<t>Pagination metadata is carried in this provisional resource extension:</t>
<sourcecode><![CDATA[urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource]]></sourcecode>
<t>The extension contains a multi-valued complex attribute named <tt>attributePagination</tt>. An array is used because a collection response can defer more than one protected attribute independently.</t>
<t>Each entry has the following fields:</t>
<table><thead><tr><th>Field</th><th>Requirement</th><th>Description</th></tr></thead><tbody>
<tr><td><tt>attribute</tt></td><td>REQUIRED</td><td>Canonical path of the protected attribute.</td></tr>
<tr><td><tt>state</tt></td><td>REQUIRED</td><td><tt>deferred</tt> or <tt>page</tt>.</td></tr>
<tr><td><tt>firstPage</tt></td><td>REQUIRED for <tt>deferred</tt></td><td>Authoritative absolute or relative URI-reference for retrieving the first page from this parent.</td></tr>
<tr><td><tt>itemsPerPage</tt></td><td>REQUIRED for <tt>page</tt></td><td>Number of values actually returned in this response.</td></tr>
<tr><td><tt>nextCursor</tt></td><td>CONDITIONAL for <tt>page</tt></td><td>Opaque cursor; present only when another page is known to exist.</td></tr>
<tr><td><tt>totalResults</tt></td><td>OPTIONAL for <tt>page</tt></td><td>Total authorized values; normally limited to the first page and omitted when expensive.</td></tr>
<tr><td><tt>attributeVersion</tt></td><td>OPTIONAL</td><td>Opaque version of the logical attribute collection when the provider offers stronger change detection.</td></tr>
</tbody></table>
<t>The presence of <tt>nextCursor</tt> is authoritative. This specification does not define a separate <tt>hasMore</tt> field because it would duplicate <tt>nextCursor</tt> and could become inconsistent with it.</t>
<t><tt>firstPage</tt> is a URI-reference as defined by <xref target="RFC3986"/>, not a cursor. It indicates that no page of that attribute was consumed in the current response. Clients SHOULD follow it as supplied rather than reconstruct it. A provider MUST generate it from trusted routing configuration and MUST NOT copy an unvalidated <tt>Host</tt> or forwarding header into the response.</t>
</section>
<section anchor="processing-rules"><name>Processing Rules</name>
<section anchor="attribute-selection"><name>Attribute Selection</name>
<t>The service provider first applies <xref target="RFC7644"/> <tt>attributes</tt>, <tt>excludedAttributes</tt>, schema <tt>returned</tt> characteristics, and authorization rules. It then applies attribute pagination only to selected and authorized protected attributes.</t>
<ul><li><t>If a protected attribute is excluded, no values and no pagination entry are returned.</t></li><li><t>If the client is not authorized to discover the attribute, no pagination entry is returned.</t></li><li><t>A provider MUST NOT use pagination metadata to reveal the existence, size, source mapping, or values of an unauthorized attribute.</t></li><li><t>An attribute with <tt>returned: always</tt> MUST be returned and is not eligible for deferred mode.</t></li></ul>
</section>
<section anchor="collection-search"><name>Collection Search</name>
<t>For a collection search returning multiple parent resources, a provider using automatic deferred mode:</t>
<ol><li><t>Retrieves the parent-resource page without invoking the loader, relationship query, exact count, or hydration path for any protected attribute.</t></li><li><t>Omits the selected protected attribute values from each resource.</t></li><li><t>Adds one <tt>attributePagination</tt> entry with state <tt>deferred</tt> for each selected protected attribute that can be retrieved for that parent.</t></li><li><t>Supplies a parent-specific <tt>firstPage</tt> URI-reference in every entry.</t></li></ol>
<t>More than one protected attribute MAY be deferred on the same resource because deferral does not load any values. Root <tt>count</tt>, <tt>cursor</tt>, <tt>page</tt>, or <tt>startIndex</tt> parameters continue to control the number of parent resources and MUST NOT be interpreted as an attribute page size.</t>
<t><tt>attributeCount</tt> and <tt>attributeCursor</tt> MUST NOT cause per-parent protected-attribute hydration on a collection endpoint. A provider MUST reject either attribute-level parameter on a collection endpoint with <tt>attributePaginationNotSupported</tt>. Root <tt>count</tt> and root cursor parameters remain valid and independent.</t>
<t>The provider SHOULD NOT issue an exact count query merely to decide whether a deferred descriptor is needed. Consequently, following <tt>firstPage</tt> can legitimately return an empty attribute page.</t>
</section>
<section anchor="resource-by-identifier-first-page"><name>Resource-by-Identifier First Page</name>
<t>When a request addresses one parent resource and selects exactly one protected attribute for hydration, the provider:</t>
<ol><li><t>Validates the parent and canonical attribute path.</t></li><li><t>Evaluates authorization for the parent, attribute, and visible values.</t></li><li><t>Chooses the effective page size from <tt>attributeCount</tt> or provider policy.</t></li><li><t>Determines a deterministic total order.</t></li><li><t>Retrieves no more than <tt>effectivePageSize + 1</tt> candidate rows from the first query that discovers the values.</t></li><li><t>Removes the look-ahead row before child, CMVA, reference, or display-value hydration.</t></li><li><t>Returns no more than the effective page size.</t></li><li><t>Returns <tt>nextCursor</tt> only when the look-ahead row proves another page exists.</t></li><li><t>MAY calculate <tt>totalResults</tt> when it can do so within configured cost limits.</t></li></ol>
<t>If more than one protected attribute is selected on a resource-by-identifier request, the provider MUST NOT hydrate all of them. It SHOULD return each as <tt>deferred</tt> with an independent <tt>firstPage</tt> URI-reference. A provider MAY instead reject an explicitly ambiguous request with <tt>multipleAttributePaginationNotSupported</tt> when that behavior is advertised. This rule permits a bare resource-by-identifier request to remain bounded even when several default-returned protected attributes exist.</t>
</section>
<section anchor="continuation-page"><name>Continuation Page</name>
<t>When <tt>attributeCursor</tt> is present, the provider:</t>
<ol><li><t>Validates cursor syntax, integrity, expiry, and context.</t></li><li><t>Re-evaluates authorization for the parent, attribute, and values.</t></li><li><t>Applies the stored keyset boundary and ordering.</t></li><li><t>Retrieves no more than <tt>effectivePageSize + 1</tt> candidate rows.</t></li><li><t>Removes the look-ahead row before hydration.</t></li><li><t>Returns a replacement <tt>nextCursor</tt> only when another page is known to exist.</t></li><li><t>SHOULD omit <tt>totalResults</tt>.</t></li></ol>
<t>An invalid cursor MUST NOT fall back to a first page, an unbounded query, or index-based attribute pagination.</t>
</section>
<section anchor="reference-derived-attributes"><name>Reference-Derived Attributes</name>
<t>For a reference-derived attribute, pagination MUST be applied at the relationship or source-resource query before referenced objects are hydrated. The provider MUST NOT first collect every referenced identifier and then paginate the hydrated result.</t>
<t>The source query SHOULD return only the identifiers and ordering values needed for the bounded page. Hydration queries SHOULD use joins, array binding, temporary tables, or bounded chunks appropriate to the database. Implementations MUST account for database expression limits; a page of 1000 plus one look-ahead row MUST NOT become a single 1001-expression <tt>IN</tt> predicate. The look-ahead row MUST be removed before hydration because it is not part of the response page.</t>
</section>
<section anchor="adaptive-safety-limits"><name>Adaptive Safety Limits</name>
<t>Page size is not the only resource limit. A provider MAY stop a page before reaching the effective page size when a response-byte, serialization, query-time, or hydration budget is reached. If more authorized values remain, it MUST return a <tt>nextCursor</tt> whose boundary is the last value actually returned.</t>
<t>A provider MUST NOT return a cursor that advances past values omitted only because a safety budget was reached.</t>
</section>
</section>
<section anchor="examples"><name>Examples</name>
<t>The values and page sizes in these examples are illustrative. The <tt>App</tt> resource type and its <tt>grants</tt> and <tt>accounts</tt> attributes are illustrative implementation-specific extensions identified by a URN in the <tt>example</tt> namespace defined by <xref target="RFC6963"/>; they are not proposed for registration.</t>
<section anchor="collection-search-with-deferred-app-grants"><name>Collection Search with Deferred App Grants</name>
<sourcecode type="http-message"><![CDATA[GET /Apps?attributes=id,displayName,grants&count=100
Prefer: scim-attribute-pagination=deferred]]></sourcecode>
<sourcecode type="json"><![CDATA[{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 2,
  "itemsPerPage": 2,
  "startIndex": 1,
  "Resources": [
    {
      "schemas": [
        "urn:example:scim:schemas:2.0:App",
        "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource"
      ],
      "id": "app-123",
      "displayName": "Payroll",
      "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource": {
        "attributePagination": [
          {
            "attribute": "grants",
            "state": "deferred",
            "firstPage": "/Apps/app-123?attributes=id%2Cgrants"
          }
        ]
      }
    },
    {
      "schemas": [
        "urn:example:scim:schemas:2.0:App",
        "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource"
      ],
      "id": "app-456",
      "displayName": "Travel",
      "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource": {
        "attributePagination": [
          {
            "attribute": "grants",
            "state": "deferred",
            "firstPage": "/Apps/app-456?attributes=id%2Cgrants"
          }
        ]
      }
    }
  ]
}]]></sourcecode>
<t>The <tt>grants</tt> arrays are absent, not empty. No grant query, count, identifier list, or hydration is performed while building this parent collection.</t>
</section>
<section anchor="first-page-without-attributecount"><name>First Page Without <tt>attributeCount</tt></name>
<t>The client follows the first link:</t>
<sourcecode type="http-message"><![CDATA[GET /Apps/app-123?attributes=id%2Cgrants]]></sourcecode>
<t>Assume the provider chooses an effective page size of two for this abbreviated example.</t>
<sourcecode type="json"><![CDATA[{
  "schemas": [
    "urn:example:scim:schemas:2.0:App",
    "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource"
  ],
  "id": "app-123",
  "grants": [
    {
      "value": "grant-001",
      "$ref": "../Grants/grant-001",
      "granteeId": "user-001"
    },
    {
      "value": "grant-002",
      "$ref": "../Grants/grant-002",
      "granteeId": "user-002"
    }
  ],
  "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource": {
    "attributePagination": [
      {
        "attribute": "grants",
        "state": "page",
        "itemsPerPage": 2,
        "nextCursor": "opaque-attribute-cursor"
      }
    ]
  }
}]]></sourcecode>
<t><tt>itemsPerPage</tt> describes the number of values actually present in the response page.</t>
</section>
<section anchor="continuation"><name>Continuation</name>
<sourcecode type="http-message"><![CDATA[GET /Apps/app-123?attributes=id%2Cgrants&attributeCursor=opaque-attribute-cursor]]></sourcecode>
<t>The continuation request does not need <tt>attributeCount</tt>; the cursor binds the effective page size. The response contains another <tt>nextCursor</tt> only if another page exists and normally omits <tt>totalResults</tt>.</t>
</section>
<section anchor="client-preferred-page-size"><name>Client-Preferred Page Size</name>
<sourcecode type="http-message"><![CDATA[GET /Groups/group-123?attributes=id%2CdisplayName%2Cmembers&attributeCount=250]]></sourcecode>
<t>The provider returns at most 250 authorized member values, subject to a smaller provider or response-byte limit.</t>
</section>
<section anchor="multiple-protected-attributes"><name>Multiple Protected Attributes</name>
<sourcecode type="http-message"><![CDATA[GET /Apps/app-123?attributes=id%2Cgrants%2Caccounts]]></sourcecode>
<t>The provider returns neither array and advertises both independently:</t>
<sourcecode type="json"><![CDATA[{
  "schemas": [
    "urn:example:scim:schemas:2.0:App",
    "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource"
  ],
  "id": "app-123",
  "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource": {
    "attributePagination": [
      {
        "attribute": "grants",
        "state": "deferred",
        "firstPage": "/Apps/app-123?attributes=id%2Cgrants"
      },
      {
        "attribute": "accounts",
        "state": "deferred",
        "firstPage": "/Apps/app-123?attributes=id%2Caccounts"
      }
    ]
  }
}]]></sourcecode>
<t>The client follows the links independently, so a single cursor never has to encode multiple positions.</t>
</section>
</section>
<section anchor="cursor-context-ordering-and-integrity"><name>Cursor Context, Ordering, and Integrity</name>
<t>An attribute cursor MUST be bound to at least:</t>
<ul><li><t>Service provider and tenant.</t></li><li><t>Parent resource type and identifier.</t></li><li><t>Canonical protected attribute path.</t></li><li><t>Requested sub-attribute projection.</t></li><li><t>Effective page size.</t></li><li><t>Service-defined ordering and null-order policy.</t></li><li><t>Last stable keyset boundary.</t></li><li><t>Source relationship or reference mapping when the attribute is derived.</t></li><li><t>Authorization-relevant client or subject context when required by provider policy.</t></li></ul>
<t>Changing a bound value MUST cause the provider to reject the cursor. Binding to an exact access-token string is NOT REQUIRED; authorization is re-evaluated on every page.</t>
<t>The provider MUST use a deterministic total order. When the logical ordering value is not unique, a stable unique identifier MUST be appended as a tie-breaker. For example:</t>
<sourcecode><![CDATA[(attributeSortValue, attributeValueId)]]></sourcecode>
<t>When no logical order is defined, ordering by an immutable relationship or value identifier is RECOMMENDED. The boundary used to create the next cursor MUST correspond to the last value actually returned, not the look-ahead row.</t>
<t>A cursor MUST be either:</t>
<ul><li><t>an unpredictable server-side handle mapped to protected state; or</t></li><li><t>an integrity-protected token whose fields cannot be modified without detection.</t></li></ul>
<t>Encoding cursor state with Base64 alone does not provide integrity. Encryption is not required when the token reveals no confidential information, but integrity or server-side validation is required.</t>
</section>
<section anchor="consistency-and-concurrent-modification"><name>Consistency and Concurrent Modification</name>
<t>Keyset pagination provides stable forward progress over the provider's ordering but does not by itself create a snapshot.</t>
<t>During traversal:</t>
<ul><li><t>Values inserted before the current boundary might not be observed.</t></li><li><t>Values inserted after the boundary might be observed.</t></li><li><t>Deleted values might disappear.</t></li><li><t>A value whose ordering key changes can be skipped or repeated.</t></li></ul>
<t>Clients MUST tolerate these outcomes unless stronger consistency is advertised. Providers SHOULD use immutable ordering keys where possible.</t>
<t>A provider MAY offer stronger change detection by returning <tt>attributeVersion</tt> and binding it into the cursor. The version represents the logical attribute collection and need not equal the parent resource's <tt>meta.version</tt>, especially for a reference-derived attribute. If strict continuation cannot be satisfied after the collection changes, the provider MAY reject the cursor and require traversal to restart.</t>
</section>
<section anchor="mutation-safety"><name>Mutation Safety</name>
<t>An attribute page or deferred representation is not a complete statement of the protected attribute's values. A client MUST NOT use it as the source for a full-resource <tt>PUT</tt> or a PATCH operation that replaces the complete protected attribute.</t>
<t>This is important because <xref target="RFC7644"/> permits a provider to clear or default omitted read-write attributes during <tt>PUT</tt>, and deployed clients sometimes use GET-modify-PUT flows.</t>
<t>Extension-aware clients SHOULD modify protected attributes with value-targeted PATCH operations, such as adding a value or removing a value selected by a value filter. A provider MAY define a compatibility profile that allows complete replacement when the client independently possesses the complete desired set.</t>
<t>When a request contains the pagination extension metadata or is otherwise known to have been derived from a partial representation, a provider SHOULD reject an unsafe full replacement with <tt>partialAttributeReplacement</tt>. A provider cannot always infer how a client constructed a request after the client removes the metadata; therefore deployment negotiation and client-profile controls remain necessary.</t>
<t>This specification does not change the authorization, mutability, uniqueness, or referential-integrity rules for modifying the underlying attribute.</t>
</section>
<section anchor="errors"><name>Errors</name>
<t>Errors use the SCIM error response defined by <xref target="RFC7644"/>.</t>
<table><thead><tr><th><tt>scimType</tt></th><th>Suggested status</th><th>Meaning</th></tr></thead><tbody>
<tr><td><tt>invalidAttributeCursor</tt></td><td>400</td><td>Cursor is malformed, modified, unknown, or does not match its request context.</td></tr>
<tr><td><tt>expiredAttributeCursor</tt></td><td>400</td><td>Cursor has expired.</td></tr>
<tr><td><tt>invalidAttributeCount</tt></td><td>400</td><td>Count is invalid or violates provider policy.</td></tr>
<tr><td><tt>attributePaginationNotSupported</tt></td><td>400</td><td>The resource, attribute, endpoint, or request form does not support attribute pagination.</td></tr>
<tr><td><tt>multipleAttributePaginationNotSupported</tt></td><td>400</td><td>The request attempts to hydrate or continue more than one protected attribute.</td></tr>
<tr><td><tt>attributePaginationRequired</tt></td><td>400</td><td>The provider cannot safely perform unbounded retrieval and deferred behavior has not been established for the client.</td></tr>
<tr><td><tt>partialAttributeReplacement</tt></td><td>400</td><td>The request attempts unsafe replacement using a partial or deferred attribute representation.</td></tr>
</tbody></table>
<t>Example:</t>
<sourcecode type="json"><![CDATA[{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "status": "400",
  "scimType": "invalidAttributeCursor",
  "detail": "Attribute cursor invalid for this request.\n                Request the first page again."
}]]></sourcecode>
<t>Providers SHOULD avoid details that disclose whether a parent, protected attribute, hidden value, tenant, or cursor boundary exists.</t>
</section>
<section anchor="operational-limits"><name>Operational Limits</name>
<t>A service provider MUST be able to bound the work performed on behalf of a single client. Deferred collection retrieval converts one large response into a series of optional follow-up requests, so a client can still fan out across many parents and attributes; the mechanism relocates the work rather than eliminating it.</t>
<t>A service provider therefore MUST enforce a maximum attribute page size (Section 5) and SHOULD enforce limits on response size, query and hydration time, request rate, concurrency for expensive protected attributes, cursor issuance and lifetime, and the number of deferred descriptors returned in one response. When a limit is reached the provider MUST terminate the page early and return a cursor rather than exceeding the limit or returning an incomplete page without one (Section 8).</t>
<t>For complete extraction of very large datasets, providers SHOULD offer an asynchronous export or bulk mechanism rather than encouraging millions of synchronous cursor requests.</t>
<t>Appendix C gives non-normative implementation guidance on query construction, budget selection, and observability.</t>
<section anchor="totals-and-empty-attributes"><name>Totals and Empty Attributes</name>
<t>Exact totals can be as expensive as retrieving the values and are not required for forward traversal. Providers SHOULD omit <tt>totalResults</tt> when it requires a costly count. Deferred collection responses SHOULD NOT count protected values merely to populate metadata.</t>
<t>An advertised <tt>firstPage</tt> can return zero values and no <tt>nextCursor</tt>. This is not an error.</t>
</section>
</section>
<section anchor="security-and-privacy-considerations"><name>Security and Privacy Considerations</name>
<t>Authorization MUST be evaluated for every page. Possession of an attribute cursor or <tt>firstPage</tt> link does not grant access to the parent, attribute, or referenced values.</t>
<t>Providers MUST prevent a cursor from being used across tenants, parent resources, protected attributes, projections, or incompatible authorization contexts. Replaying the same cursor for the same authorized context MAY return the same logical page; cross-context replay MUST fail.</t>
<t>Cursors are untrusted input. A provider MUST validate syntax, length, integrity, expiry, and context before using cursor fields in a query. Cursor fields MUST be passed to databases as bound parameters and MUST NOT be concatenated into SQL.</t>
<t>Cursors can appear in URLs, browser history, proxy logs, and telemetry. They SHOULD contain no plaintext personal data, credentials, tenant names, or sensitive internal identifiers. Providers SHOULD redact query parameters from logs and use short, risk-appropriate cursor lifetimes. Cursor responses SHOULD be treated as private authenticated content by caches.</t>
<t>The provider MUST re-evaluate value-level authorization on each page. <tt>itemsPerPage</tt> and <tt>totalResults</tt>, when returned, MUST describe values visible to the requester unless the provider explicitly defines a different non-leaking policy.</t>
<t>The deferred descriptor itself can reveal that an attribute exists for a resource. It MUST be omitted when the requester is not authorized to discover the attribute.</t>
<t>Page-size limits alone do not eliminate denial-of-service risk. Providers SHOULD apply the work budgets in Section 14.3 and MAY return HTTP 429 or 503 when rate or capacity limits are reached.</t>
</section>
<section anchor="relationship-to-other-approaches"><name>Relationship to Other Approaches</name>
<section anchor="rfc-9865"><name>RFC 9865</name>
<t><xref target="RFC9865"/> pages collections of SCIM resources. This document reuses its main design properties—opaque forward cursors, provider-selected defaults, maximum page sizes, cursor expiry, per-page authorization, and no fallback after an invalid cursor—but scopes the cursor to values of one attribute on one parent.</t>
</section>
<section anchor="multi-value-paging-and-filtering-drafts"><name>Multi-Value Paging and Filtering Drafts</name>
<t><xref target="I-D.hunt-scim-mv-paging"/> (Hunt and Wilson) and its replacement <xref target="I-D.hunt-scim-mv-filtering"/> (Hunt) previously proposed value qualifiers containing index pagination and filters inside the <tt>attributes</tt> parameter, including paging values across multiple returned parents. Support was discovered through a boolean <tt>mvpaging</tt> attribute in <tt>ServiceProviderConfig</tt>. The SCIM working group charter names this work as input for the multi-value query filtering and paging work item.</t>
<t>A call for adoption of <xref target="I-D.hunt-scim-mv-filtering"/> was issued to the working group in May 2022. The draft was not adopted, for lack of sufficient responses; the substantive response received did not support adoption, on the grounds summarized in Section 16.8. This document does not assume that history is irrelevant to it. Sections 16.8 and 1.1 set out what this document claims that the 2022 proposal did not, and Section 16.9 records the design question that remains open.</t>
<t>This document uses that work as input and differs in three respects. First, it separates parent collection traversal from attribute traversal, which bounds work and avoids encoding multiple parent-specific continuation positions in one request. Second, it uses an opaque keyset cursor rather than index qualifiers, for the reasons given in Section 16.2.1. Third, it uses a structured discovery extension rather than a single boolean, because a service provider needs to advertise which attributes are protected, what page-size limits apply, and which modes are supported, none of which a boolean can express.</t>
<t>Two properties claimed for the qualifier approach warrant direct comment, because this document deliberately trades both away.</t>
<t><em>Backward compatibility.</em> Qualifier-based paging is backward compatible in the sense that a client that sends no qualifier receives unmodified behavior. That property is preserved here by the explicit mode of Section 4.1. It is not preserved by automatic deferred mode, and this document does not claim otherwise. The reason is that explicit mode alone does not solve the operational problem: an unmodified client issuing a bare <tt>GET /Groups/{id}</tt> against a group with millions of members still causes the unbounded work described in Section 2, because the client has no way to know it should ask for less. A mechanism that only protects the service provider when the client cooperates does not protect the service provider. Automatic mode exists to close that gap, and the negotiation surface in Sections 4.1 and 4.2 exists to make the resulting behavior change safe and observable rather than silent.</t>
<t><em>Statelessness.</em> Index qualifiers are stateless in that the server retains nothing between requests. This document accepts server-side or cursor-encoded continuation state because statelessness and correctness are in conflict here: an index position is only a valid continuation point if the underlying value sequence has not changed, and Section 16.2.1 documents what happens in deployment when it has. The cursors defined in this document are self-describing and integrity-protected rather than stored server-side (Section 11), which recovers most of the operational benefit of statelessness without inheriting the correctness defect.</t>
<section anchor="ldap-range-retrieval-and-the-index-qualifier-fai"><name>LDAP Range Retrieval and the Index-Qualifier Failure Mode</name>
<t>Index-qualified retrieval of multi-valued attribute values has been deployed at scale for more than two decades and its limitations are documented. The expired <xref target="I-D.kashi-incremental"/> proposed incremental retrieval of multi-valued properties, and Active Directory implements this mechanism as range retrieval: a client attaches a <tt>range=low-high</tt> option to an attribute name in a search request, and the server returns that subset, with the maximum values returned in one response governed by a server policy.</t>
<t>The relevant operational finding is the ordering guarantee. Value ordering in a range retrieval request is arbitrary, and consistent across successive requests only while the attribute is not modified between them. Implementers have reported the consequence directly: when another client adds or removes values between a client's successive range requests, the retrieved sequence can contain overlapping, duplicated, or skipped values.</t>
<t>This is the same class of defect that index-based collection pagination exhibits, occurring inside a single resource, and it is the primary reason this document specifies keyset cursors bound to a deterministic total ordering (Section 7) rather than index ranges. It is also why this document specifies concurrent-modification semantics and optional attribute versioning explicitly rather than leaving them to implementations.</t>
<t>A related contemporaneous proposal, <tt>draft-haripriya-partial-entry</tt>, addressed partial entry retrieval from a different direction and is noted here for completeness.</t>
</section>
</section>
<section anchor="odata-collection-continuation"><name>OData Collection Continuation</name>
<t>OData 4.01 <xref target="OData-JSON"/> permits a partial collection-valued property or expanded navigation collection to carry a property-specific <tt>nextLink</tt>, annotated as <tt>{property}@odata.nextLink</tt>. A response to an expanded parent collection can therefore return a bounded set of child values per parent together with a per-parent continuation link. The <tt>firstPage</tt> and <tt>nextCursor</tt> concepts in this document follow the same architectural principle: continuation metadata belongs to the partial nested collection, while the values remain associated with their parent.</t>
<t>OASIS also considered the scoping question this document faces. Issue ODATA-519 records that client-driven paging with <tt>$top</tt> and <tt>$skip</tt> was permitted for all collection-valued resources while server-driven paging via next links was restricted to collections of entities or entity references, and notes that there is no semantic reason for that restriction. This document does not adopt the restriction: the deferred and paged behavior defined here applies to any complex multi-valued attribute a service provider designates as protected, whether or not its values are references.</t>
</section>
<section anchor="relationship-and-sub-resource-models"><name>Relationship and Sub-Resource Models</name>
<t>Two related proposals represent a multi-valued attribute's values as resources rather than as attribute values: a top-level relationship resource such as <tt>/GroupMembers</tt>, defined in <xref target="I-D.zollner-scim-groupmember"/>, and a per-attribute sub-resource endpoint such as <tt>/Groups/{id}/members</tt> or a generalized <tt>/{ResourceType}/{id}/{attributeUrn}</tt>. Both enable standard collection pagination, filtering, and in the relationship-resource case per-relationship create and delete operations.</t>
<t>This document is not an alternative to either. Its relationship to each differs.</t>
<section anchor="top-level-relationship-resources"><name>Top-Level Relationship Resources</name>
<t>A top-level relationship resource addresses <em>relationship management</em>: individually addressable memberships, cross-collection queries such as retrieving all memberships created within a time window, atomic per-relationship lifecycle operations, and change detection over the relationship set. Those capabilities require relationships to be first-class resources and cannot be provided by attribute pagination. This document does not attempt to provide them.</t>
<t>This document addresses <em>bounded retrieval</em>: how a service provider returns a resource that carries a high-cardinality attribute, without unbounded hydration, for any complex multi-valued attribute. The two concerns are orthogonal, and a provider that implements one still needs the other. A provider exposing <tt>/GroupMembers</tt> still returns <tt>Group</tt> resources containing a <tt>members</tt> attribute, because <tt>Group.members</tt> remains defined by <xref target="RFC7643"/> with <tt>returned: default</tt>, and that provider still requires defined behavior for the attribute when the group is large. This document supplies it. Support for one does not imply support for the other, and neither obsoletes the other.</t>
</section>
<section anchor="per-attribute-sub-resource-endpoints"><name>Per-Attribute Sub-Resource Endpoints</name>
<t>The sub-resource model and the mechanism defined here converge, and the difference between them is narrower than it first appears.</t>
<t>The <tt>firstPage</tt> URI-reference defined in Section 6 <em>is</em> a per-attribute endpoint address. A collection search returns each parent resource without its protected attribute values together with an authoritative address at which those values may be traversed. This is the same retrieval shape as a sub-resource endpoint, and it produces the same client flow: obtain the parent representation without the high-cardinality attribute, then traverse that attribute separately at its own address.</t>
<t>This document specifies that address as provider-generated and discovered rather than client-constructed from a URI template. That choice is deliberate and is the substantive difference between the two models:</t>
<ul><li><t>A provider-generated link can bind ordering, projection, filter, and authorization context into the address itself (Section 10), so a continuation is valid only in the context that produced it. A client-constructed template cannot carry that context and must reconstruct it from query parameters that the client may combine inconsistently.</t></li><li><t>Discovery (Section 5) tells a client which attributes behave this way before it issues a request, rather than requiring the client to know or guess which sub-resource endpoints a given provider exposes for a given resource type.</t></li><li><t>The mechanism applies uniformly to attributes for which no sub-resource endpoint has been defined, including implementation-specific and dynamically derived attributes, without requiring a new endpoint and a new registration for each one.</t></li><li><t>The parent resource's representation remains the response envelope, so the client receives the attribute in the schema context it belongs to, and <tt>meta.version</tt>, schema URIs, and <tt>returned</tt> characteristics remain meaningful for the values returned.</t></li></ul>
<t>A service provider that exposes per-attribute sub-resource endpoints MAY use those endpoint addresses as the <tt>firstPage</tt> value, in which case the two models coincide exactly and this document supplies the discovery, ordering, continuation, and mutation-safety contract that a bare endpoint definition otherwise leaves to implementations.</t>
</section>
</section>
<section anchor="whole-resource-pagination"><name>Whole-Resource Pagination</name>
<t>An alternative raised in working group mailing list discussion is to paginate the JSON object representing a SCIM resource, treating the serialized resource rather than one attribute's value list as the unit of division. This has the attraction of addressing a resource with several large multi-valued attributes, and of addressing large single-valued attributes, in one mechanism.</t>
<t>This document does not adopt that model, for four reasons.</t>
<ul><li><t>A division of the serialized object has no attribute semantics. It cannot express that an attribute with <tt>returned: always</tt> must never be withheld, and it cannot distinguish a withheld attribute from an empty one, which Section 4 identifies as a correctness requirement for mutation safety.</t></li><li><t>A continuation point over serialized output cannot be bound to a deterministic ordering of the underlying value set, so it reintroduces the concurrent-modification defect described in Section 16.2.1 at the document level rather than the attribute level.</t></li><li><t>Authorization is applied per value (Section 15). A serialization-level boundary can split a value set at a point that does not correspond to any authorization decision, so the provider must still evaluate the whole set before deciding what to emit. Response size is bounded; the underlying work is not.</t></li><li><t>The generated boundary is not addressable. A client cannot request the second page of one specific attribute without traversing what precedes it, which defeats the selective-retrieval use case in Section 3.</t></li></ul>
<t>A provider needing to bound several protected attributes on one resource achieves this with the mechanism defined here by deferring each independently and hydrating one per response, as specified in Section 8.</t>
</section>
<section anchor="http-chunked-transfer-encoding"><name>HTTP Chunked Transfer Encoding</name>
<t>Chunked transfer encoding has been raised in SCIM working group mailing list discussion as an existing mechanism that could address large resource representations without a SCIM-level extension.</t>
<t>Chunked transfer encoding bounds neither the work described in Section 2 nor the memory required to produce the response. It permits a response to be transmitted without a known content length, which allows the service provider to begin transmitting before serialization completes. It does not reduce the number of values the provider must retrieve, hydrate, authorize, or serialize, and it does not bound the intermediate identifier lists, expression-limit exposure, or fan-out during collection searches. A provider that streams a group of several million members has converted a large allocation into a long-running one; the request is still unbounded, and the client still receives a representation it cannot process incrementally without a value-level continuation contract.</t>
<t>Chunked transfer encoding is complementary to this document, not an alternative to it, and a provider MAY use it for the bounded pages defined here.</t>
</section>
<section anchor="deployed-practices-and-implementation-experience"><name>Deployed Practices and Implementation Experience</name>
<t>Deployed identity clients demonstrate both sides of the compatibility problem. Microsoft Entra provisioning requests Groups with <tt>excludedAttributes=members</tt> and uses targeted membership operations, which naturally avoids unbounded reads. Okta documents that a bare <tt>GET /Groups/{id}</tt> must return <tt>members</tt> and that some custom integrations use PUT with full membership state. PingOne management APIs use provider-defined page limits and opaque cursor links for large collections. Providers therefore need client-specific migration evidence before changing automatic behavior for an established integration.</t>
<t>Implementers have independently converged on the retrieval pattern this document standardizes. One reported deployment handling groups exceeding one hundred thousand members retrieves the group with its members excluded and then paginates the membership through a separate endpoint, having added that endpoint as an implementation-specific extension. That two-step flow — parent representation without the high-cardinality attribute, followed by a bounded traversal of the attribute at its own address — is the flow specified in Sections 6 and 8 of this document. The contribution here is not the pattern but its standardization: an authoritative provider-generated <tt>firstPage</tt> link rather than a client-constructed URI template, a deterministic ordering and continuation contract, and discovery that tells a client which attributes behave this way before it issues the first request.</t>
</section>
<section anchor="inverse-filtering-over-the-counterpart-resource"><name>Inverse Filtering Over the Counterpart Resource</name>
<t>Working group feedback on the earlier multi-value filtering proposal observed that the common group-membership use cases can be satisfied with existing SCIM filtering and ordinary resource pagination, without any protocol extension. Where a counterpart resource exists and the inverse relationship is filterable, membership can be enumerated as a resource collection:</t>
<sourcecode type="http-message"><![CDATA[GET /Users?filter=groups.value eq "2819c223-7f76-453a-919d-413861904646"]]></sourcecode>
<t>The results are resources rather than attribute values, so they page under Section 3.4.2.4 of <xref target="RFC7644"/> with no new mechanism. Membership tests reduce to compound filters in the same way.</t>
<t>This document accepts that analysis. For enumerating the members of a group, or the groups containing a user, inverse filtering is an effective technique that requires nothing new, and clients that can use it should. The observation that the base SCIM schemas contain few attributes large enough to warrant server-side value pagination — principally <tt>Group.members</tt> and <tt>User.groups</tt> — is also accepted; see Section 3.</t>
<t>Three limits define what remains.</t>
<t>First, and most importantly, inverse filtering answers a different question. It provides a way for a client to enumerate memberships; it does not define what a service provider returns when a client requests the <em>parent resource</em>, which <xref target="RFC7643"/> entitles it to do and which returns <tt>members</tt> by default. Section 1.1 sets out that gap. A provider cannot decline to answer <tt>GET /Groups/{id}</tt> on the grounds that a better query exists.</t>
<t>Second, it requires a filterable inverse. <tt>User.groups</tt> is defined with <tt>mutability: readOnly</tt>, and support for filtering on it is uneven across deployed service providers. Where the counterpart attribute is not filterable, or is not indexed for that access path, the technique is unavailable and the client has no way to discover this in advance.</t>
<t>Third, it requires a counterpart resource. Implementation-specific assignment attributes are frequently derived rather than persisted, and frequently have no resource on the other side of the relationship to filter over. For these attributes the inverse query does not exist to be issued.</t>
<t>Inverse filtering and this document are therefore complementary. A service provider SHOULD support inverse filtering where the relationship permits it, and clients SHOULD prefer it for enumeration. This document defines what happens on the paths inverse filtering does not cover.</t>
</section>
<section anchor="root-level-attribute-traversal-open-issue"><name>Root-Level Attribute Traversal (Open Issue)</name>
<t>[[ RFC Editor / Working Group note: this section states an open design question that this revision does not resolve. It is included because the question is material to the value of the mechanism. To be resolved before working group last call. ]]</t>
<t>As specified in this revision, traversing a protected attribute requires a request per parent resource. A collection search returns each parent with a <tt>firstPage</tt> link, and the client follows one link per parent. For a client enumerating memberships across many groups, this is a request per group.</t>
<t>This is a substantive limitation of the mechanism as specified, not merely an inefficiency, for two reasons. First, the request count scales with the number of parent resources rather than with the number of values retrieved. Second, and more consequentially, it forecloses a root-level form such as:</t>
<sourcecode type="http-message"><![CDATA[GET /Groups?attributes=members&attributeCursor=<opaque>]]></sourcecode>
<t>in which a single traversal spans parents. That form is a prerequisite for change-tracking and delta-query mechanisms over relationship sets, which are anticipated future work for this protocol. A design that cannot express it constrains that work.</t>
<t>The cursor mechanism defined in Section 10 appears capable of carrying this form. A keyset cursor is defined over a deterministic total ordering; extending that ordering from <tt>(value identifier)</tt> within one parent to the composite <tt>(parent identifier, value identifier)</tt> across a collection yields a single continuation sequence over the whole relationship set. The per-parent traversal specified in Section 8 then becomes the case in which the ordering is constrained to one parent, rather than the only available mode. A delta traversal would order over a change sequence instead.</t>
<t>Several questions remain unresolved, and this document does not treat the design as settled:</t>
<ul><li><t>What the response envelope should be. A root-level traversal returns values belonging to many parents; whether these are returned as partial parent resources, as a flat sequence of <tt>(parent, value)</tt> pairs, or in some other form determines whether the result remains interpretable in the schema context of its parent.</t></li><li><t>Whether the composite ordering can be evaluated efficiently by service providers whose relationship storage is partitioned by parent, which is a common arrangement and the reason the per-parent form was specified first.</t></li><li><t>How the result relates to a relationship resource collection (Section 16.4.1). A flat <tt>(parent, value)</tt> sequence and a paged relationship resource collection may be close enough that specifying both is not warranted.</t></li><li><t>Whether authorization can be evaluated per value at acceptable cost when a page spans parents with differing access-control outcomes.</t></li></ul>
<t>Working group input is invited on all four, and specifically on whether this work belongs in this document or in the relationship-resource work described in Section 16.4.1.</t>
</section>
</section>
<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document requests registration of the following URIs in the IANA "System for Cross-domain Identity Management (SCIM) Schema URIs" registries.</t>
<section anchor="scim-schema-uri-for-data-resources"><name>SCIM Schema URI for Data Resources</name>
<ul><li><t><strong>Schema URI:</strong> <tt>urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource</tt></t></li><li><t><strong>Name:</strong> Attribute Pagination Resource Extension</t></li><li><t><strong>Reference:</strong> This document</t></li></ul>
</section>
<section anchor="scim-server-related-schema-uri"><name>SCIM Server-Related Schema URI</name>
<ul><li><t><strong>Schema URI:</strong> <tt>urn:ietf:params:scim:schemas:extension:attributePagination:2.0:ServiceProviderConfig</tt></t></li><li><t><strong>Name:</strong> Attribute Pagination Service Provider Configuration Extension</t></li><li><t><strong>Reference:</strong> This document</t></li></ul>
</section>
<section anchor="http-preference"><name>HTTP Preference</name>
<t>This document requests registration of the following preference in the IANA "HTTP Preferences" registry defined by <xref target="RFC7240"/>:</t>
<ul><li><t><strong>Preference:</strong> <tt>scim-attribute-pagination</tt></t></li><li><t><strong>Value:</strong> <tt>deferred</tt></t></li><li><t><strong>Optional Parameters:</strong> None</t></li><li><t><strong>Description:</strong> Indicates that the client understands deferred and cursor-paged SCIM multi-valued attribute representations defined by this document.</t></li><li><t><strong>Reference:</strong> This document</t></li></ul>
<t>The SCIM detail error keywords in Section 13 extend Table 9 of <xref target="RFC7644"/>. No separate IANA registry for SCIM detail error keywords exists at the time of writing.</t>
</section>
</section>
<section anchor="acknowledgements"><name>Acknowledgements</name>
<t>The author thanks the following contributors, whose input shaped this revision: Phillip Hunt, for the earlier multi-value paging and filtering work that this document takes as input and for the history of that effort; Daniel Cormier, for reporting deployed implementation experience with large group memberships; Danny Zollner, for identifying the resource-oriented alternatives evaluated in Section 16 and for detailed review comments on the -00 revision; and Matt Peterson, whose 2022 analysis of membership use cases is recorded in Section 16.8.</t>
<t>Acknowledgement does not imply endorsement. Several of the contributors named above have expressed reservations about the approach taken in this document, and Section 16 records those positions.</t>
</section>

  </middle>
  <back>
    <references><name>References</name>
      <references><name>Normative References</name>
<reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119"><front><title>Key words for use in RFCs to Indicate Requirement Levels</title><author initials="S." surname="Bradner"/><date year="1997"/></front><seriesInfo name="BCP" value="14"/><seriesInfo name="RFC" value="2119"/><seriesInfo name="DOI" value="10.17487/RFC2119"/></reference><reference anchor="RFC3986" target="https://www.rfc-editor.org/info/rfc3986"><front><title>Uniform Resource Identifier (URI): Generic Syntax</title><author initials="T." surname="Berners-Lee"/><date year="2005"/></front><seriesInfo name="STD" value="66"/><seriesInfo name="RFC" value="3986"/><seriesInfo name="DOI" value="10.17487/RFC3986"/></reference><reference anchor="RFC7240" target="https://www.rfc-editor.org/info/rfc7240"><front><title>Prefer Header for HTTP</title><author initials="J." surname="Snell"/><date year="2014"/></front><seriesInfo name="RFC" value="7240"/><seriesInfo name="DOI" value="10.17487/RFC7240"/></reference><reference anchor="RFC7643" target="https://www.rfc-editor.org/info/rfc7643"><front><title>System for Cross-domain Identity Management: Core Schema</title><author initials="P." surname="Hunt"/><date year="2015"/></front><seriesInfo name="RFC" value="7643"/><seriesInfo name="DOI" value="10.17487/RFC7643"/></reference><reference anchor="RFC7644" target="https://www.rfc-editor.org/info/rfc7644"><front><title>System for Cross-domain Identity Management: Protocol</title><author initials="P." surname="Hunt"/><date year="2015"/></front><seriesInfo name="RFC" value="7644"/><seriesInfo name="DOI" value="10.17487/RFC7644"/></reference><reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174"><front><title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title><author initials="B." surname="Leiba"/><date year="2017"/></front><seriesInfo name="BCP" value="14"/><seriesInfo name="RFC" value="8174"/><seriesInfo name="DOI" value="10.17487/RFC8174"/></reference><reference anchor="RFC9865" target="https://www.rfc-editor.org/info/rfc9865"><front><title>Cursor-Based Pagination of System for Cross-domain Identity Management (SCIM) Resources</title><author initials="M." surname="Peterson"/><date year="2025"/></front><seriesInfo name="RFC" value="9865"/><seriesInfo name="DOI" value="10.17487/RFC9865"/></reference>
      </references>
      <references><name>Informative References</name>
<reference anchor="RFC6963" target="https://www.rfc-editor.org/info/rfc6963"><front><title>A Uniform Resource Name (URN) Namespace for Examples</title><author initials="P." surname="Saint-Andre"/><date year="2013"/></front><seriesInfo name="BCP" value="183"/><seriesInfo name="RFC" value="6963"/><seriesInfo name="DOI" value="10.17487/RFC6963"/></reference>
<reference anchor="I-D.hunt-scim-mv-filtering">
  <front><title>SCIM Protocol: Multi-Value Filtering Extension</title>
  <author initials="P." surname="Hunt"/><date year="2021"/></front>
  <seriesInfo name="Internet-Draft" value="draft-hunt-scim-mv-filtering-00"/>
  <refcontent>Work in Progress</refcontent>
</reference>
<reference anchor="I-D.hunt-scim-mv-paging">
  <front><title>SCIM Protocol: Multi-Value Paging Extension</title>
  <author initials="P." surname="Hunt"/><author initials="G." surname="Wilson"/>
  <date year="2019"/></front>
  <seriesInfo name="Internet-Draft" value="draft-hunt-scim-mv-paging-00"/>
  <refcontent>Work in Progress</refcontent>
</reference>
<reference anchor="I-D.kashi-incremental">
  <front><title>Incremental Retrieval of Multi-valued Properties</title>
  <author surname="Kashi"/><date year="1999"/></front>
  <seriesInfo name="Internet-Draft" value="draft-kashi-incremental-00"/>
  <refcontent>Work in Progress</refcontent>
</reference>
<reference anchor="I-D.zollner-scim-groupmember">
  <front><title>SCIM GroupMember Resource Extension</title>
  <author initials="D." surname="Zollner"/><date year="2026"/></front>
  <seriesInfo name="Internet-Draft" value="draft-zollner-scim-groupmember-resource-extension"/>
  <refcontent>Work in Progress</refcontent>
</reference>
<reference anchor="MS-ADTS" target="https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/">
  <front><title>[MS-ADTS]: Active Directory Technical Specification</title>
  <author><organization>Microsoft</organization></author><date/></front>
</reference>
<reference anchor="OData-JSON" target="https://docs.oasis-open.org/odata/">
  <front><title>OData JSON Format Version 4.01</title>
  <author><organization>OASIS</organization></author><date/></front>
</reference>
      </references>
    </references>
<section anchor="provisional-scim-schema-definitions"><name>Provisional SCIM Schema Definitions</name>
<t>The definitions in this appendix are provisional review representations. They are to be converted to the final RFCXML artwork and checked with the designated SCIM schema expert before publication.</t>
<section anchor="resource-extension-schema"><name>Resource Extension Schema</name>
<sourcecode type="json"><![CDATA[{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
  "id": "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:Resource",
  "name": "AttributePaginationResourceExtension",
  "description": "Describes deferred or cursor-paged multi-valued attributes on a SCIM resource.",
  "attributes": [
    {
      "name": "attributePagination",
      "type": "complex",
      "multiValued": true,
      "required": false,
      "mutability": "readOnly",
      "returned": "default",
      "uniqueness": "none",
      "description": "Independent retrieval state for protected multi-valued attributes.",
      "subAttributes": [
        {
          "name": "attribute",
          "type": "string",
          "multiValued": false,
          "required": true,
          "caseExact": false,
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Canonical SCIM path of the protected attribute."
        },
        {
          "name": "state",
          "type": "string",
          "multiValued": false,
          "required": true,
          "caseExact": true,
          "canonicalValues": ["deferred", "page"],
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Indicates whether values are deferred or a page is present."
        },
        {
          "name": "firstPage",
          "type": "reference",
          "referenceTypes": ["uri"],
          "multiValued": false,
          "required": false,
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Authoritative URI-reference used to retrieve the first page."
        },
        {
          "name": "itemsPerPage",
          "type": "integer",
          "multiValued": false,
          "required": false,
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Values returned in this page."
        },
        {
          "name": "nextCursor",
          "type": "string",
          "multiValued": false,
          "required": false,
          "caseExact": true,
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Opaque cursor for the next page."
        },
        {
          "name": "totalResults",
          "type": "integer",
          "multiValued": false,
          "required": false,
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Optional total authorized values."
        },
        {
          "name": "attributeVersion",
          "type": "string",
          "multiValued": false,
          "required": false,
          "caseExact": true,
          "mutability": "readOnly",
          "returned": "default",
          "uniqueness": "none",
          "description": "Optional collection version token."
        }
      ]
    }
  ]
}]]></sourcecode>
</section>
<section anchor="service-provider-configuration-extension-schema"><name>Service Provider Configuration Extension Schema</name>
<sourcecode type="json"><![CDATA[{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
  "id":
   "urn:ietf:params:scim:schemas:extension:attributePagination:2.0:ServiceProviderConfig",
  "name": "AttributePaginationServiceProviderConfigExtension",
  "description": "Support and policy for attribute pagination.",
  "attributes": [
    {
      "name": "supported",
      "type": "boolean",
      "multiValued": false,
      "required": true,
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "cursor",
      "type": "boolean",
      "multiValued": false,
      "required": true,
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "defaultMode",
      "type": "string",
      "multiValued": false,
      "required": false,
      "caseExact": true,
      "canonicalValues": ["explicit", "deferred"],
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "defaultPageSize",
      "type": "integer",
      "multiValued": false,
      "required": false,
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "maxPageSize",
      "type": "integer",
      "multiValued": false,
      "required": false,
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "maxHydratedAttributesPerResponse",
      "type": "integer",
      "multiValued": false,
      "required": false,
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "cursorTimeout",
      "type": "integer",
      "multiValued": false,
      "required": false,
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "totalResults",
      "type": "string",
      "multiValued": false,
      "required": false,
      "caseExact": true,
      "canonicalValues": ["unsupported", "optional", "supported"],
      "mutability": "readOnly",
      "returned": "default"
    },
    {
      "name": "resourceTypes",
      "type": "complex",
      "multiValued": true,
      "required": false,
      "mutability": "readOnly",
      "returned": "default",
      "subAttributes": [
        {
          "name": "resourceType",
          "type": "string",
          "multiValued": false,
          "required": true,
          "caseExact": true,
          "mutability": "readOnly",
          "returned": "default"
        },
        {
          "name": "attributes",
          "type": "string",
          "multiValued": true,
          "required": true,
          "caseExact": false,
          "mutability": "readOnly",
          "returned": "default"
        }
      ]
    }
  ]
}]]></sourcecode>
</section>
</section>
<section anchor="changes-from-00"><name>Changes from -00</name>
<t>[[ RFC Editor: please remove this section before publication. ]]</t>
<ul><li><t>Changed intended status from Informational to Standards Track because automatic deferral replaces <xref target="RFC7644"/> Section 3.9 response semantics for negotiating parties and adds attributes to existing <xref target="RFC7643"/> structures.</t></li><li><t>Expanded the problem from explicitly requested single-resource pagination to both deferred collection retrieval and bounded resource-by-identifier retrieval.</t></li><li><t>Made <tt>attributeCount</tt> optional and defined a provider-selected default page size.</t></li><li><t>Added advertised <tt>explicit</tt> and automatic <tt>deferred</tt> modes plus staged compatibility-profile guidance.</t></li><li><t>Added an <xref target="RFC7240"/> in-band preference for client opt-in without making its absence an unsafe-behavior request.</t></li><li><t>Replaced the single metadata object with an <tt>attributePagination</tt> array supporting independent deferred descriptors.</t></li><li><t>Added <tt>state</tt> and <tt>firstPage</tt> fields and removed the redundant <tt>hasMore</tt> field.</t></li><li><t>Allowed multiple protected attributes to be deferred while retaining at most one hydrated attribute per response.</t></li><li><t>Defined selection behavior for <tt>attributes</tt>, <tt>excludedAttributes</tt>, <tt>returned: always</tt>, default-returned attributes, authorization, and empty attributes.</t></li><li><t>Added <tt>Updates: 7643, 7644</tt>, following <xref target="RFC9865"/>, together with replacement rules for <xref target="RFC7644"/> Section 3.9 attribute-return behavior scoped to negotiated parties, and documented that automatic deferral is not wire-compatible with every existing client.</t></li><li><t>Added GET-modify-PUT and full-replacement safety requirements.</t></li><li><t>Added adaptive byte/time budgets, fan-out controls, and asynchronous export guidance.</t></li><li><t>Added database expression-limit guidance and required removal of the look-ahead row before hydration.</t></li><li><t>Strengthened cursor context, total ordering, integrity, logging, reauthorization, and privacy requirements.</t></li><li><t>Added concurrent-modification semantics and optional <tt>attributeVersion</tt> change detection.</t></li><li><t>Added implementation-neutral handling for persisted and reference-derived attributes.</t></li><li><t>Added observability guidance using bounded dimensions.</t></li><li><t>Reframed the abstract and introduction around the <xref target="RFC7643"/> <tt>returned: default</tt> conformance gap rather than around generalization across attribute types, and added Section 1.1 enumerating the non-conformant or unsafe options currently available to a service provider.</t></li><li><t>Added Section 16.8 accepting inverse filtering over the counterpart resource as effective for membership enumeration, and stating the three limits that leave the conformance gap open.</t></li><li><t>Added Section 16.9 recording root-level attribute traversal as an open design question, including the composite <tt>(parent, value)</tt> ordering that may express it and four unresolved questions put to the working group.</t></li><li><t>Recorded the May 2022 call for adoption of <xref target="I-D.hunt-scim-mv-filtering"/> and its outcome (Section 16.2).</t></li><li><t>Moved query construction, budget selection, and observability guidance from Section 14 into non-normative Appendix C, leaving normative operational limits in the body.</t></li><li><t>Added prior-art comparisons with <xref target="RFC9865"/>, the Hunt multi-value paging and filtering drafts, LDAP/Active Directory range retrieval and its documented ordering failure mode, OData nested collection continuation, top-level relationship resources, and per-attribute sub-resource endpoints.</t></li><li><t>Addressed feedback received on the SCIM mailing list in January and February 2026: documented the backward-compatibility and statelessness trade-offs relative to qualifier-based paging (Section 16.2), added rebuttals of whole-resource pagination (Section 16.5) and HTTP chunked transfer encoding (Section 16.6), and recorded reported implementation experience with the deferred retrieval pattern (Section 16.7).</t></li><li><t>Reframed the relationship to resource-based models: the <tt>firstPage</tt> link is described as a provider-generated per-attribute endpoint address, and Section 16.4.2 states the conditions under which the sub-resource model and this mechanism coincide.</t></li><li><t>Added deployment interoperability considerations based on Microsoft Entra and Okta client behavior.</t></li><li><t>Added provisional IANA registration requests for the resource and ServiceProviderConfig extension schema URIs and the HTTP preference token.</t></li></ul>
</section>
<section anchor="implementation-guidance-non-normative"><name>Implementation Guidance (Non-Normative)</name>
<t>This appendix contains implementation guidance. It defines no protocol behavior and conformance does not depend on it.</t>
<section anchor="persisted-attributes"><name>Persisted Attributes</name>
<t>A directly persisted attribute can use a parent-scoped keyset query:</t>
<sourcecode type="sql"><![CDATA[SELECT ...
FROM RESOURCE_SUBOBJECT
WHERE TENANT_ID = ?
  AND PARENT_ID = ?
  AND MAPPING_PATH = ?
  AND ID > ?
ORDER BY ID
FETCH FIRST ? ROWS ONLY]]></sourcecode>
<t>The final bind is the effective page size plus one.</t>
</section>
<section anchor="reference-derived-attributes-2"><name>Reference-Derived Attributes</name>
<t>A reference-derived attribute can page the relationship at its source:</t>
<sourcecode type="sql"><![CDATA[SELECT ...
FROM GRANT_RESOURCE
WHERE TENANT_ID = ?
  AND APP_ID = ?
  AND ID > ?
ORDER BY ID
FETCH FIRST ? ROWS ONLY]]></sourcecode>
<t>Only the returned page is hydrated into the parent attribute. Implementations SHOULD avoid N+1 display-value queries and unbounded identifier lists.</t>
</section>
<section anchor="work-budgets"><name>Work Budgets</name>
<t>Deferred collection retrieval changes one large response into optional follow-up calls. A malicious or poorly designed client can still fan out across many parents and attributes. Providers SHOULD enforce:</t>
<ul><li><t>Maximum attribute page size.</t></li><li><t>Maximum response bytes and serialized values.</t></li><li><t>Query-time and hydration-time budgets.</t></li><li><t>Per-client and per-tenant request-rate limits.</t></li><li><t>Concurrency limits for expensive protected attributes.</t></li><li><t>Cursor issuance and lifetime limits.</t></li><li><t>Maximum root-resource page size.</t></li><li><t>Maximum deferred descriptors per response.</t></li></ul>
<t>For complete extraction of very large datasets, providers SHOULD offer an asynchronous export or bulk mechanism rather than encouraging millions of synchronous cursor requests.</t>
</section>
<section anchor="observability"><name>Observability</name>
<t>Providers SHOULD distinguish at least:</t>
<ul><li><t>deferred collection responses;</t></li><li><t>first attribute pages;</t></li><li><t>continuation pages;</t></li><li><t>terminal pages;</t></li><li><t>invalid or expired cursors;</t></li><li><t>effective page size and returned value count;</t></li><li><t>time spent discovering values versus hydrating them;</t></li><li><t>response bytes and budget-driven early page termination.</t></li></ul>
<t>Metrics MUST NOT use tenant identifiers, parent identifiers, cursor contents, attribute values, or other unbounded/high-cardinality dimensions.</t>
</section>
</section>
  </back>
</rfc>
