From 97bbaf16c92398c843f0184809645fb4b9b22410 Mon Sep 17 00:00:00 2001 From: sreecharan-desu Date: Sat, 24 Jan 2026 09:41:41 +0530 Subject: [PATCH] fix(auth-server): Add email claims to ID Token for compatibility Mozilla Connect (and potentially other SPs) requires email and email_verified claims in the ID Token to successfully authenticate users. This change ensures these claims are included when the email or profile scope is requested. This also adds unit tests to verify the claims are only included when appropriate scopes are authorized. Closes: https://github.com/mozilla/fxa/issues/18854 --- packages/fxa-auth-server/lib/oauth/grant.js | 11 +++++++++++ packages/fxa-auth-server/test/oauth/grant.js | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/packages/fxa-auth-server/lib/oauth/grant.js b/packages/fxa-auth-server/lib/oauth/grant.js index 962125fa933..bc7aad141b0 100644 --- a/packages/fxa-auth-server/lib/oauth/grant.js +++ b/packages/fxa-auth-server/lib/oauth/grant.js @@ -249,6 +249,17 @@ async function generateIdToken(grant, accessToken) { claims.auth_time = Math.floor(grant.authAt / 1000); } + if ( + grant.email && + grant.scope && + (grant.scope.contains('email') || + grant.scope.contains('profile') || + grant.scope.contains('profile:email')) + ) { + claims.email = grant.email; + claims.email_verified = true; + } + return jwt.sign(claims); } diff --git a/packages/fxa-auth-server/test/oauth/grant.js b/packages/fxa-auth-server/test/oauth/grant.js index 5c62bad630c..2d634e336e6 100644 --- a/packages/fxa-auth-server/test/oauth/grant.js +++ b/packages/fxa-auth-server/test/oauth/grant.js @@ -415,4 +415,24 @@ describe('generateTokens', () => { Math.floor(requestedGrant.authAt / 1000) ); }); + + it('should include email and email_verified in ID token claims when requested by scope', async () => { + requestedGrant.scope = ScopeSet.fromArray(['openid', 'email']); + requestedGrant.email = 'test@example.com'; + const result = await generateTokens(requestedGrant); + assert.ok(result.id_token); + const jwt = decodeJWT(result.id_token); + assert.strictEqual(jwt.claims.email, 'test@example.com'); + assert.strictEqual(jwt.claims.email_verified, true); + }); + + it('should NOT include email and email_verified in ID token claims when NOT requested by scope', async () => { + requestedGrant.scope = ScopeSet.fromArray(['openid']); + requestedGrant.email = 'test@example.com'; + const result = await generateTokens(requestedGrant); + assert.ok(result.id_token); + const jwt = decodeJWT(result.id_token); + assert.isUndefined(jwt.claims.email); + assert.isUndefined(jwt.claims.email_verified); + }); });