Skip to content

fix: map email from preferred_username/upn for OIDC providers like En… - #4073

Open
Vansh98789 wants to merge 3 commits into
ory:mainfrom
Vansh98789:fix/oidc-email-fallback
Open

fix: map email from preferred_username/upn for OIDC providers like En…#4073
Vansh98789 wants to merge 3 commits into
ory:mainfrom
Vansh98789:fix/oidc-email-fallback

Conversation

@Vansh98789

@Vansh98789 Vansh98789 commented Aug 8, 2026

Copy link
Copy Markdown

Fixes #3916

This PR fixes the missing email attribute in the /api/oauth/userinfo endpoint for OIDC providers like Microsoft Entra ID (Azure AD).

Root cause. extractOIDCUserProfile in npm/src/controller/utils.ts only mapped the email from idTokenClaims.email or userinfo.email:

profile.claims.email =
  typeof idTokenClaims.email === 'string' ? idTokenClaims.email : userinfo.email;

This line had three problems:

  1. No fallback to a usable claim — Microsoft Entra ID often does not populate the standard email claim at all. The email lives in preferred_username or upn, which the old code never looked at, so the email stayed undefined.

  2. Blind fallback — when idTokenClaims.email was not a string, it unconditionally used userinfo.email, even if that was also missing (undefined) or an array (Entra sends email as ["a@x.com", "b@x.com"] when multiple emails exist).

  3. No type guard on the fallbackuserinfo.email could be undefined, null, or an array and was assigned to profile.claims.email anyway.

Fix. Added an extractOIDCEmail helper and wired extractOIDCUserProfile to use it:

profile.claims.email = extractOIDCEmail(
  idTokenClaims as Record<string, unknown>,
  userinfo as Record<string, unknown>
);

extractOIDCEmail takes both sources and walks a priority ladder, returning the first value that is actually usable:

Priority | Check | Covers -- | -- | -- 1 | idTokenClaims.email (string, non-empty) | Standard OIDC (Google, Okta, etc.) 2 | userinfo.email (string, non-empty) | Standard OIDC 3 | email as array → take [0] | Entra ID multiple emails 4 | preferred_username (ID token, then userinfo) | Entra ID common case 5 | upn (ID token, then userinfo) | Entra ID last resort 6 | undefined | Nothing found

To reproduce locally:

cd npm
npx tap test/sso/extract_oidc_email.test.ts   # 8 pass, 0 fail
npx tsc --noEmit                              # no errors
  • New unit tests

Checklist

  • My code follows the style guidelines of this project

  • I have performed a self-review of my own code and corrected any misspellings

  • I have commented my code, particularly in hard-to-understand areas

  • I have made corresponding changes to the documentation

  • My changes generate no new warnings

  • I have added tests that prove my fix is effective or that my feature works

  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • Bug Fixes

    • Improved OIDC sign-in profile handling by recognizing email addresses from additional standard and provider-specific claims.
    • Added fallback support for array-valued email claims and user information when a primary email claim is unavailable.
  • Tests

    • Added coverage for claim precedence, fallback behavior, provider-specific usernames, and missing email scenarios.

@CLAassistant

CLAassistant commented Aug 8, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ef2338b-e24f-4e31-8a43-0700e08eb46c

📥 Commits

Reviewing files that changed from the base of the PR and between 53cdf79 and 5ff4df1.

📒 Files selected for processing (2)
  • npm/src/controller/utils.ts
  • npm/test/sso/extract_oidc_email.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • npm/src/controller/utils.ts
  • npm/test/sso/extract_oidc_email.test.ts

📝 Walkthrough

Walkthrough

The OIDC controller now extracts email values from standard and fallback claims in ID-token claims and userinfo. The user profile uses this helper. Tests cover precedence, arrays, Entra-specific claims, fallbacks, and missing values.

Changes

OIDC email extraction

Layer / File(s) Summary
Email extraction and profile integration
npm/src/controller/utils.ts, npm/test/sso/extract_oidc_email.test.ts
Added extractOIDCEmail with claim precedence, array handling, preferred_username, and upn fallbacks. Updated extractOIDCUserProfile to use the helper. Added tests for supported values and undefined results.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the email mapping fix for OIDC providers using preferred_username or upn.
Description check ✅ Passed The description explains the issue, root cause, fix, testing steps, and completed checklist items.
Linked Issues check ✅ Passed The changes address issue #3916 by returning email values from Entra OIDC claims and covering the behavior with tests.
Out of Scope Changes check ✅ Passed The changes are limited to OIDC email extraction and related unit tests, which match issue #3916.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@npm/src/controller/utils.ts`:
- Around line 277-291: Update extractOIDCEmail so each source’s email value is
normalized by selecting its first non-empty string, handling arrays before
falling back to userinfo. Preserve ID-token precedence for both scalar and array
claims, skip empty array entries, and add regression tests covering array
precedence and empty-first-entry behavior.

In `@npm/test/sso/extract_oidc_email.test.ts`:
- Around line 4-6: Remove the tap.teardown callback that calls process.exit(0)
in the test file. Allow Tap to finalize assertions, summaries, and failures
normally; do not replace it with another forced process exit.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c9d1bc56-803c-4a12-b45c-461c2f3e1bb8

📥 Commits

Reviewing files that changed from the base of the PR and between e13ed65 and 53cdf79.

📒 Files selected for processing (2)
  • npm/src/controller/utils.ts
  • npm/test/sso/extract_oidc_email.test.ts

Comment thread npm/src/controller/utils.ts Outdated
Comment thread npm/test/sso/extract_oidc_email.test.ts Outdated
@deepakprabhakara

deepakprabhakara commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks @Vansh98789, the team will review your PR soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OIDC Connection: Missing profile email in /api/oauth/userinfo endpoint

3 participants