First name and last name get blanked out when signing in to Identity Cloud or AM 7.x using Apple social sign-on
The purpose of this article is to provide assistance if a user's first name and last name get blanked out when signing in to ForgeRock Identity Cloud or AM with Apple® social single sign-on (SSO).
Symptoms
Recent Changes
OpenID Connect (OIDC) Social SSO with Apple has been implemented. See Apple SSO integration with Identity Cloud for social authentication/registration (Identity Cloud) and Configure Social Identity Providers (AM) for further information.
A social authentication journey has been configured that includes the Patch Object node for progressive profiling.
Note
For Identity Cloud and AM 7.2 and later, you must enable the Request Native App for UserInfo setting to allow progressive profiling for native iOS apps. See Request Native App for UserInfo for information.
Causes
Apple does not conform to the usual standards for UserInfo when acting as an OIDC SSO provider. It only returns the user information the first time the user consents to share their information and does not return this information during the subsequent sign-ins with Apple. See Authenticating Users with Sign in with Apple for further information.
In a typical authentication journey that is configured to use more than one social provider, each social provider (other than Apple) returns the user information on each sign-in. Typically, in a journey that includes progressive profiling, a Patch Object node is responsible for continually updating the user profile data each time a user logs in. When a user chooses to sign in with Apple, this may result in the removal of their first name and last name when the user logs in after the first time.
Solution
The issue can be resolved by doing the following:
-
Pre-AM 7.2:
Update the Normalized Profile to Managed User script so that it sets a flag when the givenName
andfamilyName
are not available.
This step does not apply to Identity Cloud or AM 7.2 and later since the updated Normalized Profile to Managed User script is included in the release.
-
Add a Scripted Decision node to your social authentication journey. The purpose of this node is to skip patching the user's details when
givenName
andfamilyName
are missing.
Update the Normalized Profile to Managed User script (pre-AM 7.2)
You will need to update the Normalized Profile to Managed User script to set a flag when the givenName
and FamiliyName
are not available.
AM admin UI
- Go to Realms > [Realm Name] > Scripts and select the Normalized Profile to Managed User script.
- Add the following code snippet to the script, as appropriate:
- JavaScript // if the givenName and familyName is null or empty // then add a boolean flag to the shared state to indicate names are not present // this could be used elsewhere // for eg. this could be used in a scripted decision node to by-pass patching // the user object with blank values when givenName and familyName is not present var noGivenName = normalizedProfile.get('givenName').isNull() || normalizedProfile.get('givenName').asString().trim().length === 0 var noFamilyName = normalizedProfile.get('familyName').isNull() || normalizedProfile.get('familyName').asString().trim().length === 0 sharedState.put('nameEmptyOrNull', noGivenName && noFamilyName)
The updated script will look like this (with initial comments removed for readability):(function () { var frJava = JavaImporter( org.forgerock.json.JsonValue ); var managedUserData = frJava.JsonValue.json(frJava.JsonValue.object()); managedUserData.put('givenName', normalizedProfile.get('givenName')); managedUserData.put('sn', normalizedProfile.get('familyName')); managedUserData.put('mail', normalizedProfile.get('email')); managedUserData.put('userName', normalizedProfile.get('username')); if (normalizedProfile.get('postalAddress').isNotNull()) { managedUserData.put('postalAddress', normalizedProfile.get('postalAddress')); } if (normalizedProfile.get('addressLocality').isNotNull()) { managedUserData.put('city', normalizedProfile.get('addressLocality')); } if (normalizedProfile.get('addressRegion').isNotNull()) { managedUserData.put('stateProvince', normalizedProfile.get('addressRegion')); } if (normalizedProfile.get('postalCode').isNotNull()) { managedUserData.put('postalCode', normalizedProfile.get('postalCode')); } if (normalizedProfile.get('country').isNotNull()) { managedUserData.put('country', normalizedProfile.get('country')); } if (normalizedProfile.get('phone').isNotNull()) { managedUserData.put('telephoneNumber', normalizedProfile.get('phone')); } // if the givenName and familyName is null or empty // then add a boolean flag to the shared state to indicate names are not present // this could be used elsewhere // for eg. this could be used in a scripted decision node to by-pass patching // the user object with blank values when givenName and familyName is not present var noGivenName = normalizedProfile.get('givenName').isNull() || normalizedProfile.get('givenName').asString().trim().length === 0 var noFamilyName = normalizedProfile.get('familyName').isNull() || normalizedProfile.get('familyName').asString().trim().length === 0 sharedState.put('nameEmptyOrNull', noGivenName && noFamilyName) return managedUserData; }());
- Groovy // if the givenName and familyName is null or empty // then add a boolean flag to the shared state to indicate names are not present // this could be used elsewhere // for eg. this could be used in a scripted decision node to by-pass patching // the user object with blank values when givenName and familyName is not present boolean noGivenName = normalizedProfile.givenName.isNull() || (!normalizedProfile.givenName.asString()?.trim()) boolean noFamilyName = normalizedProfile.familyName.isNull() || (!normalizedProfile.familyName.asString()?.trim()) sharedState.put("nameEmptyOrNull", noGivenName && noFamilyName)
The updated script will look like this (with initial comments removed for readability): import static org.forgerock.json.JsonValue.field import static org.forgerock.json.JsonValue.json import static org.forgerock.json.JsonValue.object import org.forgerock.json.JsonValue JsonValue managedUser = json(object( field("givenName", normalizedProfile.givenName), field("sn", normalizedProfile.familyName), field("mail", normalizedProfile.email), field("userName", normalizedProfile.username))) if (normalizedProfile.postalAddress.isNotNull()) managedUser.put("postalAddress", normalizedProfile.postalAddress) if (normalizedProfile.addressLocality.isNotNull()) managedUser.put("city", normalizedProfile.addressLocality) if (normalizedProfile.addressRegion.isNotNull()) managedUser.put("stateProvince", normalizedProfile.addressRegion) if (normalizedProfile.postalCode.isNotNull()) managedUser.put("postalCode", normalizedProfile.postalCode) if (normalizedProfile.country.isNotNull()) managedUser.put("country", normalizedProfile.country) if (normalizedProfile.phone.isNotNull()) managedUser.put("telephoneNumber", normalizedProfile.phone) // if the givenName and familyName is null or empty // then add a boolean flag to the shared state to indicate names are not present // this could be used elsewhere // for eg. this could be used in a scripted decision node to by-pass patching // the user object with blank values when givenName and familyName is not present boolean noGivenName = normalizedProfile.givenName.isNull() || (!normalizedProfile.givenName.asString()?.trim()) boolean noFamilyName = normalizedProfile.familyName.isNull() || (!normalizedProfile.familyName.asString()?.trim()) sharedState.put("nameEmptyOrNull", noGivenName && noFamilyName) return managedUser
- Click Save.
Add a Scripted Decision node to your authentication journey
You will need to add a Scripted Decision node to your social authentication journey with a script that uses a flag to skip the patching when givenName
and familiyName
are missing. This node should be placed directly before the Patch Object node in your social authentication progressive profile journey.
Create the decision node script
Identity Cloud admin UI:
- Go to Scripts > Auth Scripts.
- Click New Script.
- Select Journey Decision Node and click Next.
- Enter a name for the script and enter the Javascript script, similar to this example: if (sharedState.get('nameEmptyOrNull')) { outcome = 'true' } else { outcome = 'false' }
- Click Save and Close.
AM admin UI:
- Go to Realms > [Realm Name] > Scripts and click New Script.
- Enter a name for the script and select Decision node script for authentication trees as the script type.
- Click Create.
- Enter the script details, similar to this example:
- JavaScript if (sharedState.get('nameEmptyOrNull')) { outcome = 'true' } else { outcome = 'false' }
- Groovy if (sharedState.get("nameEmptyOrNull").asBoolean()) { outcome = "true" } else { outcome = "false" }
- Click Save Changes.
Add a scripted decision node to the social authentication journey
- Select the social authentication journey you wish to update:
- Identity Cloud admin UI: Go to Journeys > [Journey Name]
- AM admin UI: Go to: Realms > [Realm Name] > Authentication > Trees > [Tree Name]
- Add a Scripted Decision node directly before the Patch Object node. In the node details:
- Enter a suitable name for the node, for example, Skip Patching?.
- Select the script you created in the previous steps.
- Set the Outcomes as
true
andfalse
.
- Click Save Changes.
An example journey that includes the Skip Patching? node may look similar to this:
See Also
Apple SSO integration with Identity Cloud for social authentication/registration
How do I create end user journeys for social registration and login in Identity Cloud?