Skip to content
github-actions[bot] edited this page Oct 1, 2026 · 5 revisions

KMP Supabase Auth

Supabase authentication for Kotlin Multiplatform — native Google sign-in through the Android Credential Manager, native Apple sign-in through ASAuthorization on iOS, anonymous sessions with an id-preserving upgrade, and a web-OAuth fallback everywhere else.

The public API is entirely commonMain. No consumer writes a platform-conditional import to make sign-in work.

Set supabaseAuthVersion to the version shown by the Maven Central badge above — that badge is read live from the registry and is always the latest published release.

repositories {
    mavenCentral()
    google() // see the note below — required only for cmp-supabase-auth-compose
}

val supabaseAuthVersion = "<see the Maven Central badge>"

dependencies {
    implementation("io.github.mobilebytelabs:cmp-supabase-auth:$supabaseAuthVersion")  // headless
    implementation("io.github.mobilebytelabs:cmp-supabase-auth-compose:$supabaseAuthVersion")  // Compose UI (optional)
}

Why google()? cmp-supabase-auth-compose pulls in Compose Multiplatform, whose transitive AndroidX dependencies (androidx.savedstate, androidx.lifecycle-*) are published to Google's Maven repository and are not mirrored to Maven Central. Verified by resolving both modules against Central alone: the build fails on those transitive artifacts, not on this library's own.

cmp-supabase-auth alone needs only mavenCentral(). If you are not using the Compose bindings, you do not need google() for this library at all.

val AppModule = module {
    includes(
        kmpSupabaseAuth(KmpSupabaseAuthConfig(projectRef = "…", googleWebClientId = "…")),
        kmpSupabaseAuthComposeModule(),
    )
}

KmpSupabaseLoginScreen(
    viewModel = koinViewModel(),
    client = koinInject(),
    onSignedIn = { navigateHome() },
)

Install & wire up →


Why this exists

Four apps in one workspace had each built their own Supabase sign-in. One of them worked. The others were a provider interface wired to nothing, a REST client with the native token hardcoded to "", and a Supabase client with no auth at all. A fifth shipped an Apple button on Android that opened a browser it could never return from.

Auth is the worst thing to re-implement per app: a bug in it is a security bug, and a fix that only reaches the next project is close to worthless.

What it gives you

Native sign-in Credential Manager (Android) · ASAuthorization (iOS) · OAuth redirect elsewhere — the plugin picks per platform
Guest sessions signInAnonymously() with RLS from the first write; linkIdentity upgrades without changing the user id
Layered DI One includes(kmpSupabaseAuth(config)), or one line per rung across core/network → core/store → core/data
Drop-in UI Slot-based KmpSupabaseLoginScreen, brand-compliant provider buttons, a session-driven ViewModel
Testability FakeKmpSupabaseAuthRepository ships in the main artifact

Three things worth knowing before you build on it

It never creates a Supabase client. It installs Auth and ComposeAuth onto the one your app already has. A second client carries no session, so every RLS-gated call resolves no auth.uid() — while compiling cleanly and passing static checks the whole way.

Signed-in comes from the session, never the button callback. On Android the native Google onResult(Success) callback frequently never fires even though the session landed. Observed on-device: the app sat on "Signing in…" while Supabase logged Authenticated.

The library owns the session; your app keeps owning the user. Profile data stays in your store, untouched. KmpSupabaseAuthUser carries identity only.

Read the architecture →

Platform support

Android iOS Desktop Web macOS
Google native native redirect redirect redirect
Apple redirect native redirect redirect redirect
Anonymous ✅ ✅ ✅ ✅ ✅

macOS gets no native Apple sign-in — compose-auth publishes no macOS artifact. Measured, not an oversight; see the target matrix.

Built on

This library adds what those do not: DI, a provider-neutral domain surface, the session-truth ViewModel, the token bridge, and the Android OAuth callback receiver.

Clone this wiki locally