Skip to content

feat: kotlin/android sdk to kmp - #299

Open
jaidensiu wants to merge 2 commits into
mainfrom
jaiden/migrate-kt-kmp
Open

feat: kotlin/android sdk to kmp#299
jaidensiu wants to merge 2 commits into
mainfrom
jaiden/migrate-kt-kmp

Conversation

@jaidensiu

@jaidensiu jaidensiu commented Jul 9, 2026

Copy link
Copy Markdown

Migrate Kotlin/Android SDK to Kotlin Multiplatform (KMP)

Replaces the Android-only, UniFFI-generated Kotlin bindings w/ KMP SDK targeting Android and iOS from a single common API.

Changes

  • New rust/kmp-ffi crate exposing a stable C ABI (idkit_kmp.h) w/ handle-based session registry, JSON envelope layer for config/results, and FFI contract tests
  • Minor changes to core bridge to support new FFI surface
  • New kotlin/idkit KMP module w/ a common API with native bridges for Android (JNI) and iOS (cinterop)
  • New IDKitKmpSampleApp with shared business logic and native UI (Compose/SwiftUI); legacy Android example udpated to the new SDK
  • Updated build-kotlin.sh / package-kotlin.sh, CI, and publishing jobs for new artifacts

Note

High Risk
Large release and architecture shift: new FFI boundary, native artifacts per platform, macOS-gated publishing, and breaking Kotlin SDK API/artifact layout for all Android integrators.

Overview
Replaces the Android-only UniFFI/JNA kotlin/bindings module with a Kotlin Multiplatform kotlin/idkit SDK (Android + iOS) backed by a new hand-written C FFI instead of generated UniFFI Kotlin.

The Rust workspace gains idkit-kmp-ffi (libidkit_kmp) with JSON request/response envelopes, opaque request handles, and a dedicated kmp-android-release profile (panic = unwind) so FFI can turn panics into errors instead of aborting Android apps. The public Kotlin API stays under com.worldcoin.idkit but 5.0.0 breaks several behaviors (suspend preset/constraints, IDKitRequest.close(), types moved off uniffi.*, IDKitException, constraint/preset modeling, etc.) as documented in kotlin/README.md.

CI and release plumbing target the new artifacts: Linux CI builds/tests Android + validates the Android Maven slice (libidkit_kmp.so in the AAR); a new macOS kotlin-ios job runs simulator tests and rehearses full KMP publishToMavenLocal. publish-kotlin drops the UniFFI host/bindgen job, cross-builds idkit-kmp-ffi for Android ABIs, builds iOS static libs on the publish runner, and moves publish to macos-latest with ordered GitHub Packages uploads (platform modules first, root metadata last).

Adds IDKitKmpSampleApp (shared flow + Compose/SwiftUI) and retargets IDKitSampleApp to :idkit. Removes the old bindings sources/tests from the tree in favor of the new module.

Reviewed by Cursor Bugbot for commit 7a37dec. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
idkit-js-example Ready Ready Preview, Comment Jul 9, 2026 10:14pm

Request Review

@jaidensiu jaidensiu changed the title Jaiden/migrate kt kmp feat: kotlin/android sdk to kmp Jul 9, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7a37dec. Configure here.

?: throw IDKitException("unexpected_response", "confirmed status is missing its result")
IDKitStatus.Confirmed(
IDKitResult.fromJson(IdKitJson.encodeToString(JsonObject.serializer(), result)),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Confirmed rawJson not verbatim

Medium Severity

When mapping a confirmed poll status, IDKitResult is built by re-encoding the parsed JsonObject through IdKitJson instead of keeping the Rust wire JSON. That conflicts with rawJson being documented as untouched for backend verify, and IdKitJson settings can omit nulls and change serialization so POST bodies may not match what the core emitted.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7a37dec. Configure here.

pendingRequest?.close()
http.close()
scope.cancel()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Close before polling stops

Medium Severity

Lifecycle and regenerate paths call IDKitRequest.close() while a polling coroutine may still be in statusFlow or pollStatusOnce. The KMP sample’s generateRequest correctly uses cancelAndJoin before close, but dispose closes the request first and only then cancels the scope, and the Android sample cancels the job without joining before closing the prior request.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7a37dec. Configure here.

- name: Build Kotlin artifact
uses: gradle/gradle-build-action@v3
- name: Setup Android SDK
uses: android-actions/setup-android@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

GitHub Action android-actions/setup-android@v4 is not pinned to a commit SHA, allowing attackers who compromise the repository to inject backdoor code into your build and published artifacts.

More details about this

The GitHub Action android-actions/setup-android is pinned to a version tag (@v4) instead of a full commit SHA. Version tags in GitHub repositories can be reassigned or deleted by the maintainer, and a malicious actor who compromises the android-actions repository could push code to the v4 tag, causing your workflow to execute their backdoor code.

Attack scenario:

  1. An attacker compromises the android-actions/setup-android repository through credential theft or social engineering
  2. They force-push new malicious code to the v4 tag, overwriting the legitimate version
  3. Your workflow runs and fetches the v4 tag, which now points to the attacker's code instead of the original
  4. The setup-android action runs their payload, potentially stealing repository secrets (like MAVEN_CENTRAL_PASSWORD and MAVEN_SIGNING_KEY visible in your workflow), exfiltrating source code, or injecting malware into your build artifacts
  5. Your published Kotlin artifacts get distributed to Maven Central with embedded backdoor code, affecting all downstream users

To resolve this comment:

✨ Commit fix suggestion
  1. Replace the tag reference in the uses line with the action's full 40-character commit SHA instead of @v4.
    Change uses: android-actions/setup-android@v4 to uses: android-actions/setup-android@<full-commit-sha> # v4.

  2. Keep the version as an inline comment after the SHA, for example # v4, so it is still clear which release you intended to use.

  3. Choose the SHA from the exact release you want to trust on the android-actions/setup-android repository's release or tag page, and copy the full commit hash for that version.
    Pinning to the commit SHA makes the workflow use an immutable action revision instead of a movable tag.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.

You can view more details about this finding in the Semgrep AppSec Platform.

Comment thread .github/workflows/ci.yml
packages: tools platform-tools platforms;android-35 build-tools;35.0.0

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

GitHub Action Swatinem/rust-cache@v2 is not pinned to a commit SHA, allowing an attacker with repository write access to inject malicious code that executes in your workflow.

More details about this

The GitHub Action Swatinem/rust-cache@v2 is pinned to a version tag rather than a full commit SHA. An attacker with write access to the rust-cache repository could force-push a malicious commit to the v2 tag, and your workflow would automatically pull and execute the compromised code on the next run without any warning.

Here's how an attacker could exploit this:

  1. The attacker gains write access to the Swatinem/rust-cache repository (e.g., through a compromised account or insider threat)
  2. They create a malicious version of rust-cache that exfiltrates secrets or corrupts build artifacts
  3. They force-push this malicious code to the v2 tag, overwriting the old commit
  4. On your next workflow run, GitHub checks out the code at the v2 tag and executes the attacker's malicious version
  5. The attacker could then steal your GITHUB_TOKEN, AWS credentials, or other secrets available in the workflow environment, or inject a backdoor into your Kotlin SDK binaries

By pinning to a full commit SHA (e.g., Swatinem/rust-cache@a1234567890abcdef1234567890abcdef12345678), you ensure that only that exact commit is ever executed, making tag tampering impossible.

To resolve this comment:

✨ Commit fix suggestion
  1. Replace uses: Swatinem/rust-cache@v2 with a full 40-character commit SHA for the exact action release you want to keep using, for example uses: Swatinem/rust-cache@<full-commit-sha>.
  2. Add the tag or version as a comment after the SHA so the workflow stays readable, for example uses: Swatinem/rust-cache@<full-commit-sha> # v2.x.y.
  3. Get the SHA from the action's GitHub release or tag page and make sure it matches the v2 release you intend to use. This makes the workflow use an immutable action version instead of a moving tag.
  4. Keep the existing with: block unchanged unless the pinned release notes require an input change.

Alternatively, if you need to stay on the latest v2 behavior automatically, replace this third-party action with a local action such as uses: ./.github/actions/rust-cache that you control directly.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.

You can view more details about this finding in the Semgrep AppSec Platform.

Comment thread .github/workflows/ci.yml
java-version: "17"

- name: Setup Android SDK
uses: android-actions/setup-android@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

GitHub Actions workflow uses a mutable version tag (@v4) for the android-actions/setup-android action instead of pinning to a specific commit SHA, allowing attackers to inject malicious code into CI/CD runs.

More details about this

The android-actions/setup-android action is referenced using a mutable version tag (@v4) instead of being pinned to a specific commit SHA. This means the action can change at any time without your knowledge or control.

Exploit scenario: An attacker could compromise the android-actions/setup-android repository and push malicious code to the v4 tag. The next time this workflow runs, it would automatically execute the backdoored action. With this step running in your CI/CD pipeline, the attacker could:

  1. Inject malicious code into your build artifacts (the Android SDK setup)
  2. Steal secrets from the workflow environment (like signing keys or credentials used during the build)
  3. Compromise your build outputs before they're published

Since this action runs early in your workflow (Setup Android SDK step) and has elevated access to your repository context, an attacker could compromise your entire Kotlin SDK build process.

To resolve this comment:

✨ Commit fix suggestion
  1. Replace the tag-based action reference with a full 40-character commit SHA in the uses line for android-actions/setup-android.
  2. Keep the readable version as a comment after the SHA so future updates are easier to track, for example: uses: android-actions/setup-android@<full-commit-sha> # v4.
  3. Verify that the pinned commit corresponds to the exact action release you want to keep using, instead of pinning a moving tag like @v4. Pinning to a commit makes the action immutable, so the workflow cannot silently pick up changed code later.
  4. Leave the rest of the step unchanged, including the existing with: configuration for packages.

Alternatively, if you need to avoid depending on a third-party action here, replace android-actions/setup-android with a trusted local action such as ./.github/actions/... or a GitHub-owned action that provides the same setup behavior.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.

You can view more details about this finding in the Semgrep AppSec Platform.

Comment thread .github/workflows/ci.yml
run: echo "toolchain=$(yq '.toolchain.channel' rust-toolchain.toml)" >> $GITHUB_OUTPUT

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

GitHub Action not pinned to commit SHA; @stable tag can be updated with malicious code, compromising your workflow at runtime.

More details about this

The GitHub Action dtolnay/rust-toolchain@stable is pinned to the stable tag rather than a specific commit SHA. This means the action can change at any time when the tag is updated.

Attack scenario:

  1. An attacker compromises the dtolnay/rust-toolchain repository or performs a man-in-the-middle attack
  2. They push a malicious version of the action to the stable tag—for example, injecting code that steals environment secrets or injects a backdoor into your build artifacts
  3. The next time your workflow runs, it automatically pulls the compromised stable version since the action is not pinned to a specific commit
  4. The malicious code executes with the permissions of your GitHub Actions runner, which in this case has contents: read access, allowing it to read your repository code and potentially exfiltrate sensitive data

By pinning to a full commit SHA (e.g., dtolnay/rust-toolchain@a6e1e2f3c4b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9), you ensure that only that exact version of the action runs, making it immutable and preventing this type of supply-chain attack.

To resolve this comment:

✨ Commit fix suggestion
  1. Replace the floating action reference dtolnay/rust-toolchain@stable with a full 40-character commit SHA for the exact action release you want to use, for example dtolnay/rust-toolchain@<full-commit-sha>.
  2. Keep the intended version visible by adding a comment after the SHA, such as uses: dtolnay/rust-toolchain@<full-commit-sha> # stable or # v1.x.y, so future updates are easier to manage.
  3. Leave the with: block unchanged so the workflow still installs the toolchain from ${{ steps.rust-version.outputs.toolchain }}. Pinning the action version makes the workflow immutable, while the toolchain: input can still select the Rust version to install.

Alternatively, if you need the workflow to track a newer release of this action, update it to that release first, then pin that release's commit SHA instead of using a tag like @stable.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.

You can view more details about this finding in the Semgrep AppSec Platform.

Comment on lines +3 to +37
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>IDKit KMP Sample</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>org.worldcoin.idkit.kmp.sample.callback</string>
<key>CFBundleURLSchemes</key>
<array>
<string>idkitkmpsample</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>UILaunchScreen</key>
<dict/>
</dict>
</plist>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:
The application's App Transport Security (ATS) configuration does not leverage the in-built public key pinning mechanisms. The application should consider leverage ATS public key pinning to ensure that the application only communicates to serves with an allow-listed certificate (and public key). By default the device will allow connections if the default trust store (CA store) posesses the right certificates. The number of accepted Certificate Authorities by default is hundreds. Using public key pinning vastly reduces the attack surface.

To resolve this comment:

🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by ATS-consider-pinning.

You can view more details about this finding in the Semgrep AppSec Platform.

@semgrep-code-worldcoin

Copy link
Copy Markdown

Semgrep found 6 github-actions-mutable-action-tag findings:

GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608.

Comment thread kotlin/Examples/IDKitKmpSampleApp/shared/build.gradle.kts
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.

2 participants