Twitter OAuth not returning email despite proper configuration

I'm using better-auth with Twitter OAuth and can't get the email address from users. I've set up everything according to docs but still getting:
ERROR [Better Auth]: Provider did not return email. This could be due to misconfiguration in the provider settings.
ERROR [Better Auth]: Provider did not return email. This could be due to misconfiguration in the provider settings.
What I've done: - Enabled "Request email from users" in Twitter dev portal - Set up app as "Web App" with "Confidential client" - Set callback URL to http://localhost:3000/api/auth/callback/twitter - Been following this guide: https://www.better-auth.com/docs/authentication/twitter Current code:
twitter: {
clientId: process.env.TWITTER_CLIENT_ID as string,
clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
mapProfileToUser: (profile: TwitterProfile) => {
console.log("profile", profile);
return {
twitterHandle: profile.username,
email: profile.email as string,
name: profile.name as string,
image: profile.profile_image_url as string,
username: profile.username,
};
},
}
twitter: {
clientId: process.env.TWITTER_CLIENT_ID as string,
clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
mapProfileToUser: (profile: TwitterProfile) => {
console.log("profile", profile);
return {
twitterHandle: profile.username,
email: profile.email as string,
name: profile.name as string,
image: profile.profile_image_url as string,
username: profile.username,
};
},
}
What I'm getting:
@repo/web:dev: profile {
@repo/web:dev: data: {
@repo/web:dev: name: 'Robin Faraj',
@repo/web:dev: profile_image_url: 'https://pbs.twimg.com/profile_images/1855238561395544064/xZgBtaNg_normal.jpg',
@repo/web:dev: username: 'robin_faraj',
@repo/web:dev: id: '2517496955'
@repo/web:dev: }
@repo/web:dev: }
@repo/web:dev: profile {
@repo/web:dev: data: {
@repo/web:dev: name: 'Robin Faraj',
@repo/web:dev: profile_image_url: 'https://pbs.twimg.com/profile_images/1855238561395544064/xZgBtaNg_normal.jpg',
@repo/web:dev: username: 'robin_faraj',
@repo/web:dev: id: '2517496955'
@repo/web:dev: }
@repo/web:dev: }
No email in the profile data. How can I get Twitter to return the email address with better-auth? Is this a Twitter API limitation or a configuration issue? Any help appreciated!
Twitter (X) | Better Auth
Twitter provider setup and usage.
12 Replies
KiNFiSH
KiNFiSH•4w ago
Can you please make sure this consent screen appear when you try to signin
No description
iPheNoMeNaL-oG
iPheNoMeNaL-oG•4w ago
I'm having trouble with this right now as well..
socialProviders: {
twitter: {
clientId: process.env.TWITTER_CLIENT_ID!,
clientSecret: process.env.TWITTER_CLIENT_SECRET!,
redirectUri: "/dashboard",
scope: [
"tweet.read",
"tweet.write",
"users.read",
"offline.access",
"users.email",
],
mapProfileToUser: (profile: any) => {
console.log("Map Profile To User", profile);
return {
id: profile.id,
name: profile.name,
email: profile.email,
image: profile.profile_image_url,
emailVerified: false,
username: profile.username,
};
},
socialProviders: {
twitter: {
clientId: process.env.TWITTER_CLIENT_ID!,
clientSecret: process.env.TWITTER_CLIENT_SECRET!,
redirectUri: "/dashboard",
scope: [
"tweet.read",
"tweet.write",
"users.read",
"offline.access",
"users.email",
],
mapProfileToUser: (profile: any) => {
console.log("Map Profile To User", profile);
return {
id: profile.id,
name: profile.name,
email: profile.email,
image: profile.profile_image_url,
emailVerified: false,
username: profile.username,
};
},
I'm logging the data coming back and it does indeed include email..
// this is what is returned by
data: {
profile_image_url: 'https://pbs.twimg.com/profile_images/basgdasdfasf.jpg',
id: '234623732472',
username: 'randomUsername',
name: 'Random Name',
}
// this is what is returned by
data: {
profile_image_url: 'https://pbs.twimg.com/profile_images/basgdasdfasf.jpg',
id: '234623732472',
username: 'randomUsername',
name: 'Random Name',
}
It's included in the scope and it's in the profile data.. yet it says it isnt there Calling linkSocial like this
const handleTwitterLink = async () => {
const data = await authClient.linkSocial(
{
provider: "twitter",
callbackURL: "/dashboard/account/apps",
scopes: ["users.email"],
},
const handleTwitterLink = async () => {
const data = await authClient.linkSocial(
{
provider: "twitter",
callbackURL: "/dashboard/account/apps",
scopes: ["users.email"],
},
my twitter account has an email linked to it.. not sure I guess for context.. I'm logged into my app via credentials and linking twitter to it I have been successful in this but trying to grab the email now and it's not working ..
robinfaraj
robinfarajOP•4w ago
got it working somehow
twitter: {
clientId: process.env.TWITTER_CLIENT_ID as string,
clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
scope: ["users.email", "users.read", "tweet.read", "tweet.write", "follows.read", "media.write", "offline.access"],
getUserInfo: async ({ accessToken }) => {
const response = await fetch(
'https://api.twitter.com/2/users/me?user.fields=confirmed_email,profile_image_url,name,username,verified,verified_type',
{ headers: { 'Authorization': `Bearer ${accessToken}` } }
);

if (!response.ok) throw new Error('Failed to fetch user info from Twitter');

const data = await response.json();
return {

user: {
id: data.data.id,
email: data.data.confirmed_email,
name: data.data.name,
image: data.data.profile_image_url,
username: data.data.username,
emailVerified: true,
isBlueVerified: data.data.verified_type === "blue"
},
data: data.data
};
},
},
twitter: {
clientId: process.env.TWITTER_CLIENT_ID as string,
clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
scope: ["users.email", "users.read", "tweet.read", "tweet.write", "follows.read", "media.write", "offline.access"],
getUserInfo: async ({ accessToken }) => {
const response = await fetch(
'https://api.twitter.com/2/users/me?user.fields=confirmed_email,profile_image_url,name,username,verified,verified_type',
{ headers: { 'Authorization': `Bearer ${accessToken}` } }
);

if (!response.ok) throw new Error('Failed to fetch user info from Twitter');

const data = await response.json();
return {

user: {
id: data.data.id,
email: data.data.confirmed_email,
name: data.data.name,
image: data.data.profile_image_url,
username: data.data.username,
emailVerified: true,
isBlueVerified: data.data.verified_type === "blue"
},
data: data.data
};
},
},
`
KiNFiSH
KiNFiSH•4w ago
here you are getting the email right ?
iPheNoMeNaL-oG
iPheNoMeNaL-oG•4w ago
@KiNFiSH Yes it's logging the email.. but email: profile.email is throwing an email not found error
KiNFiSH
KiNFiSH•4w ago
You mean data.email ? Internally we do covert it from confirmed_email response to email literal
robinfaraj
robinfarajOP•4w ago
how, I don't get anything I need to fetch it separately in getUserinfo
KiNFiSH
KiNFiSH•4w ago
We fetch for you and merge with the response data
robinfaraj
robinfarajOP•4w ago
but I was also in version 1.1.10 - just upgraded to 1.2.7
KiNFiSH
KiNFiSH•4w ago
Yeah you should update
robinfaraj
robinfarajOP•4w ago
lots of things are red now, but I'll hope it'll fix some things 😄
KiNFiSH
KiNFiSH•4w ago
Yeah. It should be working Lemme know if that’s not the case

Did you find this page helpful?