WebAuthn Kotlin Multiplatform
Standards-first Kotlin Multiplatform building blocks for WebAuthn and passkey integrations.
This project helps teams implement passwordless login without rebuilding the hardest parts from scratch. It gives you typed protocol models, strict validation, backend ceremony services, platform passkey clients, and optional transport/adaptation modules that stay close to the WebAuthn specification.
Start with the mobile-first public documentation for
Android, iOS, Compose, full-stack examples, and generated API reference entry points.
Why This Project Exists
- WebAuthn is security-sensitive and protocol-heavy.
- Passkey products often need to share logic across backend, Android, and iOS.
- Kotlin teams usually want typed APIs, predictable validation, and flexible integration points instead of one monolithic SDK.
This repo focuses on those needs:
- Standards first: behavior is driven by WebAuthn L3 and related RFCs.
- Kotlin-first: KMP modules share the right logic instead of pushing everything into platform wrappers.
- Flexible integration: use only the modules you need, from pure model/validation all the way to Ktor routes and client transport helpers.
- Heavy lifting included: challenge/origin validation, authenticator-data parsing, signature verification boundaries, attestation policy hooks, and platform bridge logic are already here.
What You Can Build With It
- A JVM/Ktor WebAuthn backend using typed ceremony services.
- Android and iOS passkey clients with shared Kotlin orchestration.
- A client/server setup that shares model and validation semantics instead of duplicating protocol assumptions.
- A modular stack where server, client, transport, storage, and attestation trust can be adopted separately.
Sample Recordings
WebAuthn Core Concepts
WebAuthn has two ceremony pairs:
- Registration (
create)
- Authentication (
get)
Each pair has a server start step and a server finish step, with the platform authenticator in the middle.
sequenceDiagram
autonumber
actor User
participant App as Client App
participant Auth as Platform Authenticator
participant RP as Relying Party Server
note over RP,App: Registration ceremony
App->>RP: registration/start request
RP-->>App: registration/start response (challenge + options)
App->>Auth: navigator.credentials.create / platform create
Auth-->>App: RegistrationResponse
App->>RP: registration/finish (credential response)
RP-->>App: verified registration
note over RP,App: Authentication ceremony
App->>RP: authentication/start request
RP-->>App: authentication/start response (challenge + options)
App->>Auth: navigator.credentials.get / platform get
Auth-->>App: AuthenticationResponse
App->>RP: authentication/finish (credential response)
RP-->>App: verified sign-in
The finish payload carries each credential response once. The server derives ceremony type, challenge,
and origin from its signed clientDataJSON; clients must not echo those values as independent claims.
Validation and trust decisions are server responsibilities: challenge/origin/type checks, authenticator
data rules, signature/attestation verification, counter handling, and policy decisions.
Repository structure
The repository follows a layered model that keeps protocol and validation concerns separate from transport and platform adapters.
flowchart TB
CLIENT["Client stack<br/>Shared orchestration and platform bridges"]
SERVER["JVM server stack<br/>Ceremonies, storage and HTTP adapters"]
CRYPTO["Cryptography boundary<br/>Crypto contracts and implementations"]
FOUNDATION["Shared foundation<br/>Validation, serialization and runtime"]
MODEL["Protocol model<br/>Typed WebAuthn contracts"]
CLIENT --> FOUNDATION
CLIENT --> MODEL
SERVER --> FOUNDATION
SERVER --> CRYPTO
CRYPTO --> FOUNDATION
FOUNDATION --> MODEL
The overview shows logical responsibility areas rather than every Gradle
dependency. See the architecture guide for the
reference integration and focused core, client, and server dependency views.
Repository areas
core/ contains reusable protocol, validation, runtime, serialization, and crypto contracts.
client/ contains typed platform operations, generic ceremony flow, platform bridges, Compose helpers, and client transport.
server/ contains JVM server services, Ktor/store adapters, JVM crypto, and optional trust metadata.
- contains runnable samples and demo entry points; these modules are not published.
Common entry points
How To Read Module Docs
Most module READMEs follow this baseline structure (adapted per module when needed):
What it provides: the module's owned responsibilities.
When to use: where it belongs in an integration.
How to use: practical API snippets plus required caller responsibilities.
How it fits in the system: dependency and data-flow context.
Recommended adoption paths:
- Start server-side with
model -> core -> crypto-api -> server-core-jvm (+ server-ktor if you want HTTP adapters).
- Start client-side with
client-core -> client-flow -> platform bridge (+ client-compose for Compose UI).
- Add
client-prf-crypto only when you need PRF-derived application crypto.
Install
The coordinated release train uses one version for the full published surface. JVM and Android dependency
configurations can use the BOM; Kotlin Multiplatform common and Native source sets should put that same
version on each artifact because Java Platform constraints are not available to Native variants.
repositories {
google()
mavenCentral()
}
Use only the modules your app actually wires in. In Kotlin Multiplatform projects, shared modules belong in commonMain, while concrete platform bridges belong in the matching platform source set.
Recommended client setup
For the default Kotlinx backend contract and recommended Android/iOS platform composition, use
webauthn-client-flow plus webauthn-client-ktor-kotlinx in common code and
webauthn-client-defaults in each platform source set:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.szijpeter:webauthn-client-flow:<version>")
implementation("io.github.szijpeter:webauthn-client-ktor-kotlinx:<version>")
}
androidMain.dependencies {
implementation("io.github.szijpeter:webauthn-client-defaults:<version>")
}
iosMain.dependencies {
implementation("io.github.szijpeter:webauthn-client-defaults:<version>")
}
}
}
The app still creates its own Ktor HttpClient and engine. The defaults artifact selects the
Android JSON implementation and platform construction only; PasskeyFlow leaves presentation state
and backend exception policy application-owned.
Android hosts must also add a Credential Manager provider such as
androidx.credentials:credentials-play-services-auth; the WebAuthn client modules provide the API
bridge but deliberately leave provider-runtime selection to the application.
Compose your stack
Use the lower-level modules when you supply your own WebAuthnJsonCodec, Ktor contract codec, or
platform construction. This dependency-pure consumer fixture deliberately does not resolve
webauthn-json-kotlinx through the neutral client modules:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.szijpeter:webauthn-client-core:<version>")
implementation("io.github.szijpeter:webauthn-client-json-core:<version>")
implementation("io.github.szijpeter:webauthn-client-flow:<version>")
implementation("io.github.szijpeter:webauthn-client-ktor:<version>")
implementation("io.github.szijpeter:webauthn-json-api:<version>")
}
androidMain.dependencies {
implementation("io.github.szijpeter:webauthn-client-platform:<version>")
}
iosMain.dependencies {
implementation("io.github.szijpeter:webauthn-client-platform:<version>")
}
}
}
JVM/Ktor server example:
dependencies {
implementation(platform("io.github.szijpeter:webauthn-bom:<version>"))
implementation("io.github.szijpeter:webauthn-server-core-jvm")
implementation("io.github.szijpeter:webauthn-server-jvm-crypto")
implementation("io.github.szijpeter:webauthn-server-ktor")
implementation("io.github.szijpeter:webauthn-server-store-exposed")
}
Composition notes:
- Client apps do not need dependencies.
Published to Maven Central (latest version is shown in the Maven Central badge above). Maintainers can still validate publication locally with:
./gradlew publishToMavenLocal --stacktrace
Quick Start Paths
Server-first
Use:
Client-first
Use:
End-to-end reference app
Start with:
Desktop and CLI strategy notes for this repo live in docs/DESKTOP_CLI_STRATEGY.md.
Public Modules
Status and Current Limits
This repository is publicly released and still pre-1.0.
Current state:
- Core/server validation paths are production-leaning.
- Publish/release infrastructure is now wired for Maven Central and compatibility baselines.
- Client flows are usable on Android and iOS with generic
PasskeyFlow orchestration and raw platform responses.
- iOS external security-key support is still being hardened before it can be documented as fully ready.
Security and Release Hygiene
- Vulnerability reporting: see
SECURITY.md.
- Public-launch checklist:
docs/PUBLIC_LAUNCH_CHECKLIST.md.
- Maven Central maintainer guide: .
Maintainer Workflow
tools/agent/setup-hooks.sh
tools/agent/quality-gate.sh --mode fast --scope changed --block false
tools/agent/quality-gate.sh --mode strict --scope changed --block false
./gradlew apiCheck --stacktrace
./gradlew publishToMavenLocal --stacktrace
Related Docs
License: Apache-2.0. See LICENSE.