From 414d0d454fd2ec1043ffe331703a70c2785bf443 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:49:28 +0300 Subject: [PATCH 1/5] feat: add RP signatures page, nullifier guidance, and SDK reference updates - New `signatures.mdx` page with pseudocode algorithm, JS/Go SDK examples, and cross-language test vectors - Add Step 6 to integration guide: nullifier explanation and PostgreSQL storage guidance - Fix `signRequest` API calls to use v4 options object across integrate and JS reference - Add `return_to` parameter to SDK code examples (JS, Swift, Kotlin) - Expand Go SDK reference with `NewSigner`, `WithAction`/`WithTTL` options - Add signatures page to docs.json navigation Co-Authored-By: Claude Opus 4.6 (1M context) --- cspell.json | 3 + docs.json | 1 + world-id/idkit/go.mdx | 55 ++++++++- world-id/idkit/integrate.mdx | 54 +++++++-- world-id/idkit/javascript.mdx | 17 +-- world-id/idkit/kotlin.mdx | 4 +- world-id/idkit/react.mdx | 2 + world-id/idkit/signatures.mdx | 210 ++++++++++++++++++++++++++++++++++ world-id/idkit/swift.mdx | 4 +- 9 files changed, 323 insertions(+), 27 deletions(-) create mode 100644 world-id/idkit/signatures.mdx diff --git a/cspell.json b/cspell.json index edb3392..3d8167a 100644 --- a/cspell.json +++ b/cspell.json @@ -66,6 +66,7 @@ "johndoe", "keccak", "keypair", + "nullifiers", "kotlinx", "Lazo", "listas", @@ -149,6 +150,7 @@ "sybil", "SYNCMODE", "tamperable", + "TIMESTAMPTZ", "testnet", "testnets", "thirdweb", @@ -156,6 +158,7 @@ "tunnelmole", "tute", "uniffi", + "unlinkable", "unpackedProof", "urlencode", "userop", diff --git a/docs.json b/docs.json index 3b05f4a..1ced9a5 100644 --- a/docs.json +++ b/docs.json @@ -49,6 +49,7 @@ "group": "IDKit", "pages": [ "world-id/idkit/integrate", + "world-id/idkit/signatures", "world-id/idkit/build-with-llms", "world-id/idkit/onchain-verification", "world-id/idkit/design-guidelines", diff --git a/world-id/idkit/go.mdx b/world-id/idkit/go.mdx index 46f2c7f..aa242b7 100644 --- a/world-id/idkit/go.mdx +++ b/world-id/idkit/go.mdx @@ -16,12 +16,18 @@ go get github.com/worldcoin/idkit/go/idkit@latest ## Generate RP signature -Use `idkit.SignRequestWithTTL` instead to configure the signature's expiration time (default is 5 minutes). +### One-shot signing + +Use `SignRequest` with functional options for the simplest integration: ```go import "github.com/worldcoin/idkit/go/idkit" -sig, err := idkit.SignRequest(os.Getenv("RP_SIGNING_KEY")) +// For uniqueness proofs: include the action +sig, err := idkit.SignRequest( + os.Getenv("RP_SIGNING_KEY"), + idkit.WithAction("my-action"), +) if err != nil { // handle error } @@ -35,11 +41,52 @@ rpContext := map[string]any{ } ``` +### Reusable signer + +For high-throughput backends, create a `Signer` once and reuse it. This parses the key upfront and avoids repeated allocations. + +```go +signer, err := idkit.NewSigner(os.Getenv("RP_SIGNING_KEY")) +if err != nil { + log.Fatal(err) +} + +// Use in your request handler +sig, err := signer.SignRequest( + idkit.WithAction("my-action"), + idkit.WithTTL(600), // optional, default 300s +) +``` + ## API -- `SignRequest(signingKeyHex)` -- `SignRequestWithTTL(signingKeyHex, ttl)` +### Functions + +| Function | Description | +|----------|-------------| +| `SignRequest(signingKeyHex, opts...)` | One-shot signing with options | +| `SignRequestWithTTL(signingKeyHex, ttl)` | Convenience wrapper with custom TTL | +| `NewSigner(signingKeyHex)` | Creates a reusable `Signer` from a hex key | + +### Options + +| Option | Description | +|--------|-------------| +| `WithAction(action)` | Hashes and appends the action to the signed payload (required for uniqueness proofs) | +| `WithTTL(ttl)` | Overrides the default 300-second TTL | + +### `RpSignature` + +```go +type RpSignature struct { + Sig string `json:"sig"` // 0x-prefixed, 65-byte hex + Nonce string `json:"nonce"` // 0x-prefixed, 32-byte field element + CreatedAt uint64 `json:"created_at"` // Unix seconds + ExpiresAt uint64 `json:"expires_at"` // Unix seconds +} +``` ## Related pages +- [RP Signatures](/world-id/idkit/signatures) — algorithm details, pseudocode, and test vectors - [Integrate IDKit](/world-id/idkit/integrate) diff --git a/world-id/idkit/integrate.mdx b/world-id/idkit/integrate.mdx index 1d7a2c8..add7976 100644 --- a/world-id/idkit/integrate.mdx +++ b/world-id/idkit/integrate.mdx @@ -47,13 +47,15 @@ Signatures verify that proof requests genuinely come from your app, preventing a ```typescript title="JavaScript" import { NextResponse } from "next/server"; -import { signRequest } from "@worldcoin/idkit/signing"; +import { signRequest } from "@worldcoin/idkit-core/signing"; export async function POST(request: Request): Promise { const { action } = await request.json(); - const signingKey = process.env.RP_SIGNING_KEY!; - const { sig, nonce, createdAt, expiresAt } = signRequest(action, signingKey); + const { sig, nonce, createdAt, expiresAt } = signRequest({ + signingKeyHex: process.env.RP_SIGNING_KEY!, + action, + }); return NextResponse.json({ sig, @@ -81,7 +83,15 @@ func handleRPSignature(w http.ResponseWriter, r *http.Request) { return } - sig, err := idkit.SignRequest(os.Getenv("RP_SIGNING_KEY")) + var body struct { + Action string `json:"action"` + } + _ = json.NewDecoder(r.Body).Decode(&body) + + sig, err := idkit.SignRequest( + os.Getenv("RP_SIGNING_KEY"), + idkit.WithAction(body.Action), + ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -105,7 +115,7 @@ func handleRPSignature(w http.ResponseWriter, r *http.Request) { # Step 4: Generate the connect URL and collect proof -You can test during development using the [simulator](https://simulator.worldcoin.org/) and setting `environment` to `"staging"`. +You can test during development using the [simulator](https://simulator.worldcoin.org/) and setting `environment` to `"staging"`. ```typescript title="JavaScript" @@ -122,7 +132,7 @@ const request = await IDKit.request({ app_id: "app_xxxxx", // Action: Context that scopes what the user is proving uniqueness for // e.g., "verify-account-2026" or "claim-airdrop-2026". - action: "my-action", + action: "my-action", rp_context: { rp_id: "rp_xxxxx", // Your app's `rp_id` from the Developer Portal nonce: rpSig.nonce, @@ -132,7 +142,8 @@ const request = await IDKit.request({ }, allow_legacy_proofs: true, environment: "production", // Only set this to staging for testing with the simulator - // Signal (optional): Bind specific context into the requested proof. + return_to: "https://example.com/verify-done", // Optional: deep-link callback URL + // Signal (optional): Bind specific context into the requested proof. // Examples: user ID, wallet address. Your backend should enforce the same value. }).preset(orbLegacy({ signal: "local-election-1" })); @@ -300,11 +311,34 @@ export async function POST(request: Request): Promise { }, ); - const payload = await response.json(); - return NextResponse.json(payload, { status: response.status }); + if (!response.ok) { + return NextResponse.json({ error: "Verification failed" }, { status: 400 }); + } + + // Proof is valid — now store the nullifier (see Step 6) + return NextResponse.json({ success: true }); } ``` +# Step 6: Store the nullifier + +Every World ID proof contains a nullifier — a value derived from the user's World ID, your app, and the action. The same person verifying the same action always produces the same nullifier, but different apps or actions produce different ones — making nullifiers unlinkable across apps. + +The Developer Portal confirms the proof is **cryptographically valid**, but your backend must check that the nullifier hasn't been used before. Without this, the same person could verify multiple times for the same action. + +Nullifiers are returned as 0x-prefixed hex strings representing 256-bit integers. We recommend converting and storing them as numbers to avoid parsing and casing issues that can lead to security vulnerabilities. For example, PostgreSQL doesn't natively support 256-bit integers, instead you can convert to the nullifier to a decimal and store it as `NUMERIC(78, 0)`. + + +```sql title="PostgreSQL schema" +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + -- Store the nullifier as a new column to the users table or in a separate table + nullifier NUMERIC(78, 0) NOT NULL, + action TEXT NOT NULL, + verified_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), +); +``` + ## Architecture overview @@ -328,9 +362,11 @@ sequenceDiagram Client->>Backend: 6. Forward proof payload Backend->>Portal: POST /v4/verify/{rp_id} Portal-->>Backend: Verification result + Backend->>Backend: 7. Check & store nullifier Backend-->>Client: Success / failure ``` ## Next pages +- [RP Signatures](/world-id/idkit/signatures) — algorithm details, pseudocode, and test vectors - [POST /v4/verify reference](/api-reference/developer-portal/verify) diff --git a/world-id/idkit/javascript.mdx b/world-id/idkit/javascript.mdx index 2cd9fdd..a2ca159 100644 --- a/world-id/idkit/javascript.mdx +++ b/world-id/idkit/javascript.mdx @@ -46,11 +46,10 @@ const builder = IDKit.request({ expires_at: 1735689900, signature: "0x...", }, - action_description: "Verify user", - bridge_url: undefined, allow_legacy_proofs: true, - override_connect_base_url: undefined, environment: "production", // Only set this to staging for testing with the simulator + return_to: "https://example.com/done", // Optional: deep-link callback URL + bridge_url: undefined, // Optional: custom bridge URL }); ``` @@ -111,19 +110,21 @@ Use subpath exports on your backend: import { signRequest } from "@worldcoin/idkit-core/signing"; import { hashSignal } from "@worldcoin/idkit-core/hashing"; -const { sig, nonce, createdAt, expiresAt } = signRequest( - "my-action", - process.env.RP_SIGNING_KEY!, -); +const { sig, nonce, createdAt, expiresAt } = signRequest({ + signingKeyHex: process.env.RP_SIGNING_KEY!, + action: "my-action", + ttl: 300, // optional, default 300s +}); const signalHash = hashSignal("user-123"); ``` -`signRequest` should only run in trusted server environments. +`signRequest` should only run in trusted server environments. See [RP Signatures](/world-id/idkit/signatures) for the full algorithm and test vectors. ## Related pages - [Getting started](/world-id/idkit/integrate) +- [RP Signatures](/world-id/idkit/signatures) - [React](/world-id/idkit/react) - [Error Codes](/world-id/idkit/error-codes) - [POST /v4/verify reference](/api-reference/developer-portal/verify) diff --git a/world-id/idkit/kotlin.mdx b/world-id/idkit/kotlin.mdx index 5785d50..eb979ad 100644 --- a/world-id/idkit/kotlin.mdx +++ b/world-id/idkit/kotlin.mdx @@ -54,10 +54,8 @@ val config = IDKitRequestConfig( appId = "app_xxxxx", action = "my-action", rpContext = rpContext, - actionDescription = "Verify user", - bridgeUrl = null, allowLegacyProofs = true, - overrideConnectBaseUrl = null, + returnTo = "https://example.com/done", // Optional: deep-link callback URL environment = Environment.PRODUCTION, ) diff --git a/world-id/idkit/react.mdx b/world-id/idkit/react.mdx index b7306a5..872f3d8 100644 --- a/world-id/idkit/react.mdx +++ b/world-id/idkit/react.mdx @@ -132,3 +132,5 @@ For RP signature generation in React/Next.js apps, use the pure JS subpath: ```ts import { signRequest } from "@worldcoin/idkit/signing"; ``` + +See [RP Signatures](/world-id/idkit/signatures) for the full algorithm and test vectors. diff --git a/world-id/idkit/signatures.mdx b/world-id/idkit/signatures.mdx new file mode 100644 index 0000000..ea003ce --- /dev/null +++ b/world-id/idkit/signatures.mdx @@ -0,0 +1,210 @@ +--- +title: "RP Signatures" +description: "Spec for generating RP signatures, with pseudocode, SDK examples, and test vectors." +"og:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" +"twitter:image": "https://raw.githubusercontent.com/worldcoin/developer-docs/main/images/docs/docs-meta.png" +--- + +Relying Party (RP) signatures prove that a proof request genuinely comes from your app, preventing impersonation attacks. +Your backend signs every request with the `signing_key` from the [Developer Portal](https://developer.world.org), +and World App verifies the signature before generating a proof. RP signatures are enforced for World ID 4.0 requests. + + + Never expose your signing key to client-side code. If the key leaks, rotate it immediately in the Developer Portal. + + +## Algorithm + + +```text title="Implement it yourself" +// IMPORTANT: Use Keccak-256, NOT SHA3-256. They have different padding. +// Most Ethereum libraries (ethers, viem, web3) use Keccak-256. + +function hash_to_field(input_bytes) -> bytes32: + h = keccak256(input_bytes) // 32 bytes + n = big_endian_uint256(h) >> 8 // shift right 8 bits + return uint256_to_32bytes_be(n) // always starts with 0x00 + +function compute_rp_signature_message(nonce_bytes32, created_at_u64, expires_at_u64, action?) -> bytes: + msg = new bytes(49) // 49 bytes without action, 81 with + msg[0] = 0x01 // version byte + msg[1..32] = nonce_bytes32 // 32-byte field element + msg[33..40] = u64_to_be(created_at) // big-endian uint64 + msg[41..48] = u64_to_be(expires_at) // big-endian uint64 + + if action is not null: + msg[49..80] = hash_to_field(utf8_encode(action)) + + return msg + +function sign_request(signing_key_hex, action?, ttl_seconds = 300): + // Accept signing keys with or without 0x prefix + key = parse_hex_32_bytes(signing_key_hex) + + // 1. Generate nonce + random = crypto_random_bytes(32) + nonce_bytes = hash_to_field(random) + + // 2. Timestamps + created_at = unix_time_seconds() + expires_at = created_at + ttl_seconds + + // 3. Build message + msg = compute_rp_signature_message(nonce_bytes, created_at, expires_at, action) + + // 4. EIP-191 prefix and hash + // The prefix uses the DECIMAL byte length of the message (e.g. "49" or "81") + prefix = "\x19Ethereum Signed Message:\n" + decimal_string(length(msg)) + digest = keccak256(prefix + msg) + + // 5. Sign with recoverable ECDSA (secp256k1) + (r, s, recovery_id) = ecdsa_secp256k1_sign(digest, key) + + // 6. Encode: r(32) || s(32) || v(1), where v = recovery_id + 27 + sig65 = r + s + byte(recovery_id + 27) + + return { + sig: "0x" + hex(sig65), + nonce: "0x" + hex(nonce_bytes), + created_at: created_at, + expires_at: expires_at, + } +``` + +```typescript title="JavaScript / TypeScript" +// Also available from @worldcoin/idkit/signing and @worldcoin/idkit-core/signing +import { signRequest } from "@worldcoin/idkit-server"; + +const sig = signRequest({ + signingKeyHex: process.env.RP_SIGNING_KEY!, + action: "my-action", + ttl: 300, // optional, default 300s +}); + +// sig = { sig, nonce, createdAt, expiresAt } +``` + +```go title="Go" +import "github.com/worldcoin/idkit/go/idkit" + +// One-shot signing with options +sig, err := idkit.SignRequest( + os.Getenv("RP_SIGNING_KEY"), + idkit.WithAction("my-action"), + idkit.WithTTL(300), // optional, default 300s +) + +// Or create a reusable signer for high-throughput backends +signer, err := idkit.NewSigner(os.Getenv("RP_SIGNING_KEY")) +sig, err = signer.SignRequest(idkit.WithAction("my-action")) +``` + + +## Test vectors + +Use these to verify your implementation. All vectors use deterministic inputs. + +### `hash_to_field` + + +```text title="empty string" +input: "" (empty) +output: 0x00c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4 +``` + +```text title='"test_signal"' +input: "test_signal" +output: 0x00c1636e0a961a3045054c4d61374422c31a95846b8442f0927ad2ff1d6112ed +``` + +```text title="raw bytes" +input: [0x01, 0x02, 0x03] +output: 0x00f1885eda54b7a053318cd41e2093220dab15d65381b1157a3633a83bfd5c92 +``` + +```text title='"hello"' +input: "hello" (0x68656c6c6f) +output: 0x001c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36dea +``` + + +### `compute_rp_signature_message` + + +```text title="without action (49 bytes)" +compute_rp_signature_message( + nonce = 0x008ae1aa597fa146ebd3aa2ceddf360668dea5e526567e92b0321816a4e895bd, + created_at = 1700000000, + expires_at = 1700000300, +) + +output: +01008ae1aa597fa146ebd3aa2ceddf360668dea5e526567e92b0321816a4e895bd000000006553f100000000006553f22c +``` + +```text title='with action "test-action" (81 bytes)' +compute_rp_signature_message( + nonce = 0x008ae1aa597fa146ebd3aa2ceddf360668dea5e526567e92b0321816a4e895bd, + created_at = 1700000000, + expires_at = 1700000300, + action = "test-action", +) + +output: +01008ae1aa597fa146ebd3aa2ceddf360668dea5e526567e92b0321816a4e895bd000000006553f100000000006553f22c00aa0ce59768ae5b1c52f07a9387f14f09f277422c0d2f8a268c7bad0c60a46a +``` + + +### `sign_request` + + +```text title="without action (session proof)" +sign_request( + signing_key = 0xabababababababababababababababababababababababababababababababab, + random = [0x00, 0x01, ..., 0x1f], // deterministic for testing + created_at = 1700000000, // fixed clock for testing + ttl = 300, +) + +nonce: 0x008ae1aa597fa146ebd3aa2ceddf360668dea5e526567e92b0321816a4e895bd +msg length: 49 bytes +sig: 0x14f693175773aed912852a601e9c0fd30f2afe2738d31388316232ce6f64ae9e4edbfb19d81c4229ba9c9fca78ede4b28956b7ba4415f08d957cbc1b3bdaa4021b +``` + +```text title='with action "test-action" (uniqueness proof)' +sign_request( + signing_key = 0xabababababababababababababababababababababababababababababababab, + action = "test-action", + random = [0x00, 0x01, ..., 0x1f], // deterministic for testing + created_at = 1700000000, // fixed clock for testing + ttl = 300, +) + +nonce: 0x008ae1aa597fa146ebd3aa2ceddf360668dea5e526567e92b0321816a4e895bd +msg length: 81 bytes +sig: 0x05594adb6c1495768a38d523d7d6ee6356b2c31231919198794ed022ade7d08f73753f83bd167067d99c9b969d28e9222315837c66af25867b041273a6d5056f1b +``` + + +### Validation checks + + +```text title="format" +signature: ^0x[0-9a-f]{130}$ (65 bytes) +nonce: ^0x[0-9a-f]{64}$ (32 bytes) +nonce[0]: always 0x00 +v byte: 27 (0x1b) or 28 (0x1c) +``` + +```text title="behavior" +default TTL: 300 seconds +keys accepted: with or without 0x prefix +rejected: invalid hex, wrong-length keys +``` + + +## Related pages + +- [Integration guide](/world-id/idkit/integrate) +- [JavaScript SDK reference](/world-id/idkit/javascript) +- [Go SDK reference](/world-id/idkit/go) diff --git a/world-id/idkit/swift.mdx b/world-id/idkit/swift.mdx index 5ae1eb5..e1bb1d3 100644 --- a/world-id/idkit/swift.mdx +++ b/world-id/idkit/swift.mdx @@ -37,10 +37,8 @@ let config = IDKitRequestConfig( appId: "app_xxxxx", action: "my-action", rpContext: rpContext, - actionDescription: "Verify user", - bridgeUrl: nil, allowLegacyProofs: true, - overrideConnectBaseUrl: nil, + returnTo: "https://example.com/done", // Optional: deep-link callback URL environment: .production ) From e723b8c350e227df4151a15ba9e6cb53c42b0c0a Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:49:55 +0300 Subject: [PATCH 2/5] link --- world-id/idkit/signatures.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world-id/idkit/signatures.mdx b/world-id/idkit/signatures.mdx index ea003ce..f447d7f 100644 --- a/world-id/idkit/signatures.mdx +++ b/world-id/idkit/signatures.mdx @@ -7,7 +7,7 @@ description: "Spec for generating RP signatures, with pseudocode, SDK examples, Relying Party (RP) signatures prove that a proof request genuinely comes from your app, preventing impersonation attacks. Your backend signs every request with the `signing_key` from the [Developer Portal](https://developer.world.org), -and World App verifies the signature before generating a proof. RP signatures are enforced for World ID 4.0 requests. +and World App verifies the signature before generating a proof. RP signatures are enforced for [World ID 4.0 requests](/world-id/4-0-migration). Never expose your signing key to client-side code. If the key leaks, rotate it immediately in the Developer Portal. From 9c51bbe7cd7fc24cd3b56e1992eeec3acb5c4d99 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:27:20 +0300 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20address=20PR=20review=20=E2=80=94=20?= =?UTF-8?q?pseudocode=20buffer=20size,=20SQL=20syntax,=20UNIQUE=20constrai?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix pseudocode to conditionally allocate 49 or 81 bytes based on action - Remove trailing comma in PostgreSQL CREATE TABLE example - Add UNIQUE(nullifier, action) constraint to enforce replay protection at DB level Co-Authored-By: Claude Opus 4.6 (1M context) --- world-id/idkit/integrate.mdx | 5 ++--- world-id/idkit/signatures.mdx | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/world-id/idkit/integrate.mdx b/world-id/idkit/integrate.mdx index add7976..32a6cbe 100644 --- a/world-id/idkit/integrate.mdx +++ b/world-id/idkit/integrate.mdx @@ -330,12 +330,11 @@ Nullifiers are returned as 0x-prefixed hex strings representing 256-bit integers ```sql title="PostgreSQL schema" -CREATE TABLE users ( - id SERIAL PRIMARY KEY, - -- Store the nullifier as a new column to the users table or in a separate table +CREATE TABLE nullifiers ( nullifier NUMERIC(78, 0) NOT NULL, action TEXT NOT NULL, verified_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + UNIQUE (nullifier, action) ); ``` diff --git a/world-id/idkit/signatures.mdx b/world-id/idkit/signatures.mdx index f447d7f..1d1b31f 100644 --- a/world-id/idkit/signatures.mdx +++ b/world-id/idkit/signatures.mdx @@ -26,7 +26,8 @@ function hash_to_field(input_bytes) -> bytes32: return uint256_to_32bytes_be(n) // always starts with 0x00 function compute_rp_signature_message(nonce_bytes32, created_at_u64, expires_at_u64, action?) -> bytes: - msg = new bytes(49) // 49 bytes without action, 81 with + size = 81 if action else 49 + msg = new bytes(size) msg[0] = 0x01 // version byte msg[1..32] = nonce_bytes32 // 32-byte field element msg[33..40] = u64_to_be(created_at) // big-endian uint64 From 532827de51a270123cbaa56fd77a775ba36b05b3 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:03:22 +0300 Subject: [PATCH 4/5] remove validation checks --- world-id/idkit/signatures.mdx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/world-id/idkit/signatures.mdx b/world-id/idkit/signatures.mdx index 1d1b31f..ed0a11c 100644 --- a/world-id/idkit/signatures.mdx +++ b/world-id/idkit/signatures.mdx @@ -187,23 +187,6 @@ sig: 0x05594adb6c1495768a38d523d7d6ee6356b2c31231919198794ed022ade7d08f73753f83b ``` -### Validation checks - - -```text title="format" -signature: ^0x[0-9a-f]{130}$ (65 bytes) -nonce: ^0x[0-9a-f]{64}$ (32 bytes) -nonce[0]: always 0x00 -v byte: 27 (0x1b) or 28 (0x1c) -``` - -```text title="behavior" -default TTL: 300 seconds -keys accepted: with or without 0x prefix -rejected: invalid hex, wrong-length keys -``` - - ## Related pages - [Integration guide](/world-id/idkit/integrate) From 613543a64bccd7da5853f502cee065d8b22a9ae8 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:06:57 +0300 Subject: [PATCH 5/5] fix: use mobile deep-link scheme for return_to examples Co-Authored-By: Claude Opus 4.6 (1M context) --- world-id/idkit/integrate.mdx | 2 +- world-id/idkit/javascript.mdx | 2 +- world-id/idkit/kotlin.mdx | 2 +- world-id/idkit/swift.mdx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/world-id/idkit/integrate.mdx b/world-id/idkit/integrate.mdx index 32a6cbe..4743cda 100644 --- a/world-id/idkit/integrate.mdx +++ b/world-id/idkit/integrate.mdx @@ -142,7 +142,7 @@ const request = await IDKit.request({ }, allow_legacy_proofs: true, environment: "production", // Only set this to staging for testing with the simulator - return_to: "https://example.com/verify-done", // Optional: deep-link callback URL + return_to: "myapp://verify-done", // Optional: mobile deep-link callback URL // Signal (optional): Bind specific context into the requested proof. // Examples: user ID, wallet address. Your backend should enforce the same value. }).preset(orbLegacy({ signal: "local-election-1" })); diff --git a/world-id/idkit/javascript.mdx b/world-id/idkit/javascript.mdx index a2ca159..8d82e55 100644 --- a/world-id/idkit/javascript.mdx +++ b/world-id/idkit/javascript.mdx @@ -48,7 +48,7 @@ const builder = IDKit.request({ }, allow_legacy_proofs: true, environment: "production", // Only set this to staging for testing with the simulator - return_to: "https://example.com/done", // Optional: deep-link callback URL + return_to: "myapp://verify-done", // Optional: mobile deep-link callback URL bridge_url: undefined, // Optional: custom bridge URL }); ``` diff --git a/world-id/idkit/kotlin.mdx b/world-id/idkit/kotlin.mdx index eb979ad..66b0891 100644 --- a/world-id/idkit/kotlin.mdx +++ b/world-id/idkit/kotlin.mdx @@ -55,7 +55,7 @@ val config = IDKitRequestConfig( action = "my-action", rpContext = rpContext, allowLegacyProofs = true, - returnTo = "https://example.com/done", // Optional: deep-link callback URL + returnTo = "myapp://verify-done", // Optional: mobile deep-link callback URL environment = Environment.PRODUCTION, ) diff --git a/world-id/idkit/swift.mdx b/world-id/idkit/swift.mdx index e1bb1d3..b74f87b 100644 --- a/world-id/idkit/swift.mdx +++ b/world-id/idkit/swift.mdx @@ -38,7 +38,7 @@ let config = IDKitRequestConfig( action: "my-action", rpContext: rpContext, allowLegacyProofs: true, - returnTo: "https://example.com/done", // Optional: deep-link callback URL + returnTo: "myapp://verify-done", // Optional: mobile deep-link callback URL environment: .production )