Skip to content

[Proposal]: Add Provider participant type and offer discovery to dev.ucp.common.identity_linking #384

@vcello

Description

@vcello

Summary

This proposal enhances dev.ucp.common.identity_linking to support a new class of identity
linkage: a buyer establishing a pre-session account relationship with a third-party Provider
(a payment instrument issuer, BNPL provider, loyalty program, rewards network, etc.) before or
at the start of a shopping journey.

Once linked, the Provider can surface shopping-phase benefits — installment pricing, reward
earning rates, loyalty discounts — that meaningfully influence purchase decisions during product
discovery and cart review. The core insight: if linking only happens at checkout, the benefit
arrives after the purchase decision has already been made (or abandoned).

This proposal introduces two additions to dev.ucp.common.identity_linking: (1) Provider
as a fourth UCP participant type alongside Buyer, Platform, and Business; and (2) offer
discovery
— a protocol hook by which a linked Provider publishes the shopping-phase benefits
it can surface for a buyer.

Motivation

The existing dev.ucp.common.identity_linking capability covers session-scoped identity linking
between a Buyer and a Business — the Platform establishing who the Buyer is within an active
commerce session. This is well-suited for the checkout phase.

But many Providers have valuable signal and benefits that are only useful before the purchase
decision is made:

Context Benefit Why checkout is too late
BNPL provider "As low as $25/mo" on product listings; pre-approved purchasing power Buyer evaluates affordability during browsing, not after selecting "pay with BNPL"
Airline card "Earn 3x miles on this United flight" vs. "1x on American" Buyer chooses between flights during search, not at payment
Cash-back card "2% back at this merchant; 1% at others" Buyer decides where to shop during discovery, not at checkout
Store card "10% off with your Macy's card — net price matches Nordstrom" Buyer compares prices during product discovery

In each case, the Platform needs the linked account credential before the shopping journey
begins to surface the Provider's benefit at the moment it can actually influence the Buyer's
decision.

Goals

  • Define Provider as a formal fourth UCP participant type with a clear, stable definition
  • Introduce offer discovery as a standardized protocol hook for Providers to declare
    shopping-phase benefit endpoints, enabling Platforms to integrate uniformly across Providers
    without per-Provider custom logic
  • Enable pre-session account linking via a providers array in the platform's
    dev.ucp.common.identity_linking capability config
  • Remain additive — no breaking changes to existing dev.ucp.common.identity_linking behavior

Non-Goals

  • Defining any specific Provider's response schema (each Provider extends the offer_discovery
    hook with their own schema — e.g., BNPL installment pricing, airline miles rates)
  • Payment tokenization or checkout credential issuance (those are Provider-specific extensions)
  • Mandating that Platforms support any specific Provider type
  • Defining the out-of-band Provider registration process (how a Platform obtains its client_id)

Detailed Design

New UCP Participant: Provider

A Provider is an entity with which a Buyer holds a pre-existing account relationship, whose
participation in a commerce flow can deliver measurable benefit to the Buyer — through
preferential pricing, credit, rewards, or loyalty value — and whose ability to deliver that
benefit depends on being identified earlier in the shopping journey than checkout.

A Provider is distinct from a Business: a Business is relevant to one transaction at one
merchant, while a Provider's relationship is with the Buyer across all merchants.

A Provider is also distinct from a Platform: a Platform acts as the Buyer's agent, executing
the Buyer's intent; a Provider holds an account relationship with the Buyer that the Buyer
brings into the shopping session.

API / Schema Changes

Discovery profile (static configuration)

The enhancement adds a providers array to the existing dev.ucp.common.identity_linking
capability configuration in the platform's UCP discovery profile. Each entry is a
provider_linking_config — one per Provider the platform has registered with:

{
  "provider_id": "com.affirm",
  "provider_type": "bnpl",
  "oauth_config": {
    "authorization_endpoint": "https://auth.affirm.com/oauth2/authorize",
    "token_endpoint": "https://auth.affirm.com/oauth2/token",
    "scopes": ["bnpl:scopes:account_linking"],
    "client_id": "plat_abc123",
    "code_challenge_methods_supported": ["S256"],
    "revocation_endpoint": "https://auth.affirm.com/oauth2/revoke"
  },
  "offer_discovery": {
    "endpoint": "https://api.affirm.com/api/v2/ucp/installment-pricing",
    "supported_contexts": ["product_discovery", "cart"]
  }
}

Key fields:

  • oauth_config — the OAuth 2.0 endpoints and scopes the Platform uses to initiate the
    linking flow. PKCE S256 is required (RFC 7636).
  • offer_discovery — declares the Provider's benefit endpoint and which UCP shopping
    contexts it applies to (product_discovery, cart, checkout).
  • provider_type — informational classification (bnpl, payment_instrument,
    loyalty_program, store_card, rewards_network); not used for protocol routing.

Runtime credential

After the buyer completes the OAuth flow, the Provider issues an account_credential — a
standard RFC 6749 Bearer token response. This is not part of the discovery profile; it is
produced at runtime and stored by the Platform, keyed by provider_id:

{
  "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "scope": "bnpl:scopes:account_linking",
  "expires_in": 900,
  "refresh_token": "rt_xyz...",
  "constraints": {
    "business_allowlist": [],
    "subject": "usr_7f3a9b2c...",
    "issued_at": "2026-04-24T10:00:00Z"
  }
}

The Platform presents this credential when calling the Provider's offer_discovery endpoint
and, where applicable, payment or underwriting flows. Notable:

  • constraints.business_allowlist — Provider-set restriction to specific merchants;
    absent or empty means valid at all merchants.
  • refresh_token — Providers SHOULD issue one; a buyer who links at the start of a session
    may reach checkout with an expired access token.

Full schema: to be added in accompanying PR.

Behavioral Changes

No existing behavior changes. The providers array is additive to the existing
dev.ucp.common.identity_linking config. Platforms that do not declare any providers behave
identically to today.

Risks and Mitigations

Security — SSRF via offer_discovery endpoint
Platforms register Provider endpoints in their UCP profile. Providers MUST validate incoming
requests against known platform registrations to prevent injection of malicious endpoints.
Mitigation: offer_discovery endpoints are Provider-declared (not Platform-declared), so the
Provider controls what URL is invoked.

Security — authorization code interception
Mitigation: PKCE S256 is required for all authorization requests; the plain method is
explicitly not permitted. Providers MUST enforce PKCE server-side.

Security — credential replay / scope escalation
Mitigation: account_credential carries a constraints object (including optional
business_allowlist) that Providers enforce at the API level. Platforms MUST NOT present
credentials outside their declared constraints.

Backward compatibility
This is a purely additive change — the providers array is new and optional. No existing
dev.ucp.common.identity_linking fields or behaviors are modified. Existing platforms and
businesses are unaffected.

Complexity — Provider participant introduces a new integration surface
Mitigation: The protocol standardizes only the hook (account linking + offer discovery
invocation). The Provider's response format is entirely their own to define. Platforms
integrating a new Provider only need to handle the provider_linking_config structure — the
rest is Provider-specific.

Test Plan

Unit tests

  • provider_linking_config schema validation (required fields, PKCE constraint enforcement,
    URI formats)
  • account_credential constraint parsing (business_allowlist empty vs. absent vs. populated)
  • offer_discovery_config context enum validation

Integration tests

  • Full OAuth Authorization Code + PKCE S256 flow from Platform to Provider
  • account_credential presented to offer_discovery endpoint; valid vs. expired vs.
    wrong-business responses
  • Token refresh (silent re-auth when access token expires mid-session)
  • Token revocation on buyer-initiated unlink

End-to-end tests

  • Buyer links Provider at session start; Platform queries offer_discovery in
    product_discovery context; benefit surfaced in product listing
  • Buyer links Provider; credential has non-empty business_allowlist; Platform correctly
    withholds credential at unlisted merchant
  • Buyer unlinks Provider; Platform calls revocation endpoint; subsequent offer_discovery
    calls rejected

Graduation Criteria

Working Draft → Candidate:

  • Schema merged and documented (with Working Draft disclaimer)
  • Unit and integration tests passing
  • Initial documentation written (extension of docs/specification/identity-linking.md)
  • At least one reference implementation (Affirm BNPL extensions available as reference)
  • TC majority vote to advance

Candidate → Stable:

  • Adoption feedback from at least one non-Affirm Provider collected and addressed
  • Full documentation and migration guides published
  • TC majority vote to advance

Implementation History

  • 2026-04-24: Proposal submitted.
  • [YYYY-MM-DD]: TC approved "Provisional"; capability enters "Working Draft".
  • [YYYY-MM-DD]: TC approved advancement to "Candidate".
  • [YYYY-MM-DD]: TC approved "Implemented"; capability enters "Stable".

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions