feat(microsoft): map userPrincipalName to preferred_username claim#4725
Open
matzegebbe wants to merge 1 commit intodexidp:masterfrom
Open
feat(microsoft): map userPrincipalName to preferred_username claim#4725matzegebbe wants to merge 1 commit intodexidp:masterfrom
matzegebbe wants to merge 1 commit intodexidp:masterfrom
Conversation
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
4458c9a to
e90840f
Compare
nabokihms
requested changes
Apr 8, 2026
| EmailVerified: true, | ||
| UserID: user.ID, | ||
| Username: user.Name, | ||
| PreferredUsername: user.Email, |
Member
There was a problem hiding this comment.
When we use the same attribute for the other claim, it looks... kinda odd.
Contributor
Author
There was a problem hiding this comment.
I didn't check Crowd. That implementation looks good to me.
Contributor
Author
There was a problem hiding this comment.
plus that would solve my problem and not change anything for existing installations and users (in terms of token size etc)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Map
userPrincipalNamefrom the Microsoft Graph API to thepreferred_usernameclaim in Dex-issued tokens.What this PR does / why we need it
The Microsoft connector fetches
userPrincipalNamevia the Graph API (/v1.0/me?$select=id,displayName,userPrincipalName) but only maps it toemail. ThePreferredUsernamefield onconnector.Identityis always left empty, even though other connectors (OIDC, OAuth) already populate it.This PR sets
PreferredUsernametouserPrincipalNamein bothHandleCallbackandRefresh. If the value is absent from the Graph API response, the field remains empty - no error is raised.Why this matters: For environments that use Dex as an identity broker,
preferred_usernameprovides a reliable, human-readable identifier for consistent username mapping across downstream services (e.g. Kubernetes RBAC, GitLab, Grafana). The UPN is well-suited for this because it is always present (required, non-nullable), unique within the tenant, and already fetched by the connector.Changes:
connector/microsoft/microsoft.go- setPreferredUsernametouserPrincipalNameinHandleCallbackandRefreshconnector/microsoft/microsoft_test.go- update test expectationThis is a non-breaking, additive change.
preferred_usernamewill now be populated in tokens where it was previously empty. No configuration required.Note on Azure AD / Entra ID optional claims:
This change reads
userPrincipalNamefrom the Graph API, which is always available - no optional claims configuration is needed. However, if downstream services consume the Microsoft-issued ID/access tokens directly, addingpreferred_usernameas an optional claim in the Azure app registration (Token configuration > Add optional claim) ensures the value is present there as well.Special notes for your reviewer
I have two questions about the current design of the Microsoft connector that go beyond this PR:
Why is
userPrincipalNameused as theemailfield instead of themailproperty?The connector has mapped
userPrincipalName->emailsince its initial implementation in 2017 (6193bf55). The Graph API'smailproperty has never been fetched. I assume this is becauseuserPrincipalNameis guaranteed to be non-null, whilemailis optional and can be empty (especially for personal accounts). TheemailToLowercaseoption (PR Added the possibility to activate lowercase for UPN-Strings #1888) was originally namedupnToLowercase, which suggests this is a known trade-off.This also means the Go
userstruct field is namedEmailbut actually holdsuserPrincipalName- so in this PR,identity.PreferredUsername = user.Emailis setting it to the UPN, not a separate email value. There's no need for an additional Graph API field since it would just fetch the same value twice.However, this means the
emailclaim in Dex-issued tokens may contain a UPN that differs from the user's actual mailbox address. Would it make sense to fetchmailfrom the Graph API and prefer it when available, falling back to UPN? Or is the current behavior intentional?Would a
claimMappingconfig be a better approach?The OIDC and OAuth connectors support a
claimMappingconfiguration (e.g.preferred_username: preferred_username) that lets users control which upstream claim maps to which identity field. Instead of hardcoding the UPN ->preferred_usernamemapping, would it be preferred to addclaimMappingsupport to the Microsoft connector as well? This would give users flexibility to map any Graph API user property to any identity field.If this has already been addressed and I missed it, feel free to close this.