worldcoin / worldcoin/idkit

Browser v4 uniqueness flow: constraints(proof_of_human) emits mixed-mode bridge payload and falls back to legacy / verification_rejected

Open
#204 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
9
Forks
6
Avg merge
1d 3h
Merged PRs (30d)
12

Description

Summary

We are integrating World ID 4.0 for an on-chain uniqueness-gated flow and hit two related problems in the browser path:

  1. @worldcoin/idkit-core@4.1.0 with IDKit.request(...).constraints(CredentialRequest('proof_of_human')) emitted a mixed-mode bridge request that included a top-level verification_level: "device", and the World App completed with a legacy protocol_version: "3.0" result.
  2. After bypassing the SDK request builder and sending a pure v4 bridge payload ourselves, the request no longer returned a legacy proof, but the World App/bridge completed with verification_rejected.

At this point we are no longer trying to prove that our app works; we have a minimal repro that seems to isolate the request semantics.

Environment

  • @worldcoin/idkit-core: 4.1.0
  • Browser integration, not React
  • Production environment
  • RP is registered for World ID 4.0 in the Developer Portal
  • app_id: app_969d38c8ba9adf22a4ac3b2d4400575a
  • rp_id: rp_6765160973bf7042

Repro 1: official SDK browser flow

Code shape:

const request = await IDKit.request({
  app_id,
  action: "create-auction",
  rp_context,
  allow_legacy_proofs: false,
  environment: "production",
}).constraints(
  CredentialRequest("proof_of_human")
)

const result = await request.pollUntilCompletion()
What we observed

Even though allow_legacy_proofs was false, the raw result was:

{
  "success": true,
  "result": {
    "action": "create-auction",
    "environment": "production",
    "nonce": "0x...",
    "protocol_version": "3.0",
    "responses": [
      {
        "identifier": "orb",
        "merkle_root": "0x...",
        "nullifier": "0x...",
        "proof": "0x...",
        "signal_hash": "0x00c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4"
      }
    ]
  }
}
What we found by instrumenting the SDK request

We captured the browser POST to https://bridge.worldcoin.org/request, decrypted the payload, and found that the SDK-generated request was not pure v4. It included a top-level legacy-looking field:

{
  "allow_legacy_proofs": false,
  "proof_request": {
    "proof_requests": [
      {
        "identifier": "proof_of_human",
        "issuer_schema_id": 1,
        "genesis_issued_at_min": 1775260800
      }
    ],
    "rp_id": "rp_6765160973bf7042",
    "version": 1
  },
  "signal": "0x00c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4",
  "verification_level": "device"
}

The presence of verification_level: "device" is what led us to suspect the browser request builder was emitting a mixed-mode payload.

Repro 2: pure v4 bridge request (no SDK request builder)

We then bypassed IDKit.request(...).constraints(...) and constructed the bridge request ourselves.

The payload we sent was:

{
  "action": "create-auction",
  "allow_legacy_proofs": false,
  "app_id": "app_969d38c8ba9adf22a4ac3b2d4400575a",
  "environment": "production",
  "proof_request": {
    "action": "0x00c13e38e42d2818b1b66349184c1ad52cce0d9f2d6e61b58264e3c47f592270",
    "created_at": 1775338401,
    "expires_at": 1775338701,
    "id": "82ef1792-c31c-47cb-a4d8-3c0fd97ab30b",
    "nonce": "0x0045e7472500630a9751a115500e9bd0b5fb5201e79bd19cd363c018c910bb10",
    "oprf_key_id": "0x6765160973bf7042",
    "proof_requests": [
      {
        "identifier": "proof_of_human",
        "issuer_schema_id": 1,
        "genesis_issued_at_min": null,
        "expires_at_min": null
      }
    ],
    "rp_id": "rp_6765160973bf7042",
    "session_id": null,
    "signature": "<redacted>",
    "version": 1
  }
}

Notes:

  • We omitted signal entirely in this repro to keep the request as simple as possible.
  • We also omitted genesis_issued_at_min to avoid unnecessarily excluding older credentials.
  • We did not include verification_level.

Bridge request ID for this clean repro:

  • 2bf813bf-43ec-4985-b6a6-7ab7efc25700
Result

This no longer returned a legacy 3.0 proof. Instead it completed with:

{
  "success": false,
  "error": "verification_rejected",
  "rawResult": {
    "error_code": "verification_rejected"
  }
}

So after removing the mixed-mode request shape, the behavior changed from “legacy proof returned” to “request rejected”.

Questions

  1. Is verification_level: "device" expected in the browser bridge payload for a v4 uniqueness request built from:
    • allow_legacy_proofs: false
    • CredentialRequest("proof_of_human")
      ?
  2. If not, is this a bug in the browser request builder / wasm bridge serialization for @worldcoin/idkit-core@4.1.0?
  3. For a pure v4 browser request, is the payload shape above correct, especially:
    • top-level action
    • proof_request.action = hashSignal(action)
    • proof_requests: [{ identifier: "proof_of_human", issuer_schema_id: 1 }]
    • omission of signal when no signal is intended
  4. What conditions cause verification_rejected at this stage for a pure v4 uniqueness request in production?

Why we are filing this here

The first issue appears squarely in IDKit itself: the browser request builder emitted a bridge payload that mixed v4 proof_request with a legacy-looking top-level verification_level, and that correlated with a legacy 3.0 result.

We can share more implementation details if useful, but the payloads and request IDs above should be enough to inspect bridge-side behavior.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the browser request builder and wasm bridge serialization in @worldcoin/idkit-core@4.1.0, then reproduce both payloads from the issue and compare their bridge responses. Check the captured request IDs and the SDK-generated verification_level field first. Done means the expected pure v4 request shape and the cause of verification_rejected are established, with a regression test or documented behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, wasm
Domain
api, developer-experience
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.