Skip to main content
Back to Android

SignIn

The SignIn composable renders a full app-native sign-in form. It drives the Flow Execution API loop automatically: initiating the flow, presenting the server-defined inputs and actions on each step, and completing when the user is authenticated.

SignIn requires ThunderIDProvider in its ancestor composable hierarchy.

Usage​

kotlin
import dev.thunderid.compose.components.presentation.auth.SignIn

@Composable
fun AuthView(applicationId: String) {
SignIn(
applicationId = applicationId,
modifier = Modifier.fillMaxWidth().padding(16.dp)
)
}

Parameters​

ParameterTypeRequiredDescription
applicationIdString✅The Application ID from your ThunderID application settings. Identifies which sign-in flow to execute.
modifierModifier❌Compose modifier applied to the form container.
onComplete(() -> Unit)?❌Called when authentication completes successfully.
onError((String) -> Unit)?❌Called with an error message when a flow step fails.

Handling Completion​

Use onComplete to navigate away from the sign-in screen after a successful sign-in. LocalThunderID.current.user is updated automatically before this callback fires.

kotlin
@Composable
fun AuthScreen(applicationId: String) {
SignIn(
applicationId = applicationId,
onComplete = {
// thunder.isSignedIn is now true; navigate to your home screen
},
onError = { errorMessage ->
println("Sign-in error: $errorMessage")
}
)
}

Setting Up Federated Sign-In​

When a sign-in step offers a federated or social login option, SignIn opens the identity provider in a Custom Tab through FederatedAuthSession and waits for the provider to redirect back to your app. Your host Activity must deliver that redirect to the SDK. Set up the following four pieces in the Activity that hosts SignIn.

Register the Redirect URI​

Add an intent filter with the BROWSABLE category for the scheme of your redirect URI. Without it, the browser has no app to hand the redirect to, so the user stays in the browser after signing in with the provider.

xml
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="dev.thunderid.quickstart" />
</intent-filter>
</activity>

Replace dev.thunderid.quickstart with the scheme of your redirect URI.

Set the Launch Mode to singleTask​

Set android:launchMode="singleTask" on the Activity, as shown in the manifest above. The Custom Tab runs in your app's task on top of the Activity. With standard or singleTop, the redirect starts a second instance of the Activity, which has no pending sign-in, and the original sign-in form never receives the result. singleTask closes the Custom Tab and delivers the redirect to the running Activity's onNewIntent.

Forward the Redirect from onNewIntent​

Override onNewIntent and pass the redirect URI to FederatedAuthSession.onRedirect. Without this call, the SDK never receives the provider's response, so SignIn keeps waiting after the browser closes.

kotlin
import android.content.Intent
import dev.thunderid.android.auth.FederatedAuthSession

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let(FederatedAuthSession::onRedirect)
}

Cancel a Dismissed Sign-In from onResume​

Call FederatedAuthSession.cancelIfPending() from onResume. Android delivers a redirect to onNewIntent before onResume runs, so a sign-in that is still pending at this point means the user closed the browser without finishing. Cancelling it returns the form to the same step with no error, so the user can pick an option again. Without this call, the sign-in form keeps showing its loading indicator indefinitely after the user dismisses the browser.

kotlin
override fun onResume() {
super.onResume()
FederatedAuthSession.cancelIfPending()
}

Customization with BaseSignIn​

BaseSignIn is the unstyled builder variant. It manages the Flow Execution loop and passes a SignInState object to your content lambda, giving you full control over the form's appearance.

kotlin
import dev.thunderid.compose.components.presentation.auth.BaseSignIn

@Composable
fun CustomSignInView(applicationId: String) {
BaseSignIn(applicationId = applicationId) { signInState ->
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
signInState.error?.let { error ->
Text(error, color = MaterialTheme.colorScheme.error)
}

signInState.inputs.forEach { input ->
if (input.type == "PASSWORD_INPUT") {
var value by signInState.binding(input.name)
SecureField(value, onValueChange = { value = it })
} else {
var value by signInState.binding(input.name)
OutlinedTextField(value, onValueChange = { value = it }, label = { Text(input.name) })
}
}

signInState.actions.forEach { action ->
Button(
onClick = { signInState.submit(actionId = action.id) },
enabled = !signInState.isLoading
) {
Text(action.label ?: "Submit")
}
}
}
}
}

SignInState Properties​

SignInState is passed to the BaseSignIn content lambda and exposes the current flow step.

PropertyTypeDescription
inputsList<FlowInput>Input fields to render for the current step. Each has a name, type, and required flag.
actionsList<FlowAction>Submit buttons to render. Each has an id and an optional label.
isLoadingBooleantrue while a network request is in progress.
errorString?Error message from the last failed step. null on success.

SignInState Methods​

MethodDescription
binding(name)Returns a MutableState<String> for the named input field.
submit(actionId)Submits the current field values with the given action ID.

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.