Skip to content

refactor!: collapse create_session proof type into session x create - #866

Open
kilianglas wants to merge 44 commits into
mainfrom
kilianglas/collapse-create-session
Open

refactor!: collapse create_session proof type into session x create#866
kilianglas wants to merge 44 commits into
mainfrom
kilianglas/collapse-create-session

Conversation

@kilianglas

@kilianglas kilianglas commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Removes ProofType::CreateSession and redesigns the request model: proof_type shrinks to {uniqueness, session} and session_id becomes a three-state field. Creating a session was always create-and-prove (a session proof in the same response), so it collapses into proof_type: "session" + session_id: "create" with no capability change. Hence, there are only two proof types now, Uniqueness Proofs and Session Proofs. Both can create a session, only session proofs can be bound to an existing session. Therefore, sessions can either be bound to a nullifier, or can be started without any binding. Note, that Uniquess Proofs can still be requested without creating or binding a session.

Stacked on #862.

Request model

New SessionRef type carried by ProofRequest.session_id:

wire value state
absent / null SessionRef::None
"create" SessionRef::Create — mint a fresh session, prove it in the same response
"session_<hex>" SessionRef::Existing(SessionId)

Note: The SessionRef is not in the signed part of the request.

Validation matrix (validate_proof_type):

null "create" "session_<hex>"
uniqueness ✅ plain ✅ create bound session
session ✅ (was create_session)

action remains forbidden for proof_type: "session" (both sub-states).

Different combinations have different use cases:

  • uniqueness, null: Standalone uniqueness proof that does not require session.
  • uniqueness, "create": Uniqueness proof that requires bound session for later session proof.
  • session, "create": Create new standalone session to prove human continuity without uniqueness guarantees. Used e.g., in the upcoming DeepFace proof type.
  • session, "session_<hex>": Session proof for existing session. This is used for any session_id, uniqueness bound or not.

Changes to OPRF nodes

To make this work, the auth module of the ORPF nodes had to be slightly changed. OPRF queries with prefix 0x01 (used to obtain the session seed), can now originate from Uniqueness Proof RP requests. Those have an action field iin the signed data. Hence, in order to verify the RP Request signature, the OPRF request needs to carry this action, which differs from action = oprf_seed which is used for the query. We introduce a new field called rp_signature_verification on NullifierOprfRequestAuthV1 struct.

WIP-101 RPs

Important: Combined uniqueness + create session requests are not yet supported. This will be implemented in a follow-up PR.

kilianglas and others added 5 commits July 14, 2026 15:18
Replace the dedicated create-session proof type with a three-state session reference so request semantics are explicit on the wire.

Co-authored-by: Cursor <cursoragent@cursor.com>
Carry the RP-signed uniqueness action separately from the session OPRF seed so nodes can safely authorize atomic session creation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Accept create and existing session references on uniqueness requests, mint sessions through the authenticator, and validate the corresponding response semantics.

Co-authored-by: Cursor <cursoragent@cursor.com>
Exercise the signed-action create flow end to end and generate verifier fixtures from a session minted by the uniqueness request.

Co-authored-by: Cursor <cursoragent@cursor.com>
Document create, existing, and unbound uniqueness modes together with the signed-action OPRF flow and deployment order.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kilianglas
kilianglas force-pushed the kilianglas/collapse-create-session branch from 5fee2df to f651855 Compare July 14, 2026 13:25
Comment thread crates/primitives/src/session.rs
Comment thread crates/authenticator/src/prove.rs Outdated
Comment thread crates/primitives/src/request/mod.rs
/// If omitted, the request is strictly treated as a [`ProofType::Uniqueness`] request.
/// Session creation and session proving must opt in explicitly.
#[serde(default)]
pub proof_type: ProofType,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old create_session requests will now fail for sure though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. @paolodamico is this an issue?

@kilianglas
kilianglas force-pushed the kilianglas/collapse-create-session branch from faf2043 to 2d7d228 Compare July 21, 2026 12:52
kilianglas and others added 3 commits August 3, 2026 21:41
Zero is the circuit's "no session" sentinel, satisfiable by any World ID
regardless of mt_index, so a session record left at zero could be satisfied
by an arbitrary World ID with a patched authenticator. verifyWithSession
already rejected it; apply the same check to verifySession.

V2 only — older implementations are immutable.

Reported via HackerOne #3912490.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0xThemis
0xThemis previously approved these changes Aug 4, 2026
dkales
dkales previously approved these changes Aug 4, 2026
kilianglas and others added 6 commits August 4, 2026 12:02
V2 is released and immutable, so the new entry point and the session id
validation move to a new version instead of extending V2 in place.

- IWorldIDVerifierV3 declares verifyWithSession + InvalidSessionId
- WorldIDVerifierV3 implements verifyWithSession and overrides
  verifySession to reject sessionId == 0
- WorldIDVerifierV2.sol is restored byte-identical to main; InvalidAction
  stays declared inline there and V3 inherits it
- V2 tests return to their original set (regenerated fixture values only);
  the binding and zero-session cases move to WorldIDVerifierV3Test
- test-utils deploys V3 behind the proxy; e2e and the fixture tool retarget

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ilianglas/collapse-create-session

# Conflicts:
#	contracts/src/core/interfaces/IWorldIDVerifierV3.sol
The regenerated fixture values were unnecessary: all six V2 tests are
negative (four revert on InvalidAction before any proof math, two expect
ProofInvalid), so they pass unchanged against the refreshed mocks.

Leaves the PR with no V2 footprint at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The verifySession zero-session check is a separate security fix and moves
to its own PR against main. V3 here adds only the new entry point.

Depends on that PR landing for the session-proof hardening.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ilianglas/collapse-create-session

# Conflicts:
#	contracts/src/core/interfaces/IWorldIDVerifierV3.sol
Base automatically changed from kilianglas/uniqueness-session-binding-contracts to main August 5, 2026 15:32
@kilianglas
kilianglas dismissed stale reviews from dkales and 0xThemis August 5, 2026 15:32

The base branch was changed.

kilianglas and others added 2 commits August 6, 2026 11:15
…reate-session

# Conflicts:
#	contracts/test/core/WorldIDVerifierV3Test.t.sol
#	crates/authenticator/src/prove.rs
#	crates/core/tests/generate_proof.rs
#	crates/primitives/src/request/mod.rs
#	crates/test-utils/src/anvil.rs
#	docs/world-id-4-specs/README.md
The merge left main's session-bound section alongside the create-and-bind one,
so the tool no longer compiled: session_id is now SessionRef, not
Option<SessionId>. Removes the stale block and the nullifier clone it needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kilianglas
kilianglas enabled auto-merge (squash) August 6, 2026 09:34
Keeps the discriminants aligned with the action prefixes: 0x00 for a uniqueness
action, 0x02 for a session action. 0x01 is skipped because it prefixes the
session oprf_seed rather than a proof flow.

Reinstates the reservation from #711 for future signed request payloads, and
pins it with a test so the values cannot renumber silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@0xForerunner

Copy link
Copy Markdown
Contributor

@kilianglas Why do we disallow uniqueness + session_<hex>?

@0xForerunner

Copy link
Copy Markdown
Contributor

It's worth noting that this PR allows oprf nodes to link the initial uniqueness action and the session seed. I don't really think that's a problem in practice, just wanted to flag it.


let rp_signature_verification = match (proof_request.proof_type, proof_request.action) {
(ProofType::Uniqueness, Some(action)) => {
Some(RpSignatureVerification::UniquenessAction { action })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signed message doesn't include proof_type or session_id, which means these fields can be changed and the signature is still valid. We probably want to fix this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid more general question. The proof type is implicitly encoded in the action (action with 0x00 prefix is present -> uniqueness, no action -> session). The session id field is currently not signed over by the RP, and there might be ways to abuse this (need to think about this). Since this is pre-existing behavior, though, I don't think this should be addressed in this PR.

@kilianglas

Copy link
Copy Markdown
Contributor Author

@kilianglas Why do we disallow uniqueness + session_<hex>?

We thought it might be an anonymity footgun if we allow RPs to bind uniqueness proofs to existing sessions.

@kilianglas

Copy link
Copy Markdown
Contributor Author

It's worth noting that this PR allows oprf nodes to link the initial uniqueness action and the session seed. I don't really think that's a problem in practice, just wanted to flag it.

Yes, that's true. Didn't think this was an issue, especially since r is cached anyway by the authenticator, session proofs don't need the oprf_seed round trip in most cases. Can you think of any scenario where this might be an issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants