Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion js/examples/nextjs/app/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ const RETURN_TO_TOOLTIP =

type PresetKind = "orb" | "secure_document" | "document" | "device" | "selfie";

type V4CredentialType = "proof_of_human" | "passport" | "mnc";
type V4CredentialType = "proof_of_human" | "selfie" | "passport" | "mnc";

const V4_CREDENTIAL_TO_NAME: Record<V4CredentialType, string> = {
proof_of_human: "Proof of Human",
selfie: "Selfie",
passport: "Passport",
mnc: "MNC",
};
Expand Down Expand Up @@ -409,6 +410,7 @@ export function DemoClient(): ReactElement {
}
>
<option value="proof_of_human">Proof Of Human</option>
<option value="selfie">Selfie</option>
<option value="passport">Passport</option>
<option value="mnc">MNC</option>
</select>
Expand Down
8 changes: 4 additions & 4 deletions js/packages/core/src/__tests__/smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,16 @@ describe("IDKitRequest API", () => {

it("should create any() constraint correctly", () => {
const poh = CredentialRequest("proof_of_human");
const face = CredentialRequest("face");
const constraint = any(poh, face);
const selfie = CredentialRequest("selfie");
const constraint = any(poh, selfie);
expect(constraint).toHaveProperty("any");
expect(constraint.any).toHaveLength(2);
});

it("should create enumerate() constraint correctly", () => {
const poh = CredentialRequest("proof_of_human");
const face = CredentialRequest("face");
const constraint = enumerate(poh, face);
const selfie = CredentialRequest("selfie");
const constraint = enumerate(poh, selfie);
expect(constraint).toHaveProperty("enumerate");
expect(constraint.enumerate).toHaveLength(2);
});
Expand Down
18 changes: 9 additions & 9 deletions js/packages/core/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,14 @@ class IDKitInviteCodeRequestImpl implements IDKitInviteCodeRequest {
/**
* Creates a CredentialRequest for a credential type
*
* @param credential_type - The type of credential to request (e.g., 'proof_of_human', 'face')
* @param credential_type - The type of credential to request (e.g., 'proof_of_human', 'selfie')
* @param options - Optional signal, genesis_issued_at_min, and expires_at_min
* @returns A CredentialRequest object
*
* @example
* ```typescript
* const orb = CredentialRequest('proof_of_human', { signal: 'user-123' })
* const face = CredentialRequest('face')
* const selfie = CredentialRequest('selfie')
* // Require credential to be valid for at least one year
* const withExpiry = CredentialRequest('proof_of_human', { expires_at_min: Date.now() / 1000 + 60 * 60 * 60 * 24 * 365 })
* ```
Expand Down Expand Up @@ -247,7 +247,7 @@ export function CredentialRequest(
*
* @example
* ```typescript
* const constraint = any(CredentialRequest('proof_of_human'), CredentialRequest('face'))
* const constraint = any(CredentialRequest('proof_of_human'), CredentialRequest('selfie'))
* ```
*/
export function any(...nodes: ConstraintNode[]): { any: ConstraintNode[] } {
Expand Down Expand Up @@ -539,7 +539,7 @@ class IDKitBuilder {
* @example
* ```typescript
* const request = await IDKit.request({ app_id, action, rp_context, allow_legacy_proofs: false })
* .constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('face')));
* .constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('selfie')));
* ```
*/
async constraints(constraints: ConstraintNode): Promise<IDKitRequest> {
Expand Down Expand Up @@ -680,7 +680,7 @@ class IDKitInviteCodeBuilder {
* @example
* ```typescript
* const request = await IDKit.requestWithInviteCode({ app_id, action, rp_context, allow_legacy_proofs: false })
* .constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('face')));
* .constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('selfie')));
* displayLink(request.connectorURI);
* ```
*/
Expand Down Expand Up @@ -754,7 +754,7 @@ class IDKitInviteCodeBuilder {
* action: 'my-action',
* rp_context: { ... },
* allow_legacy_proofs: false,
* }).constraints(enumerate(CredentialRequest('proof_of_human'), CredentialRequest('face')));
* }).constraints(enumerate(CredentialRequest('proof_of_human'), CredentialRequest('selfie')));
*
* // In World App: connectorURI is empty, result comes via postMessage
* // On web: connectorURI is the QR URL to display
Expand Down Expand Up @@ -812,7 +812,7 @@ function createRequest(config: IDKitRequestConfig): IDKitBuilder {
* action: 'my-action',
* rp_context: { ... },
* allow_legacy_proofs: false,
* }).constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('face')));
* }).constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('selfie')));
*
* displayLink(request.connectorURI); // user opens this URL on their phone
* const proof = await request.pollUntilCompletion();
Expand Down Expand Up @@ -873,7 +873,7 @@ function createRequestWithInviteCode(
* const request = await IDKit.createSession({
* app_id: 'app_staging_xxxxx',
* rp_context: { ... },
* }).constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('face')));
* }).constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('selfie')));
*
* // Display QR, wait for proof
* const result = await request.pollUntilCompletion();
Expand Down Expand Up @@ -923,7 +923,7 @@ function createSession(config: IDKitSessionConfig): IDKitBuilder {
* const request = await IDKit.proveSession(savedSessionId, {
* app_id: 'app_staging_xxxxx',
* rp_context: { ... },
* }).constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('face')));
* }).constraints(any(CredentialRequest('proof_of_human'), CredentialRequest('selfie')));
*
* const result = await request.pollUntilCompletion();
* // result.session_id -> same session
Expand Down
6 changes: 3 additions & 3 deletions js/packages/core/src/transports/native.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe("native transport request lifecycle", () => {
it("uses per-identifier signal hashes when response omits signal_hash", async () => {
const signalHashes = {
proof_of_human: hashSignal("poh-signal"),
face: hashSignal("face-signal"),
selfie: hashSignal("selfie-signal"),
};

const req = createNativeRequest({}, baseConfig, signalHashes, "");
Expand All @@ -139,7 +139,7 @@ describe("native transport request lifecycle", () => {
expires_at_min: 0,
},
{
identifier: "face",
identifier: "selfie",
proof: proofResponseProof,
nullifier: proofResponseNullifier("b"),
issuer_schema_id: 11,
Expand All @@ -156,7 +156,7 @@ describe("native transport request lifecycle", () => {
signalHashes.proof_of_human,
);
expect(completion.result.responses[1]?.signal_hash).toBe(
signalHashes.face,
signalHashes.selfie,
);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,8 @@ data class IDKitPollOptions(
class IDKitBuilder internal constructor(
private val inner: IdKitBuilder,
) {
// TODO: Re-enable when World ID 4.0 is live
// fun constraints(constraints: ConstraintNode): IDKitRequest =
// IDKitRequest(inner.constraints(constraints))
fun constraints(constraints: uniffi.idkit_core.ConstraintNode): IDKitRequest =
IDKitRequest(inner.constraints(constraints))

fun preset(preset: Preset): IDKitRequest =
IDKitRequest(inner.preset(preset))
Expand Down
50 changes: 49 additions & 1 deletion rust/core/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,54 @@ mod tests {
assert!(json.contains("allow_legacy_proofs"));
}

#[test]
fn test_build_request_payload_serializes_selfie_v4_request() {
let app_id = AppId::new("app_test").unwrap();
let signature = "0x".to_string() + &"00".repeat(64) + "1b";
let rp_context = RpContext::new(
"rp_1234567890abcdef",
"0x0000000000000000000000000000000000000000000000000000000000000001",
1_700_000_000,
1_700_003_600,
&signature,
)
.unwrap();
let constraints = ConstraintNode::item(CredentialRequest::new(
CredentialType::Selfie,
Some(Signal::from_string("selfie-signal")),
));

let params = BridgeConnectionParams {
app_id,
kind: RequestKind::Uniqueness {
action: "test-action".to_string(),
},
constraints: Some(constraints),
rp_context,
action_description: Some("Selfie check".to_string()),
legacy_verification_level: VerificationLevel::Device,
legacy_signal: String::new(),
bridge_url: None,
allow_legacy_proofs: false,
override_connect_base_url: None,
return_to: None,
environment: Some(Environment::Production),
identity_attributes: None,
};

let payload = build_request_payload(&params, false).unwrap();
let proof_request = payload.get("proof_request").unwrap();
assert_eq!(
proof_request["proof_requests"][0]["identifier"],
serde_json::json!("selfie")
);
assert_eq!(
proof_request["proof_requests"][0]["issuer_schema_id"],
serde_json::json!(11)
);
assert_eq!(payload["verification_level"], serde_json::json!("device"));
}

#[test]
fn test_build_request_payload_serializes_identity_attributes() {
let app_id = AppId::new("app_test").unwrap();
Expand Down Expand Up @@ -2402,7 +2450,7 @@ mod tests {
);
assert_eq!(
CredentialType::from_issuer_schema_id(11),
Some(CredentialType::Face)
Some(CredentialType::Selfie)
);
assert_eq!(
CredentialType::from_issuer_schema_id(9303),
Expand Down
46 changes: 23 additions & 23 deletions rust/core/src/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,8 @@ mod tests {
CredentialRequest::new(CredentialType::ProofOfHuman, None)
}

fn face_item() -> CredentialRequest {
CredentialRequest::new(CredentialType::Face, None)
fn selfie_item() -> CredentialRequest {
CredentialRequest::new(CredentialType::Selfie, None)
}

fn passport_item() -> CredentialRequest {
Expand Down Expand Up @@ -413,32 +413,32 @@ mod tests {
fn test_any_node() {
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
ConstraintNode::item(passport_item()),
]);

let mut available = HashSet::new();
available.insert(CredentialType::Face);
available.insert(CredentialType::Selfie);
available.insert(CredentialType::Passport);

assert!(node.evaluate(&available));
// Should return Face because it's first in priority order
// Should return Selfie because it's first in priority order
assert_eq!(
node.first_satisfying(&available),
Some(CredentialType::Face)
Some(CredentialType::Selfie)
);
}

#[test]
fn test_any_node_priority() {
// ProofOfHuman has highest priority, Face second
// ProofOfHuman has highest priority, Selfie second
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
]);

let mut available = HashSet::new();
available.insert(CredentialType::Face);
available.insert(CredentialType::Selfie);
available.insert(CredentialType::ProofOfHuman);

// Even though both are available, ProofOfHuman should be selected (higher priority)
Expand All @@ -452,7 +452,7 @@ mod tests {
fn test_all_node() {
let node = ConstraintNode::all(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
]);

let mut available = HashSet::new();
Expand All @@ -461,7 +461,7 @@ mod tests {
// Only one is available, should fail
assert!(!node.evaluate(&available));

available.insert(CredentialType::Face);
available.insert(CredentialType::Selfie);

// Both available, should succeed
assert!(node.evaluate(&available));
Expand All @@ -471,7 +471,7 @@ mod tests {
#[test]
fn test_enumerate_node() {
let node = ConstraintNode::enumerate(vec![
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
ConstraintNode::item(passport_item()),
]);

Expand Down Expand Up @@ -511,17 +511,17 @@ mod tests {
fn test_face_orb_example() {
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
]);

let mut available = HashSet::new();
available.insert(CredentialType::Face);
available.insert(CredentialType::Selfie);

// Only face available
// Only selfie available
assert!(node.evaluate(&available));
assert_eq!(
node.first_satisfying(&available),
Some(CredentialType::Face)
Some(CredentialType::Selfie)
);

// Both available - proof_of_human has priority
Expand Down Expand Up @@ -555,15 +555,15 @@ mod tests {
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::all(vec![
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
ConstraintNode::item(passport_item()),
]),
]);

let credentials = node.collect_credential_types();
assert_eq!(credentials.len(), 3);
assert!(credentials.contains(&CredentialType::ProofOfHuman));
assert!(credentials.contains(&CredentialType::Face));
assert!(credentials.contains(&CredentialType::Selfie));
assert!(credentials.contains(&CredentialType::Passport));
}

Expand All @@ -572,7 +572,7 @@ mod tests {
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::all(vec![
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
ConstraintNode::item(passport_item()),
]),
]);
Expand All @@ -597,7 +597,7 @@ mod tests {
fn test_serialization() {
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
]);

let json = serde_json::to_string(&node).unwrap();
Expand All @@ -615,15 +615,15 @@ mod tests {
// Test any constraint
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
]);
let (items, expr) = node.to_protocol().unwrap();

assert_eq!(items.len(), 2);
let json = serde_json::to_string(&expr).unwrap();
assert!(json.contains("any"));
assert!(json.contains("proof_of_human"));
assert!(json.contains("face"));
assert!(json.contains("selfie"));
}

#[test]
Expand Down Expand Up @@ -656,7 +656,7 @@ mod tests {
// Multiple items should have constraint expression
let node = ConstraintNode::any(vec![
ConstraintNode::item(poh_item()),
ConstraintNode::item(face_item()),
ConstraintNode::item(selfie_item()),
]);
let (items, expr) = node.to_protocol_top_level().unwrap();

Expand Down
Loading
Loading