OAuth2 Server sending 'null' as the 'sub' parameter instead of User Id When using OAuth2 Server module to authorize with users, I am not receiving the correct &quot;sub&quot; parameter in the response from the server. I get all the other fields such as email, email_verified, name, preferred_username, and zoneinfo; just not the sub parameter.
{
  &quot;sub&quot;: null,
  &quot;email&quot;: &quot;someuser@email.com&quot;,
  &quot;email_verified&quot;: true,
  &quot;name&quot;: &quot;SomeUser&quot;,
  &quot;preferred_username&quot;: &quot;SomeUser&quot;,
  &quot;zoneinfo&quot;: &quot;Asia/Dubai&quot;
}

I've tried using hook_oauth2_server_user_claims to alter this parameter like so:
function my_module_oauth_server_user_claims($account, $requested_scopes) {
  $claims = [];

  if (in_array('profile', $requested_scopes)) {
    $claims['sub'] = $account-&gt;id();
    $claims['user_id'] = $account-&gt;id(); // for debugging purposes
  }

  return $claims;
} 

Doing this adds the user_id parameter to the response body, but the 'sub' is still set as 'null', therefore it must get override afterwards.
I double checked the documentation and have all the permissions set, but can't find anything about this particular issue.. Why is `sub' not being populated correctly during this process?
The code invoking that hook is the following one, in the OAuth2Storage.php file, line 750 and forward.
    // Prepare the default claims.
    $claims = [
      'sub' =&gt; $account-&gt;{$sub_property}-&gt;value,
    ];

    if (in_array('email', $requested_scopes)) {
      $claims['email'] = $account-&gt;getEmail();
      $claims['email_verified'] = $this-&gt;configFactory-&gt;get('user.settings')
        -&gt;get('verify_mail');
    }

    if (in_array('profile', $requested_scopes)) {
      if (!empty($account-&gt;label())) {
        $claims['name'] = $account-&gt;getDisplayName();
        $claims['preferred_username'] = $account-&gt;getAccountName();
      }
      if (!empty($account-&gt;timezone)) {
        $claims['zoneinfo'] = $account-&gt;getTimeZone();
      }
      $anonymous_user = new AnonymousUserSession();
      if ($anonymous_user-&gt;hasPermission('access user profiles')) {
        $claims['profile'] = $account-&gt;toUrl('canonical', ['absolute' =&gt; TRUE]);
      }
      if ($picture = $this-&gt;getUserPicture($account)) {
        $claims['picture'] = $picture;
      }
    }

    // Allow modules to supply additional claims.
    $claims += $this-&gt;moduleHandler-&gt;invokeAll('oauth2_server_user_claims', [
      'account' =&gt; $account,
      'requested_scopes' =&gt; $requested_scopes,
    ]);

    // Finally, allow modules to alter claims.
    $context = [
      'claims' =&gt; &amp;$claims,
      'account' =&gt; $account,
      'requested_scopes' =&gt; $requested_scopes,
    ];
    $this-&gt;moduleHandler-&gt;alter('oauth2_server_user_claims', $context);
    return $claims;

Since the code first sets $claims to ['sub' =&gt; $account-&gt;{$sub_property}-&gt;value] and then adds any array key returned from that hook with $claims += $this-&gt;moduleHandler-&gt;invokeAll('oauth2_server_user_claims', ['account' =&gt; $account, 'requested_scopes' =&gt; $requested_scopes]); the hook cannot change the value for the sub key. You can verify how PHP behaves with the following simpler code.
$array = ['sub' =&gt; NULL];
$array += ['sub' =&gt; 10, 'id' =&gt; 10];
print_r($array);

To alter the sub key, the module should implement hook_oauth2_server_user_claims_alter().
function my_module_oauth_server_user_claims_alter(&amp;$context) {
  if (in_array('profile', $context['requested_scopes'])) {
    $context['claims']['sub'] = $account-&gt;id();
    $context['claims']['user_id'] = $account-&gt;id();
  }
}