# DomainLinkage and allowed domains

This page describes how the Verifier applies **W3C Domain Linkage** checks and the optional **issuer allow-list** (`issuerAllowedDomains`) during VP/VC validation.

***

## What problem this solves

* **Bind issuer DID → web origin**: Proves that an issuer controls a given HTTPS origin via W3C **Domain Linkage Credentials** (DLC) published at `/.well-known/did-configuration.json`.
* **Restrict acceptable issuers**: Lets verifiers **allow-list** issuer websites via `issuerAllowedDomains` to enforce business or compliance policies.

***

## When it runs

During **VP verification**:

1. Validate VP holder DID and VP proof.
2. For each embedded VC:
   * Validate VC proof and issuer DID Document.
   * If issuer advertises **LinkedDomains**, verify Domain Linkage for each advertised **origin**.
   * If `issuerAllowedDomains` present, enforce allow-list by **origin** intersection.
   * Check revocation/expiry/etc.

> If any VC fails its linkage or allow-list check, the VP is rejected per policy.

***

## Inputs

### Configuration surface

You can provide the allow-list at **request time** or via a **stored VP Query**:

* **Create Authorization Request** (`CreateAuthorizationRequestDto`)
  * `issuerAllowedDomains?: string[]`
* **Create VP Query** (`CreateVpQueryRequestDto`)
  * `issuerAllowedDomains?: string[]`

**Format**: canonical HTTPS URLs

* Must include `https://`
* Optional `:port` and `/path` (queries/fragments are not allowed)
* Examples: `https://issuer.example.com`, `https://issuer.example.com:8443`, `https://issuer.example.com/trusted`

**Normalization** (runtime):

* Host is case-folded
* Trailing slashes trimmed
* Duplicates removed
* **Matching is by `URL.origin`** (paths accepted in config but ignored for linkage matching per spec)

If omitted or empty → **no issuer-domain restriction** (backward-compatible).

***

## Domain Linkage (W3C) — how it works

1. **Discover linked origins**: From issuer DID Document services (e.g., `LinkedDomains`), the resolver extracts HTTPS **origins**.
2. **Fetch configuration**: For each origin, download `origin + "/.well-known/did-configuration.json"`.
3. **Validate DLCs**:
   * Check `type` includes `DomainLinkageCredential`
   * Subject `{ id: <issuer DID>, origin: <that origin> }`
   * Issuer equals the same DID
   * Proof verifies against one of the DID’s **assertionMethod** keys
   * Enforce safe limits (payload sizes, max entries, timeout)
4. **Accept origin** if any DLC for that origin validates.

> This proves control of the website by the issuer DID.

***

## Allow-list enforcement (`issuerAllowedDomains`)

* Build `allowedOrigins = unique(origin(url) for url in issuerAllowedDomains)`.
* For each VC issuer:
  * Compute `linkedOrigins` (validated via Domain Linkage).
    * If issuer has **no LinkedDomains**, `linkedOrigins` may be empty.
  * **Decision**:
    * If allow-list is **not** provided → skip restriction.
    * If allow-list **is** provided:
      * If issuer advertises LinkedDomains → require `intersection(allowedOrigins, linkedOrigins) ≠ ∅`.
      * If issuer does **not** advertise LinkedDomains:
        * **did:web**: treat the DID’s web **origin** as the issuer origin and compare to `allowedOrigins`.
        * Other methods (e.g., `did:empe`) with no LinkedDomains → **reject** (cannot bind DID to a web origin) unless your policy explicitly allows such issuers without origins.

> Paths in `issuerAllowedDomains` are accepted for configuration convenience but **do not** participate in the origin match.

***

## did:web specifics

* **Domain Linkage** is not required for `did:web` because the DID is inherently bound to a domain.
* For allow-list:
  * Extract the **origin** from the DID URL (e.g., `did:web:example.com` → `https://example.com`).
  * Compare against `allowedOrigins`. If no match → reject.

***

## Practical examples

**1) Stored VP Query with allow-list**

```json
{
  "query": [ { "fields": [ { "path": ["$.type"], "filter": { "type": "array", "contains": { "const": "IdentityCredential" } } } ] } ],
  "issuerAllowedDomains": [
    "https://issuer.example.com",
    "https://partners.example.org:8443/trusted"
  ]
}
```

* During verification:
  * Issuer’s LinkedDomains are DLC-validated.
  * Must match `https://issuer.example.com` or `https://partners.example.org:8443` by **origin**.

**2) did:web issuer**

* `did:web:university.edu`
* No LinkedDomains needed. Verify VC; then ensure origin `https://university.edu` is in `issuerAllowedDomains` (if provided).

**3) did:empe issuer without LinkedDomains**

* If `issuerAllowedDomains` is set → **reject** (no origin to compare), unless you disable the policy or the issuer adds LinkedDomains+DLC.

***
