[oauth-providers] Discord provider omits email from response
https://github.com/honojs/middleware/blob/cd6c667ee267f409cdf7a6a35f9cc135f5091046/packages/oauth-providers/src/providers/discord/authFlow.ts#L134
If we request the email scope, we should have response.email, but the provider only returns response.user, making us not able to retrieve the email.
@gc Thank you for raising the issue.
Hi @monoald Can you take a look at this?
Suggestion: let us get the raw response as an escape hatch. ( c.get('raw') ?)
This took longer than I wanted, Writing this for the next person 👍
As of the current implementation of the /oauth2/@me endpoint (used in the middleware), The response is only exposing partial user object with the identify scope which doesn't retrieve the email even if the email is in scope.
https://discord.com/developers/docs/topics/oauth2#get-current-authorization-information
Response
{
/* "application": {
"id": "159799960412356608",
"name": "AIRHORN SOLUTIONS",
"icon": "f03590d3eb764081d154a66340ea7d6d",
"description": "",
"hook": true,
"bot_public": true,
"bot_require_code_grant": false,
"verify_key": "c8cde6a3c8c6e49d86af3191287b3ce255872be1fff6dc285bdb420c06a2c3c8"
},
"scopes": [
"email",
"identify"
],
"expires": "2021-01-23T02:33:17.017000+00:00",
*/
"user": {
"id": "268473310986240001",
"username": "discord",
"avatar": "f749bb0cbeeb26ef21eca719337d20f1",
"discriminator": "0",
"global_name": "Discord",
"public_flags": 131072
}
}
As a workaround you can add middleware to fetch the email(add conditionals to check if email is in scope using granted-scopes) and assign it to the user's object
app.get('/discord',
discordAuth({
client_id: Bun.env.DISCORD_ID,
client_secret: Bun.env.DISCORD_SECRET,
scope: ['identify', 'email'],
redirect_uri: 'http://localhost:3000/discord',
}),async (c) => {
const token = c.get('token')
// Fetch the complete user info from Discord users endpoint.
const userResponse = await fetch('https://discord.com/api/users/@me', {
headers: { authorization: `Bearer ${token?.token}` },
}).then(res => res.json())
console.log('Raw Discord user response:', userResponse)
const email = userResponse.email // email might be undefined if the token lacks the email scope
if (!email) {
throw new HTTPException(400, { message: 'Email is undefined. Make sure your OAuth2 request includes the "email" scope and the token has the proper permissions.' })
}
const refreshToken = c.get('refresh-token')
const grantedScopes = c.get('granted-scopes')
const user = c.get('user-discord') as DiscordUser & { email: string | null }
Object.assign(user, { email })
console.log('user', user)
return c.json({
token,
refreshToken,
grantedScopes,
user,
})
})
Hi @yusukebe @monoald
This issue can currently be handled by using middleware at the app level. However, since the issue remains open, would you accept a PR that addresses this edge case directly at the library level?