You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: invite-code mode core API (WDP-73 / APP-9428)
Adds a code-based discovery channel to the existing Bridge proof flow.
The RP shows the user a 6-character code; the user types it into World
App on their phone and completes the existing Selfie Check / proof
flow. Same proof flow, same poll loop, same Status enum — only the
discovery channel changes.
Wire shape (matches wallet-bridge APP-9425 and world-app-iosAPP-9424):
- Code is canonical 6-char Crockford Base32 (5 random data chars + 1
mod-32 weighted check digit, weights 1/3/5/7/9 — all coprime to 32,
so 100% of single-char substitutions are caught). UI may format as
"ABC-DEF" but the canonical form has no separator.
- HKDF-SHA256, no salt, 32-byte output, IKM = canonical code's UTF-8
bytes. info="dx" → lookup index (lowercase hex on the wire);
info="key" → AES-256-GCM key. The encryption key never reaches the
bridge — only the index and ciphertext do.
- POST /request body has the new `request_code_enabled: true, iv,
payload, index` shape with `iv`/`payload` as standard base64 (same
as the URL/QR path).
- Response carries `session_nonce` and `code_expires_at`. Polling
attaches `Authorization: Bearer <session_nonce>` so we're forward-
compatible with the bridge's session_nonce gate (release-blocking
follow-up on Bridge). URL-mode connections keep `session_nonce: None`
and the header is omitted, so existing bridge behavior is unchanged.
Cross-device implications:
- Original WDP-73 mermaid minted a `delivery_token` ferried back via
universal link. Universal links route to the device that opened them,
so a desktop browser ↔ phone flow never receives the token. We drop
delivery_token from idkit's surface entirely. Anti-collusion in the
code path now degrades to "10-min TTL + one-shot redeem + per-IP
rate limit."
API surface (mirrors existing URL-mode):
- Rust: `BridgeConnection::create_for_invite_code` (retry-once-on-409),
`.invite_code()` / `.code_expires_at()` accessors. New crypto.rs
primitives: `generate_invite_code`, `parse_invite_code`,
`hkdf_invite_index_hex`, `hkdf_invite_key`, `generate_nonce`.
- FFI: `IDKitInviteCodeRequest` sibling to `IDKitRequestWrapper`. New
builder methods `constraints_with_invite_code` /
`preset_with_invite_code` on `IDKitBuilder`.
- WASM: `IDKitInviteCodeRequest` sibling to `IDKitRequest`,
`constraintsWithInviteCode` / `presetWithInviteCode` on the WASM
builder.
- TypeScript: `IDKitInviteCodeRequest` interface + impl,
`IDKitInviteCodeBuilder`, `IDKit.requestWithInviteCode(...)` entry
point. Code mode is bridge-only by definition (user is on a different
device than World App), so the builder skips the `isInWorldApp()`
branch.
- React: `IDKitInviteCodeRequestWidget`, `useIDKitInviteCodeRequest`,
`useIDKitInviteCodeFlow` hooks. New `InviteCodeState` UI component.
- Swift: `presetWithInviteCode(_:)` / `constraintsWithInviteCode(_:)`
on the builder; `IDKitInviteCodeRequest` wrapper exposing `code` and
`expiresAt: Date`.
Adopter diff is two lines per integration:
// before
const req = await IDKit.request(config).constraints(...);
showQR(req.connectorURI);
// after
const req = await IDKit.requestWithInviteCode(config).constraints(...);
showCode(req.code);
Tests:
- 9 new Rust unit tests covering code generation, parser (round-trip,
separator stripping, lowercase normalization, Crockford ambiguity
collapse, U-rejection, length validation, check-digit validation,
exhaustive single-char substitution detection), HKDF determinism,
hex output shape, index/key differentiation across info strings.
- 86 existing rust tests pass; 54 core JS tests pass; 23 React tests
pass. Clippy clean on native + ffi feature combos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: js/packages/react/src/types/common.ts
+2-2Lines changed: 2 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -39,8 +39,8 @@ export type IDKitInviteCodeHookResult<TResult> = {
39
39
isAwaitingUserConfirmation: boolean;
40
40
isSuccess: boolean;
41
41
isError: boolean;
42
-
/** Canonical 6-char Crockford Base32 invite code (no separator). UI may format as "ABC-DEF". */
43
-
code: string|null;
42
+
/** URL to display to the user (same shape as URL/QR mode's `connectorURI`, with `&c=<code>&a=<app_id>` appended for the `world.org/verify` landing page). */
43
+
connectorURI: string|null;
44
44
/** Unix-seconds expiry of the unredeemed code. */
0 commit comments