next-auth: GitHub user.email can be null

Describe the bug GitHub user.email field does not always contains the email. It has the email, if the user decided to share the email to public. So, session.user.email can be null in most of the cases.

To Reproduce

  • Create a simple app to with the GitHub provider
  • Disable sharing your GitHub email to public
  • If you try to login then session.user.email will be null

Expected behavior We should expose the email in session.user

Additional context In order to get the email, we need to add user.email scope to GitHub. We need to fetch emails using an API endpoint.

I read the GitHub provider file and I’m not sure we can apply login in the profile creating function. What’s the best way to implement the above logic?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 16 (5 by maintainers)

Most upvoted comments

@arunoda the default user scope is sufficient as @iaincollins pointed out. You need to request the user’s emails in a custom signin callback as they are never included in the metadata returned by github:

  callbacks: {
    signin: async (profile, account, metadata) => {
      // https://developer.github.com/v3/users/emails/#list-email-addresses-for-the-authenticated-user
      const res = await fetch('https://api.github.com/user/emails', {
        headers: {
          'Authorization': `token ${account.accessToken}`
        }
      })
      const emails = await res.json()
      if (!emails || emails.length === 0) {
        return
      }
      // Sort by primary email - the user may have several emails, but only one of them will be primary
      const sortedEmails = emails.sort((a, b) => b.primary - a.primary)
      profile.email = sortedEmails[0].email
    },
  },

Since fetch runs on the server, you have to use a Node.js implementation, for example node-fetch.

Hope this helps

next-auth v4 will include email by default. In fact, we aim for returning exactly the same fields for ALL our providers by default. See #2524

@aslakhellesoy Thank you so much for this. I was getting a null email until I added your callback. I’m using user:email.

You need to request the user’s emails in a custom signin callback as they are never included in the metadata returned by github

Shouldn’t that be mentioned in the docs? If that’s always the case that seems like a pretty big deal that users created via GitHub by Next Auth never have an associated email by default.

Here is my slightly more compact version that is TypeScript-friendly, and checks for GitHub (Next Auth v3):

signIn: async (profile, account) => {
  if (account.provider === 'github') {
    const res = await fetch('https://api.github.com/user/emails', {
      headers: { Authorization: `token ${account.accessToken}` },
    })
    const emails = await res.json()
    if (emails?.length > 0) {
      profile.email = emails.sort((a, b) => b.primary - a.primary)[0].email
    }
    return true
  }
},

I still face this issue even that it seems to be implemented and supported. But for users with an email address set to private I still face this issue

Hey, I faced this issue because I had a Github App instead of a Github OAuth App. See if that is the case for you too, hope it helps!

@aslakhellesoy can you please with the full example

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'


const options = {
    providers: [
        Providers.Google({
            clientId: process.env.FRONT_GOOGLE_CLIENT_ID,
            clientSecret: process.env.FRONT_GOOGLE_CLIENT_SECRET
        }),
        Providers.GitHub({
            clientId: process.env.FRONT_GITHUB_CLIENT_ID,
            clientSecret: process.env.FRONT_GITHUB_CLIENT_SECRET
        }),
    ],
    database: process.env.FRONT_DB_URL,

    secret: process.env.FRONT_SESSION_SECRET,

    session: {
        jwt: true,
    },

    jwt: {

        secret: process.env.FRONT_JWT_SECRET,

    },

    pages: {
    },

    callbacks: {
        signin: async (profile, account, metadata) => {
            console.info('we are here to see the callback\nP\nP');
            console.log(profile, 'is the profile');
            console.log(account, 'is the account');
            console.log(metadata, 'is the metadata');
            const res = await fetch('https://api.github.com/user/emails', {
                headers: {
                    'Authorization': `token ${account.accessToken}`
                }
            })
            const emails = await res.json()
            if (!emails || emails.length === 0) {
                return
            }
            const sortedEmails = emails.sort((a, b) => b.primary - a.primary)
            profile.email = sortedEmails[0].email
        },
    },

    events: {},

    debug: process.env.NODE_ENV === 'development',
}

export default (req, res) => NextAuth(req, res, options)

I tried to use this code but nothing happenend. Can you please explain me what to do? Where should I put your code?

This is not an error it is mentioned in the documentation.

GitHub allows the user not to expose their email address to OAuth services if they have relevant privacy settings are enabled.

You can modify the scope property on any provider - though we are already requesting it implicitly via the user property (see GitHub OAuth docs for details on scope options) so in this case will not make any difference.