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); + }); });