Skip to content
Draft
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: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ jobs:
with:
packages: tools platform-tools platforms;android-35 build-tools;35.0.0

- name: Run tests
working-directory: kotlin/
run: gradle bindings:test

- name: Build Kotlin sample app
working-directory: kotlin/Examples/IDKitSampleApp
run: ./gradlew :app:assembleDebug
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.worldcoin.idkit.IDKitRequestConfig
import com.worldcoin.idkit.documentLegacy
import com.worldcoin.idkit.idkitResultToJson
import com.worldcoin.idkit.deviceLegacy
import com.worldcoin.idkit.identityCheck
import com.worldcoin.idkit.orbLegacy
import com.worldcoin.idkit.secureDocumentLegacy
import com.worldcoin.idkit.selfieCheckLegacy
Expand All @@ -53,7 +54,9 @@ import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONArray
import org.json.JSONObject
import uniffi.idkit_core.DocumentType
import uniffi.idkit_core.Environment
import uniffi.idkit_core.IdentityAttribute
import uniffi.idkit_core.RpContext

class MainActivity : ComponentActivity() {
Expand Down Expand Up @@ -239,7 +242,7 @@ private fun LegacyPresetSelector(
onSelect: (SampleLegacyPreset) -> Unit,
) {
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
Text("Legacy preset", style = MaterialTheme.typography.labelLarge)
Text("Preset", style = MaterialTheme.typography.labelLarge)

SampleLegacyPreset.entries
.chunked(2)
Expand Down Expand Up @@ -281,6 +284,7 @@ private enum class SampleLegacyPreset(val label: String) {
DOCUMENT("document"),
DEVICE("device"),
SELFIE_CHECK("selfie check"),
IDENTITY_CHECK("identity check"),
;

fun toPreset(signal: String) = when (this) {
Expand All @@ -289,6 +293,13 @@ private enum class SampleLegacyPreset(val label: String) {
DOCUMENT -> documentLegacy(signal = signal)
DEVICE -> deviceLegacy(signal = signal)
SELFIE_CHECK -> selfieCheckLegacy(signal = signal)
IDENTITY_CHECK -> identityCheck(
attributes = listOf(
IdentityAttribute.MinimumAge(21u),
IdentityAttribute.Nationality("JPN"),
IdentityAttribute.DocumentType(DocumentType.PASSPORT),
),
)
}
}

Expand Down Expand Up @@ -366,7 +377,7 @@ private class SampleModel {
deepLinkReceivedForPendingRequest = false

android.util.Log.i("IDKitSample", "IDKit connector URL: ${request.connectorURI}")
log("Using legacy preset: ${legacyPreset.label}")
log("Using preset: ${legacyPreset.label}")
log("Generated request ID: ${request.requestId}")
log("Configured return_to callback: $returnToURL")
startPollingForRequest(
Expand Down
19 changes: 19 additions & 0 deletions kotlin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,12 @@ import com.worldcoin.idkit.IDKit
import com.worldcoin.idkit.IDKitPollOptions
import com.worldcoin.idkit.IDKitRequestConfig
import com.worldcoin.idkit.IDKitCompletionResult
import com.worldcoin.idkit.IdentityAttribute
import com.worldcoin.idkit.selfieCheckLegacy
import com.worldcoin.idkit.identityCheck
import com.worldcoin.idkit.orbLegacy
import com.worldcoin.idkit.deviceLegacy
import uniffi.idkit_core.DocumentType
import uniffi.idkit_core.Environment
import uniffi.idkit_core.RpContext

Expand Down Expand Up @@ -117,6 +120,22 @@ val request = IDKit
.preset(selfieCheckLegacy(signal = "user-123"))
```

For document-based identity attestation, use:

```kotlin
val request = IDKit
.request(config)
.preset(
identityCheck(
attributes = listOf(
IdentityAttribute.MinimumAge(21u),
IdentityAttribute.Nationality("JPN"),
IdentityAttribute.DocumentType(DocumentType.PASSPORT),
),
),
)
```

## Credential request options parity

```kotlin
Expand Down
9 changes: 9 additions & 0 deletions kotlin/bindings/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ android {
withSourcesJar()
}
}

testOptions {
unitTests.all { test ->
val rustLibDir = project.projectDir.resolve("../../target/release").canonicalPath
test.jvmArgs("-Djna.library.path=$rustLibDir")
}
}
}

dependencies {
Expand All @@ -42,6 +49,8 @@ dependencies {
implementation(kotlin("stdlib"))

testImplementation(kotlin("test"))
// The @aar variant doesn't bundle libjnidispatch — use the plain JVM jar for unit tests
testImplementation("net.java.dev.jna:jna:5.14.0")
}

afterEvaluate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ typealias IDKitRequestConfig = IdKitRequestConfig
typealias IDKitResult = IdKitResult
typealias RpContext = uniffi.idkit_core.RpContext
typealias Environment = uniffi.idkit_core.Environment
typealias DocumentType = uniffi.idkit_core.DocumentType
typealias IdentityAttribute = uniffi.idkit_core.IdentityAttribute

class IDKitClientError(message: String) : IllegalArgumentException(message)

Expand Down Expand Up @@ -333,6 +335,13 @@ fun deviceLegacy(signal: String? = null): Preset = Preset.DeviceLegacy(signal =
*/
fun selfieCheckLegacy(signal: String? = null): Preset = Preset.SelfieCheckLegacy(signal = signal)

/**
* Returns the identity check preset.
*
* This preset requires World ID 4.0-compatible clients.
*/
fun identityCheck(attributes: List<IdentityAttribute>): Preset = Preset.IdentityCheck(attributes = attributes)

fun idkitResultToJson(result: IDKitResult): String = nativeIdkitResultToJson(result)

fun idkitResultFromJson(json: String): IDKitResult = nativeIdkitResultFromJson(json)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
import uniffi.idkit_core.AppError
import uniffi.idkit_core.ConnectUrlMode
// TODO: Re-enable when World ID 4.0 is live
// import uniffi.idkit_core.CredentialType
import uniffi.idkit_core.DocumentType
import uniffi.idkit_core.Environment
import uniffi.idkit_core.IdentityAttribute
import uniffi.idkit_core.Preset
import uniffi.idkit_core.ResponseItem
import uniffi.idkit_core.RpContext
Expand All @@ -25,6 +28,7 @@ class IDKitTests {
sessionId = sessionId,
responses = emptyList<ResponseItem>(),
environment = "production",
identityAttested = null,
)

private fun sampleRpContext(): RpContext {
Expand All @@ -50,6 +54,7 @@ class IDKitTests {
overrideConnectBaseUrl = null,
returnTo = null,
environment = Environment.STAGING,
connectUrlMode = ConnectUrlMode.DEFAULT,
)

// TODO: Re-enable when World ID 4.0 is live
Expand Down Expand Up @@ -291,11 +296,25 @@ class IDKitTests {
assertTrue(doc is Preset.DocumentLegacy)
assertTrue(device is Preset.DeviceLegacy)
assertTrue(face is Preset.SelfieCheckLegacy)
assertEquals("x", (orb as Preset.OrbLegacy).signal)
assertEquals("y", (secureDoc as Preset.SecureDocumentLegacy).signal)
assertEquals("z", (doc as Preset.DocumentLegacy).signal)
assertEquals("d", (device as Preset.DeviceLegacy).signal)
assertEquals("f", (face as Preset.SelfieCheckLegacy).signal)
assertEquals("x", (orb).signal)
assertEquals("y", (secureDoc).signal)
assertEquals("z", (doc).signal)
assertEquals("d", (device).signal)
assertEquals("f", (face).signal)
}

@Test
fun `identityCheck helper exposes canonical preset`() {
val attributes = listOf(
IdentityAttribute.MinimumAge(21u),
IdentityAttribute.Nationality("JPN"),
IdentityAttribute.DocumentType(DocumentType.PASSPORT),
)

val preset = identityCheck(attributes = attributes)

assertTrue(preset is Preset.IdentityCheck)
assertEquals(attributes, preset.attributes)
}

@Test
Expand Down
7 changes: 7 additions & 0 deletions scripts/build-kotlin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ esac

HOST_LIB="$PROJECT_ROOT/target/release/libidkit.$LIB_EXT"

echo "🎯 Installing Android Rust targets"
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android >/dev/null

echo "🔧 Building Rust library (host) for binding generation"
CARGO_PROFILE_RELEASE_STRIP=none cargo build --package idkit-core --release --locked --features uniffi-bindings

Expand All @@ -32,8 +35,12 @@ CARGO_PROFILE_RELEASE_STRIP=none cargo run -p uniffi-bindgen generate \

if [ -n "${CI:-}" ]; then
echo "🧹 Cleaning host build artifacts to free disk space for Android builds"
# Preserve the host library — JVM unit tests need it via jna.library.path
cp "$HOST_LIB" "/tmp/libidkit.$LIB_EXT"
cargo clean --package idkit-core --release || true
rm -rf ~/.cargo/registry/cache || true
mkdir -p "$(dirname "$HOST_LIB")"
mv "/tmp/libidkit.$LIB_EXT" "$HOST_LIB"
fi

echo "🤖 Building Android ABIs"
Expand Down
Loading