diff --git a/docs/kratos/social-signin/90_data-mapping.mdx b/docs/kratos/social-signin/90_data-mapping.mdx
index 002766327..e4ac1fb0a 100644
--- a/docs/kratos/social-signin/90_data-mapping.mdx
+++ b/docs/kratos/social-signin/90_data-mapping.mdx
@@ -192,6 +192,108 @@ local claims = std.extVar('claims');
}
```
+### Update identity on login
+
+By default, the data mapper runs only during registration. If you want to keep identity data in sync with the upstream provider,
+set `update_identity_on_login` to `automatic` on the provider configuration. This re-runs the Jsonnet data mapper on every login
+and updates the identity's traits and metadata when they change.
+
+This is useful when the upstream identity provider is the source of truth for user attributes such as group memberships, roles, or
+profile information that can change over time.
+
+#### Configure in the Ory Console
+
+1. Go to .
+2. Select the provider you want to configure.
+3. Open **advanced settings**.
+4. Set **Update identity on login** to **automatic**.
+
+#### Configure via the CLI
+
+```shell
+ory patch identity-config \
+ --replace '/selfservice/methods/oidc/config/providers/0/update_identity_on_login="automatic"'
+```
+
+Or set it in the full provider configuration:
+
+```json
+{
+ "selfservice": {
+ "methods": {
+ "oidc": {
+ "config": {
+ "providers": [
+ {
+ "id": "my-provider",
+ "provider": "generic",
+ "update_identity_on_login": "automatic",
+ "mapper_url": "base64://..."
+ }
+ ]
+ }
+ }
+ }
+ }
+}
+```
+
+#### Access the current identity in the mapper
+
+When `update_identity_on_login` is set to `automatic`, the data mapper receives the current identity as an additional variable
+`std.extVar('identity')`. This lets you write conditional logic — for example, preserving existing trait values or merging data
+selectively.
+
+The `identity` variable contains:
+
+```json5
+{
+ traits: {
+ /* current identity traits */
+ },
+ metadata_public: {
+ /* current public metadata */
+ },
+ metadata_admin: {
+ /* current admin metadata */
+ },
+}
+```
+
+Example: update the user's groups from the provider while preserving an existing display name set by the user:
+
+```jsonnet title="data-mapper.jsonnet"
+local claims = std.extVar('claims');
+local identity = std.extVar('identity');
+
+{
+ identity: {
+ traits: {
+ email: claims.email,
+ // Keep the display name if the user has set one.
+ [if "display_name" in identity.traits && identity.traits.display_name != "" then "display_name"]:
+ identity.traits.display_name,
+ // Always update groups from the provider.
+ [if "groups" in claims.raw_claims then "groups" else null]:
+ claims.raw_claims.groups,
+ },
+ metadata_admin: {
+ [if "office" in claims.raw_claims then "office" else null]:
+ claims.raw_claims.office,
+ },
+ },
+}
+```
+
+#### Behavior details
+
+- **No unnecessary writes.** The identity is only updated in the database when traits or metadata actually changed.
+- **Metadata preservation.** When the mapper omits `metadata_public` or `metadata_admin` from its output, existing values are
+ preserved. When the mapper explicitly outputs `{}`, the field is cleared.
+- **Schema validation.** The updated identity is validated against the identity schema before it is persisted. If the mapper
+ produces invalid traits, the login fails with a validation error.
+- **No credential changes.** Credentials and verified addresses are never modified by this feature.
+
### Emails and phone numbers
:::danger