Skip to content

fix: provider claim mapping (Microsoft, GitHub) and inert usernameViaEmail option - #178

Open
heudev wants to merge 3 commits into
NodeBB:mainfrom
heudev:fix/microsoft-oidc-claims
Open

fix: provider claim mapping (Microsoft, GitHub) and inert usernameViaEmail option#178
heudev wants to merge 3 commits into
NodeBB:mainfrom
heudev:fix/microsoft-oidc-claims

Conversation

@heudev

@heudev heudev commented Aug 12, 2026

Copy link
Copy Markdown

Three independent bugs found while wiring Microsoft (Entra ID / personal MSA) and GitHub up to this plugin. They are unrelated to each other, so they are separate commits.

1. Microsoft's givenname / familyname claims are ignored

https://graph.microsoft.com/oidc/userinfo returns the given/family name without underscores, and omits name, nickname and preferred_username entirely. A real response:

{
  "sub": "AAAAAAAAAAAAAAAAAAAAAPxIiNbOEB2t7uk7_u06hig",
  "givenname": "Enes",
  "familyname": "Uysal",
  "email": "…@hotmail.com",
  "locale": "tr-TR",
  "picture": "https://graph.microsoft.com/v1.0/me/photo/$value"
}

parseUserReturn only destructures given_name / family_name, so combinedFullName is empty and every Microsoft user ends up with a blank fullname even with "Sync fullname" enabled.

Because displayName is nickname || preferred_username || name, it is also undefined for these accounts — which makes loadStrategies reject the login with insufficient-scope, a fairly misleading message given that the scopes are fine and the email is present. The fallback that exists for exactly this case is the usernameViaEmail option, which brings us to the second bug.

2. usernameViaEmail never takes effect

Controllers.editStrategy normalises every checkbox before persisting it:

const checkboxes = ['forceUsernameViaEmail', 'usernameViaEmail', 'trustEmailVerified', 'syncFullname', 'syncPicture'];
checkboxes.forEach((prop) => {
	payload[prop] = payload.hasOwnProperty(prop) && payload[prop] === 'on' ? 1 : 0;
});

So the stored value is always 1 or 0, and the ACP template checks ./usernameViaEmail == "1" accordingly — but parseUserReturn compares against the raw form value:

usernameViaEmail === 'on'

which cannot match anything editStrategy writes. The option is inert for anyone configuring it through the ACP. Every sibling setting is read with parseInt(…, 10); this one now matches. forceUsernameViaEmail is given the same treatment for consistency — it currently works only because 0 happens to be falsy as a number.

3. GitHub's login and avatar_url claims are unmapped

https://api.github.com/user names those two fields differently from the OIDC conventions the plugin expects:

{ "login": "heudev", "id": 74737994, "name": "Enes Uysal", "avatar_url": "https://avatars.githubusercontent.com/u/74737994" }

login is the account's actual username, but with no nickname or preferred_username present displayName fell through to name — creating forum accounts literally called Enes Uysal, spaces and all, instead of heudev. And because the avatar is under avatar_url rather than picture, "Sync picture" had nothing to read and no avatar was ever stored.

login is inserted ahead of name in the displayName chain and avatar_url is used as a fallback for picture. Note this changes the username assigned to newly registered GitHub users; existing accounts keep the username they were created with.

Testing

Verified against a live NodeBB 4.14.10 forum with both a Microsoft strategy (userRoute = https://graph.microsoft.com/oidc/userinfo) and a GitHub one (https://api.github.com/user). Before: Microsoft logins failed with insufficient-scope; GitHub logins produced a space-separated username and no avatar. After: accounts are created with sensible usernames, fullname is populated from the provider's claims, and the GitHub avatar syncs. npx eslint . is clean.

heudev added 3 commits August 12, 2026 15:39
Microsoft's OIDC userinfo endpoint returns givenname and familyname
rather than the standard given_name and family_name, leaving fullname
empty for every user signing in through Microsoft.
editStrategy normalises every checkbox to 1 or 0 before saving, so the
comparison against 'on' never matched and the option had no effect.
GitHub's /user response carries the account name in login and the
avatar in avatar_url, so displayName fell through to the full name and
no picture was ever synced.
@heudev heudev changed the title fix: Microsoft OIDC claims and inert usernameViaEmail option fix: provider claim mapping (Microsoft, GitHub) and inert usernameViaEmail option Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant