🎉 Native Google & Apple sign-in is here → read the guide
AuthenticationNative Sign-In

Native Sign-In (Google & Apple)

“Native” sign-in is the polished account picker built into the phone — the Google sheet that slides up on Android, the Sign in with Apple dialog on iOS — instead of bouncing the user out to a web browser. It feels faster and more trustworthy, so users complete it more often.

Under the hood every native flow produces an OIDC ID token (a signed proof of who the user is) — which signInWithIdToken already exchanges for a session. So native sign-in is not a new login method; it’s the platform glue that gets a native token to the method you already have.

[provider's native UI]  →  NativeAuthCredential  →  auth.signInWith(provider)  →  Session
        ↑ per-module, opt-in              ↑ core contract              ↑ already in the SDK

The optional supabase-auth-google and supabase-auth-apple modules wrap the platform UIs and hand you a NativeAuthProvider. The core never depends on a provider SDK you didn’t add.

libs.versions.toml
supabase-auth-google = { module = "io.github.androidpoet:supabase-auth-google", version.ref = "supabase-kmp" }
supabase-auth-apple = { module = "io.github.androidpoet:supabase-auth-apple", version.ref = "supabase-kmp" }

The nonce

Both providers take a nonce — a fresh random value, unique per sign-in, that binds the issued ID token to this request so a captured token can’t be replayed. Don’t hand-roll it from kotlin.random.Random; use the SDK’s generateNonce(), which draws from the same cryptographically secure RNG as the PKCE verifiers and is URL-safe:

import io.github.androidpoet.supabase.auth.native.generateNonce
 
val rawNonce = generateNonce()   // fresh per sign-in; pass the raw value to the config

Pass the raw nonce to the provider config below. The provider hashes it for the platform and returns the raw value on the NativeAuthCredential, so Supabase can validate the token’s hashed nonce claim.

Google (Android)

Backed by Android’s Credential Manager + Google Identity Services. Construct the provider in androidMain (it needs an Activity context):

val provider = googleAuthProvider(
    context = activity,
    config = GoogleSignInConfig(
        serverClientId = "<web-client-id>",
        nonce = rawNonce,
    ),
)
 
val session = auth.signInWith(provider) // → SupabaseResult<Session>

Apple (iOS & macOS)

Backed by AuthenticationServices (pod-free). Construct it in iosMain / macosMain:

val session = auth.signInWith(
    appleAuthProvider(AppleSignInConfig(nonce = rawNonce)),
)

Apple returns the user’s full name and email only on the very first authorization for your app, and never inside the ID token. The provider carries them through on NativeAuthCredential.fullName / .email so you can capture them then — read and store them on first sign-in, because subsequent sign-ins won’t include them.

A cancelled Apple dialog is reported distinctly: the failure carries code = "apple_sign_in_cancelled" (vs. "apple_sign_in_failed" for a real error), so you can quietly ignore a deliberate dismiss instead of showing an error.

Bring your own

NativeAuthProvider is just an interface — implement it for any provider, platform, or SDK and hand it to the same signInWith:

class MyProvider : NativeAuthProvider {
    override val provider = OAuthProvider.GITHUB
    override suspend fun signIn(): SupabaseResult<NativeAuthCredential> =
        SupabaseResult.Success(
            NativeAuthCredential(provider = OAuthProvider.GITHUB, idToken = myIdToken),
        )
}

On platforms without a bundled provider, use the redirect flow (signInWithOAuth) or supply your own NativeAuthProvider.

Want passwordless biometric sign-in instead? See Passkeys.