@@ -192,6 +192,108 @@ local claims = std.extVar('claims');
192192}
193193```
194194
195+ ### Update identity on login
196+
197+ By default, the data mapper runs only during registration. If you want to keep identity data in sync with the upstream provider,
198+ set ` update_identity_on_login ` to ` automatic ` on the provider configuration. This re-runs the Jsonnet data mapper on every login
199+ and updates the identity's traits and metadata when they change.
200+
201+ This is useful when the upstream identity provider is the source of truth for user attributes such as group memberships, roles, or
202+ profile information that can change over time.
203+
204+ #### Configure in the Ory Console
205+
206+ 1 . Go to <ConsoleLink route = " project.socialSignIn" />.
207+ 2 . Select the provider you want to configure.
208+ 3 . Open ** advanced settings** .
209+ 4 . Set ** Update identity on login** to ** automatic** .
210+
211+ #### Configure via the CLI
212+
213+ ``` shell
214+ ory patch identity-config < project-id> \
215+ --replace ' /selfservice/methods/oidc/config/providers/0/update_identity_on_login="automatic"'
216+ ```
217+
218+ Or set it in the full provider configuration:
219+
220+ ``` json
221+ {
222+ "selfservice" : {
223+ "methods" : {
224+ "oidc" : {
225+ "config" : {
226+ "providers" : [
227+ {
228+ "id" : " my-provider" ,
229+ "provider" : " generic" ,
230+ "update_identity_on_login" : " automatic" ,
231+ "mapper_url" : " base64://..."
232+ }
233+ ]
234+ }
235+ }
236+ }
237+ }
238+ }
239+ ```
240+
241+ #### Access the current identity in the mapper
242+
243+ When ` update_identity_on_login ` is set to ` automatic ` , the data mapper receives the current identity as an additional variable
244+ ` std.extVar('identity') ` . This lets you write conditional logic — for example, preserving existing trait values or merging data
245+ selectively.
246+
247+ The ` identity ` variable contains:
248+
249+ ``` json5
250+ {
251+ traits: {
252+ /* current identity traits */
253+ },
254+ metadata_public: {
255+ /* current public metadata */
256+ },
257+ metadata_admin: {
258+ /* current admin metadata */
259+ },
260+ }
261+ ```
262+
263+ Example: update the user's groups from the provider while preserving an existing display name set by the user:
264+
265+ ``` jsonnet title="data-mapper.jsonnet"
266+ local claims = std.extVar('claims');
267+ local identity = std.extVar('identity');
268+
269+ {
270+ identity: {
271+ traits: {
272+ email: claims.email,
273+ // Keep the display name if the user has set one.
274+ [if "display_name" in identity.traits && identity.traits.display_name != "" then "display_name"]:
275+ identity.traits.display_name,
276+ // Always update groups from the provider.
277+ [if "groups" in claims.raw_claims then "groups" else null]:
278+ claims.raw_claims.groups,
279+ },
280+ metadata_admin: {
281+ [if "office" in claims.raw_claims then "office" else null]:
282+ claims.raw_claims.office,
283+ },
284+ },
285+ }
286+ ```
287+
288+ #### Behavior details
289+
290+ - No unnecessary writes. The identity is only updated in the database when traits or metadata actually changed.
291+ - Metadata preservation. When the mapper omits ` metadata_public ` or ` metadata_admin ` from its output, existing values are
292+ preserved. When the mapper explicitly outputs ` {} ` , the field is cleared.
293+ - Schema validation. The updated identity is validated against the identity schema before it is persisted. If the mapper produces
294+ invalid traits, the login fails with a validation error.
295+ - No credential changes. Credentials and verified addresses are never modified by this feature.
296+
195297### Emails and phone numbers
196298
197299:::danger
0 commit comments