Skip to content

getting started

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

Install & wire up

1. Add the dependencies

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.

The Compose module brings the headless one transitively (api), so a UI app can take just it.

2. Configure

val authConfig = KmpSupabaseAuthConfig(
    projectRef        = "your-project-ref",
    googleWebClientId = BuildKonfig.GOOGLE_OAUTH_WEB_CLIENT_ID,
    redirectUrl       = "myapp://login-callback",
)

Use the Google Cloud Web client id — not the Android or iOS one. Both GoTrue and the Android Credential Manager verify against the Web id, and supplying the Android id fails at runtime with an opaque provider error. See Google Sign-In.

Leaving a provider id blank is safe: the library skips that native flow and falls back to the OAuth redirect. Only a blank or placeholder projectRef is fatal — without it there is no client to install onto.

3. Install the plugins on your existing client

The library never calls createSupabaseClient. It installs onto the client your app already has — in a kmp-project-template fork, the one your Supabase training layer codegens into core/network.

// core/network/di/ProjectNetworkModule.kt  — owner:fork, survives template sync
single<SupabaseExtrasProvider> {
    SupabaseExtrasProvider { id ->
        when (id) {
            authConfig.projectRef -> {
                {
                    kmpSupabaseAuthExtras(authConfig)(this)         // Auth
                    kmpSupabaseComposeAuthExtras(authConfig)(this)  // ComposeAuth (native sign-in)
                }
            }
            else -> { {} }
        }
    }
}

?> Two blocks because compose-auth is a Compose artifact. Keeping it out of the headless module is what lets cmp-supabase-auth reach 17 targets instead of 7 — see Target matrix.

4. Wire the DI

One line:

includes(kmpSupabaseAuth(authConfig))

…or one per layer, if you prefer each rung where it belongs:

includes(kmpSupabaseAuthNetwork(authConfig))  // core/network
includes(kmpSupabaseAuthStore())              // core/store
includes(kmpSupabaseAuthRepository())         // core/data
includes(kmpSupabaseAuthComposeModule())      // feature

kmpSupabaseAuth(config) is defined as the first three, and a test asserts the binding sets are identical — the two forms cannot drift apart.

On kmp-project-template, the client comes from a factory rather than a direct binding, so pass the lookup:

includes(
    kmpSupabaseAuth(authConfig) {
        get<SupabaseClientFactory>().requireClientFor(authConfig.projectRef).client
    },
)

5. Bridge the token to your API calls

This is the step that silently breaks RLS if skipped. The library exposes the JWT; the template's existing AuthHeaderBridge does the rest:

single<AuthTokenSource> {
    val repository = get<KmpSupabaseAuthRepository>()
    AuthTokenSource { _ -> repository.accessTokenFlow }
}

6. Show the screen

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

Or compose your own from the parts:

val google = rememberKmpSupabaseGoogleSignIn(client, onError = viewModel::onSignInFailed)
KmpSupabaseGoogleSignInButton(onClick = { viewModel.onSignInStarted(); google.launch() })
KmpSupabaseContinueAsGuestButton(onClick = viewModel::continueAsGuest)

!> Never treat the provider callback as success. On Android NativeSignInResult.Success frequently never fires even though the session landed. Observe KmpSupabaseAuthRepository.isSignedIn instead — KmpSupabaseAuthViewModel already does.

7. Guest sessions

viewModel.continueAsGuest()                       // anonymous session, RLS applies immediately
rememberKmpSupabaseGoogleSignIn(client, linkIdentity = true) // upgrade, KEEPING the user id

linkIdentity preserves the id, so no guest data has to be migrated. It works on the same install; a guest signing in on a second device gets a different id.

Next