> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corbado.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Benchmarking Passkey Implementations

> A five-step method for grading a passkey implementation against product-neutral acceptance criteria and rating its adoption potential.

export const AdoptionPillars = () => {
  const pillars = [{
    x: 8,
    name: "Readiness",
    q: "Can it work here?"
  }, {
    x: 158,
    name: "Enrollment",
    q: "Do users get one?"
  }, {
    x: 308,
    name: "Availability",
    q: "Can they reach it?"
  }, {
    x: 458,
    name: "Usage",
    q: "Do they use it?"
  }];
  return <svg viewBox="0 0 594 88" role="img" aria-label="The four adoption pillars in order: readiness, enrollment, availability, usage" style={{
    width: "100%",
    maxWidth: "594px",
    height: "auto",
    margin: "0 auto",
    display: "block",
    color: "inherit"
  }}>
            <title>The four adoption pillars</title>

            <defs>
                <marker id="pkPillarArrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" opacity="0.45" />
                </marker>
            </defs>

            {pillars.map((p, i) => <g key={p.name}>
                    <rect x={p.x} y="12" width="128" height="64" rx="11" fill="currentColor" fillOpacity="0.045" stroke="currentColor" strokeOpacity="0.22" />
                    <text x={p.x + 14} y="40" fontSize="13.5" fontWeight="600" fill="currentColor">
                        {p.name}
                    </text>
                    <text x={p.x + 14} y="60" fontSize="10.5" fill="currentColor" fillOpacity="0.65">
                        {p.q}
                    </text>
                    {i < pillars.length - 1 && <line x1={p.x + 132} y1="44" x2={p.x + 154} y2="44" stroke="currentColor" strokeOpacity="0.45" strokeWidth="1.5" markerEnd="url(#pkPillarArrow)" />}
                </g>)}
        </svg>;
};

export const BenchmarkRatingLadder = () => {
  const rungs = [{
    x: 8,
    step: "1",
    title: ["Scope"],
    values: ["What you", "reviewed"],
    mono: false,
    dashed: true
  }, {
    x: 126,
    step: "2",
    title: ["Criterion"],
    values: ["Pass", "Fail", "N/A", "Not reviewed"]
  }, {
    x: 244,
    step: "3",
    title: ["Flow"],
    values: ["Full", "Core met", "Not met", "N/A"]
  }, {
    x: 362,
    step: "4",
    title: ["Pillar"],
    values: ["Basic", "Managed", "Optimized"]
  }, {
    x: 480,
    step: "5",
    title: ["Adoption", "potential"],
    values: ["Low", "Mid", "High", "Incomplete"]
  }];
  return <svg viewBox="0 0 594 170" role="img" aria-label="Benchmark method: define scope, then criterion results roll up to a flow rating, then to a pillar level, then to overall adoption potential" style={{
    width: "100%",
    maxWidth: "594px",
    height: "auto",
    margin: "0 auto",
    display: "block",
    color: "inherit"
  }}>
            <title>How the benchmark ratings roll up</title>

            <defs>
                <marker id="pkLadderArrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" opacity="0.45" />
                </marker>
            </defs>

            {rungs.map((r, i) => <g key={r.step}>
                    <rect x={r.x} y="12" width="104" height="144" rx="11" fill="currentColor" fillOpacity="0.045" stroke="currentColor" strokeOpacity="0.22" strokeDasharray={r.dashed ? "6 4" : undefined} />
                    <text x={r.x + 12} y="34" fontSize="10" fontWeight="700" className="pk-accent-text" letterSpacing="0.08em">
                        {"STEP " + r.step}
                    </text>
                    {r.title.map((t, k) => <text key={t} x={r.x + 12} y={54 + k * 16} fontSize="13.5" fontWeight="600" fill="currentColor">
                            {t}
                        </text>)}
                    {r.values.map((v, j) => <text key={v} x={r.x + 12} y={82 + (r.title.length - 1) * 16 + j * 14} fontSize="10.5" fontFamily={r.mono === false ? undefined : "ui-monospace, SFMono-Regular, Menlo, monospace"} fill="currentColor" fillOpacity="0.6">
                            {v}
                        </text>)}
                    {i < rungs.length - 1 && <line x1={r.x + 108} y1="84" x2={r.x + 122} y2="84" stroke="currentColor" strokeOpacity="0.45" strokeWidth="1.5" markerEnd="url(#pkLadderArrow)" />}
                </g>)}
        </svg>;
};

This method turns the [flow catalogue](/passkey-ui-flows/flow-catalogue) into product-neutral requirements you can use for implementation reviews, tickets and provider comparisons.

You declare a scope first, then ratings roll up through four stages. Every table on this page belongs to one of these five steps:

<BenchmarkRatingLadder />

## Step 1. Define your scope

Before giving an implementation a rating, state what you reviewed:

* The website or app
* User and account groups
* Browsers, operating systems and credential managers
* Countries and time period
* **Anything excluded or not tested**

When comparing providers, review them under the same conditions. If a provider does not support a relevant platform or flow, show this as a gap. Use `N/A` only when a requirement does not apply to the reviewed implementation.

Also state whether each capability is **built in**, **configurable**, **requires integration work** or **requires custom development**. This separates the quality of the result from the effort needed to achieve it.

## Step 2. Grade each criterion

Every flow page ends with its acceptance criteria, each carrying an ID such as `W1.1-AC03`. Grade them one by one.

Test important platforms separately. A feature that works on Chrome but fails on Safari is one pass and one fail, not one combined result.

| Result           | Use when                                                        |
| ---------------- | --------------------------------------------------------------- |
| **Pass**         | The implementation meets the criterion.                         |
| **Fail**         | The criterion applies, but the implementation does not meet it. |
| **N/A**          | The criterion does not apply. State why.                        |
| **Not reviewed** | There is not enough evidence to give a result.                  |

Criteria come in two levels. **Core** covers behavior that must work: security, account binding, completion, cancellation and fallback. **Quality** covers measurement, testing and maintainability.

Use a simple review table:

| Criterion   | Platform                       | Result                           | Evidence                              | Notes            |
| ----------- | ------------------------------ | -------------------------------- | ------------------------------------- | ---------------- |
| `W1.2-AC01` | Safari 18 with Apple Passwords | Pass / Fail / N/A / Not reviewed | Test, documentation or analytics link | Result or reason |

Also record how strong the evidence is:

| Confidence  | Evidence                                                                      |
| ----------- | ----------------------------------------------------------------------------- |
| **High**    | Current tests across the supported platforms, preferably with production data |
| **Medium**  | Direct tests exist, but some platforms or user groups are missing             |
| **Low**     | Documentation, a demo, screenshot or narrow happy-path test                   |
| **Unknown** | No evidence is available                                                      |

Two pieces of flow metadata decide how a criterion is judged. **Benchmark as** sets how strictly the flow is required, and **User reach** describes how much of the user base it touches. Both are listed for every flow in the [availability matrix](/passkey-ui-flows/flow-catalogue#availability-matrix).

### Benchmark as

| Value                     | Meaning                                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Baseline**              | Expected production behavior whenever passkeys are in scope.                                                       |
| **Strategy**              | One primary UX pattern that can coexist with or substitute for another strategy. Do not require every alternative. |
| **Conditional**           | Required only when its documented scenario occurs in the declared scope.                                           |
| **Enhancement**           | Progressive capability that improves the flow where supported; unsupported targets are not penalized.              |
| **Supporting capability** | Non-UI behavior that improves several flows.                                                                       |

### User reach

| Value    | Meaning                                                                                           |
| -------- | ------------------------------------------------------------------------------------------------- |
| **High** | Directly affects a frequent, broad creation or returning-login opportunity.                       |
| **Mid**  | Expands device coverage, recovery or another important part of the user base.                     |
| **Low**  | Primarily improves lifecycle hygiene, trust or administration rather than direct funnel exposure. |

This is a qualitative estimate for the full audience. It is **not** a measure of overall importance, it does not affect whether a criterion passes and the labels must never be added together as a score. Credential management and Signal API reconciliation have low user reach but high reliability and lifecycle value.

### Availability

| Value            | Meaning                                                                                         |
| ---------------- | ----------------------------------------------------------------------------------------------- |
| **Universal**    | Works on every modern browser or on iOS 16+ / Android 9+ with a supporting credential provider. |
| **Limited**      | Depends on recent browser, OS or provider versions. Feature-detect and degrade gracefully.      |
| **Desktop only** | Applies to the desktop context by definition.                                                   |

Exact version requirements are listed on each flow page and in the [availability matrix](/passkey-ui-flows/flow-catalogue#availability-matrix).

## Step 3. Roll up to a flow rating

Step 2 grades one criterion at a time. A flow has around ten of them, so step 3 turns those results into a single verdict per reviewed platform. That verdict, not the individual criteria, is what the pillars in step 4 consume.

The roll-up is not simply "all passed". It separates **Core** from **Quality**: a flow whose Core criteria all pass but which fails one Quality criterion is **Core met**, which is a usable, secure flow with a measurement or maintainability gap. That is a different finding from **Not met**, where something a user depends on is broken.

| Flow rating      | Meaning                                                                             |
| ---------------- | ----------------------------------------------------------------------------------- |
| **Full**         | All applicable Core and Quality criteria pass.                                      |
| **Core met**     | All Core criteria pass, but one or more Quality criteria fail or were not reviewed. |
| **Not met**      | At least one Core criterion fails.                                                  |
| **Not reviewed** | One or more Core criteria were not reviewed and no Core criterion is known to fail. |
| **N/A**          | The complete flow does not apply to the reviewed implementation.                    |

## Step 4. Score the four pillars

Do not rate adoption by counting passed criteria. A login strategy, an optional enhancement and a management feature have different purposes. Instead, review four separate areas:

<AdoptionPillars />

| Pillar           | Question                                                                                                        |
| ---------------- | --------------------------------------------------------------------------------------------------------------- |
| **Readiness**    | Does the declared audience and implementation support the required capability?                                  |
| **Enrollment**   | Are eligible users exposed to and able to complete passkey creation?                                            |
| **Availability** | Can an enrolled credential be reached in the returning context, including cross-device and second-device cases? |
| **Usage**        | When a usable passkey is available, does the entry design lead users to initiate and complete passkey login?    |

A **primary** flow provides the main result for a pillar. A **secondary** flow improves or protects that result but cannot satisfy the pillar by itself.

| Pillar           | Primary flow IDs                             | Secondary flow IDs                                   |
| ---------------- | -------------------------------------------- | ---------------------------------------------------- |
| **Readiness**    | `E1`                                         | `W1.6`, `W2.1`, `N1.2`, `N2.6`                       |
| **Enrollment**   | `W2.1`–`W2.7`, `W3.1`, `N2.1`–`N2.6`, `N3.1` | `W2.8`, `E1`                                         |
| **Availability** | `W1.4`, `W2.4`–`W2.6`, `N2.3`                | `W1.5`, `W3.1`, `W3.2`, `N1.4`, `N3.1`, `N3.2`, `E1` |
| **Usage**        | `W1.1`, `W1.3`, `N1.1`, `N1.3`               | `W1.2`, `W1.4`–`W1.6`, `N1.2`, `N1.4`, `E1`          |

Give each pillar the highest level it **fully** meets:

| Pillar           | Basic                                                                    | Managed                                                                               | Optimized                                                             |
| ---------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| **Readiness**    | The main included platforms support the selected passkey flows.          | Readiness checks and the support matrix are complete.                                 | Relevant readiness enhancements work across the included user groups. |
| **Enrollment**   | At least one passkey-creation route works.                               | Account management and a broad creation strategy work for each included account type. | Relevant automatic, conditional and recovery creation flows work.     |
| **Availability** | Returning login, fallback and passkey management work.                   | Relevant cross-device, second-device and recovery flows work.                         | Credential-manager reconciliation works where supported.              |
| **Usage**        | At least one primary passkey-login strategy and its error handling work. | The selected primary strategy is Full and its usage can be measured.                  | Readiness and relevant discovery enhancements also work.              |

Rate each included website or app separately. If an implementation uses a different flow to achieve the same result, assess it against the same acceptance criteria. If a pillar does not reach Basic or was not reviewed, the overall result is **Incomplete**.

## Step 5. Determine adoption potential

This is the final result of the review. It rates the whole implementation, where **User reach** in the catalogue rates a single flow.

Do not average the pillars. Apply these rules:

| Adoption potential | Rule                                                                                          |
| ------------------ | --------------------------------------------------------------------------------------------- |
| **Incomplete**     | At least one pillar is below Basic or was not reviewed.                                       |
| **Low**            | Enrollment or Usage remains Basic, so passkey creation or repeat-login discovery is limited.  |
| **Mid**            | Enrollment and Usage are at least Managed, and Readiness and Availability are at least Basic. |
| **High**           | Enrollment and Usage are Optimized, and Readiness and Availability are at least Managed.      |

Determine adoption potential for web and native separately, then take the lowest result across the included applications, ordered `Incomplete < Low < Mid < High`. An application that was not reviewed counts as Incomplete. Always show the individual results and state what was not included.

<Note>
  This rates adoption **potential**, not actual adoption. A live deployment should also report enrollment, passkey login share, success, fallback and error rates for a stated user group and time period.

  For automatic creation, calculate the upgrade rate as successful server registrations divided by conditional-registration requests sent after a completed password sign-in. Do not estimate whether the credential provider considered a request eligible.
</Note>

## Using evidence

Use specifications and official platform documentation for protocol and API behavior. Use cross-platform UX research and named deployments for flow guidance. Corbado blog posts and internal implementation experience help explain feasibility, edge cases and estimated user reach, but proprietary performance figures are not universal pass/fail thresholds.

Each criterion includes a practical check and references that explain why it is included. During a review, add the test result, documentation or analytics used for the rating.
