LinkedIn Social Connection - "locale" Claim is an Object Instead of a String

Overview

While the Open ID Connect (OIDC) specs state the "locale" claim as a string: 

https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims:~:text=America/Los_Angeles.-,locale,string,-End%2DUser%27s%20locale

LinkedIn returns an object as a value for that attribute, and Auth0 also takes it as an object in the normalized user profile.

This document proposes a solution to pass a flattened "locale" attribute in the token using Actions.

 

Applies To
  • LinkedIn Social Connection
  • "locale" claim
Cause
LinkedIn is returning the ID Token with the "locale" claim in an object format instead of a string.
This is the ID Token from LinkedIn:
{
  "sub": "linkedin|Gxxxxx",
  "given_name": "<First Name>",
  "family_name": "<Last Name>",
  "nickname": "<Nickname>",
  "name": "<Full Name>",
  ...
  "locale": {
    "country": "ES",
    "language": "es"
  },
  "updated_at": "2025-07-02T15:31:58.178Z",
  "iss": "https://<domain>.auth0.com/",
  "aud": "<aud>",
  "iat": 1751470394,
  "exp": 1751506394,
  "sid": "<sid>",
  ...
}
 
 Auth0 is taking that claim and storing it in the user profile as an object:
{
    "created_at": "2025-07-01T23:40:19.372Z",
    "family_name": "<Last Name>",
    "given_name": "<First Name>",
    "identities": [
        {
            "provider": "linkedin",
            "user_id": "Gxxxxxxx",
            "connection": "linkedin",
            "isSocial": true
        }
    ],
    "locale": {
        "country": "ES",
        "language": "es"
    },
    "name": "<Full Name>",
    "nickname": "<Nickname>",
    ...
    "sub": "xxxz",
    "updated_at": "2025-07-02T15:55:57.034Z",
    "user_id": "linkedin|Gxxxxxxx",
    "last_ip": "<IP Address>",
    "last_login": "2025-07-02T15:55:57.033Z",
    "logins_count": 9,
    "blocked_for": [],
    "guardian_authenticators": [],
    "passkeys": []
}
 
Solution
Create a Post Login Action to flatten the locale scope and then add it to a custom claim on the IDToken:

/**
* Flatten the locale scope if it's not a string
*/
exports.onExecutePostLogin = async (event, api) => {
  const { locale } = event.user;
  if (typeof locale === "object") {
    // linkedin sets the locale as an object like this:
    // "locale": {
    //    "country": "US",
    //    "language": "en"
    //},

    // flatten the locale:
    api.idToken.setCustomClaim(
      "locale", 
      `${locale.language}-${locale.country}`
      );
  }
};

See Create custom claims for more details on setting custom claims with Actions.

Recommended content

No recommended content found...