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 SDKThe 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.
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 configPass 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.