ChangeCredential
The ChangeCredential composable renders a form that lets a signed-in user set a new value for one of their own credentials. It collects only a new value and its confirmation, checks it against the applicable rules, and posts the change to ThunderID via ThunderIDClient.updateUserCredentials.
It reads the rules it needs from the user type schema (GET /users/me/meta), fetched once and shared with any other mounted component that needs it (for example UserProfile), so multiple components cost one request rather than one each. The default label and messages are built from the credential attribute's own displayName in that schema, so they always match whatever an admin named it there.
By default it manages the password credential. To manage a different one, for example a PIN declared on the user type schema, set attribute; render the composable once per credential to let a user manage more than one.
ChangeCredential requires ThunderIDProvider in its ancestor composable hierarchy.
ChangeCredential collects only a new value and its confirmation, not the account's existing value. The self-service credential write path does not verify the current value today, so asking for one would only teach the user a false sense of security.
Usage
Basic Usage
import dev.thunderid.compose.components.presentation.user.ChangeCredential
@Composable
fun SecuritySection() {
Column {
Text("Security")
ChangeCredential()
}
}
Managing a Different Credential
Render one instance per credential the user type schema declares, keyed by its attribute name:
import dev.thunderid.compose.components.presentation.user.ChangeCredential
@Composable
fun SecuritySection() {
Column {
ChangeCredential()
ChangeCredential(attribute = "pin")
}
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
modifier | Modifier | ❌ | Compose modifier applied to the form container. |
attribute | String | ❌ | The credential attribute this instance manages, any attribute the user type schema declares credential: true for. Defaults to "password". |
onSuccess | (() -> Unit)? | ❌ | Called after the credential has been changed successfully. |
The display name always comes from the schema's displayName for attribute (falling back to the title-cased attribute name when the schema declares none). Rename it from your ThunderID console rather than overriding it in code: ChangeCredential has no prop for that, so the schema stays the single source of truth.
Behavior
- Renders as a collapsed row, matching
UserProfile's field rows, that opens a full-screen editor on tap. - If the schema doesn't declare
attributeas a credential, the editor opens to a message telling the user changes are unavailable and to contact their administrator, instead of the form. - If the schema declares a
regexfor the attribute, the form enforces it and shows the schema'sdescriptionas the requirement hint, falling back to a generic message when the schema has none. - Closing the editor clears the entered values; a successful change also clears them and closes the editor automatically.
Customization with BaseChangeCredential
BaseChangeCredential is the unstyled builder variant. It manages the schema lookup, validation, and the network call, and passes the current state to your content lambda.
import dev.thunderid.compose.components.presentation.user.BaseChangeCredential
@Composable
fun CustomChangePin() {
BaseChangeCredential(
attribute = "pin",
onSuccess = { println("PIN updated") }
) { state ->
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
state.error?.let { Text(it, color = MaterialTheme.colorScheme.error) }
OutlinedTextField(
value = state.newValue,
onValueChange = { state.newValue = it },
label = { Text("New ${state.credentialDisplayName}") }
)
OutlinedTextField(
value = state.confirmValue,
onValueChange = { state.confirmValue = it },
label = { Text("Confirm New ${state.credentialDisplayName}") }
)
Button(
onClick = { state.submit() },
enabled = state.evaluation.isValid && !state.loading
) {
Text(if (state.loading) "Saving…" else "Save")
}
}
}
}
BaseChangeCredential Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
modifier | Modifier | ❌ | Compose modifier applied to the content container. |
attribute | String | ❌ | The credential attribute to manage. Defaults to "password". |
credentialDisplayName | String? | ❌ | Overrides the display name resolved from the schema. ChangeCredential doesn't expose this; it's an escape hatch for callers building their own UI without schema context. |
policyRegex | String? | ❌ | Overrides the schema-derived validation regex. |
onSuccess | (() -> Unit)? | ❌ | Called after a successful credential change. |
onError | (() -> Unit)? | ❌ | Called when a change fails. |
content | @Composable (ChangeCredentialState) -> Unit | ✅ | Renders the form from the current state. |
ChangeCredentialState Properties
| Property | Type | Description |
|---|---|---|
credentialDisplayName | String | The resolved display name for the credential. |
newValue | String | The new value entered so far. Mutable. |
confirmValue | String | The re-entered confirmation value. Mutable. |
error | String? | Form-level error from the last failed submission. |
loading | Boolean | true while a submission is in flight. |
success | Boolean | true after the last submission succeeded. |
unavailable | Boolean | true when the schema doesn't declare attribute as a credential. |
evaluation | CredentialFormEvaluation | Derived validation state: isValid, confirmMatches, meetsPolicy, patternChecked, patternPassed. |
fieldError(field) | (CredentialField) -> String? | The error for CredentialField.NEW or CredentialField.FORM, if any. |
submit() | () -> Unit | Submits the current values. No-ops while invalid, loading, or unavailable. |