This repository has been archived on 2024-10-29. You can view files and clone it, but cannot push or open issues or pull requests.
Files
api/src/services/users/oauth2/discord.ts
2021-10-24 04:06:16 +02:00

165 lines
4.8 KiB
TypeScript

import axios from 'axios'
import { Request, Response, Router } from 'express'
import { query } from 'express-validator'
import querystring from 'querystring'
import {
authenticateUser,
getUserWithBearerToken
} from '../../../tools/middlewares/authenticateUser'
import { validateRequest } from '../../../tools/middlewares/validateRequest'
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
import { buildQueryURL } from '../__utils__/buildQueryURL'
import { isValidRedirectURIValidation } from '../../../tools/validations/isValidRedirectURIValidation'
import { OAuthStrategy } from '../__utils__/OAuthStrategy'
export const DISCORD_PROVIDER = 'discord'
export const DISCORD_BASE_URL = 'https://discordapp.com/api/v6'
export const discordStrategy = new OAuthStrategy(DISCORD_PROVIDER)
interface DiscordUser {
id: string
username: string
discriminator: string
avatar?: string
locale?: string
}
interface DiscordTokens {
access_token: string
token_type: string
expires_in: number
refresh_token: string
scope: string
}
const getDiscordUserData = async (
code: string,
redirectURI: string
): Promise<DiscordUser> => {
const { data: tokens } = await axios.post<DiscordTokens>(
`${DISCORD_BASE_URL}/oauth2/token`,
querystring.stringify({
client_id: process.env.DISCORD_CLIENT_ID,
client_secret: process.env.DISCORD_CLIENT_SECRET,
grant_type: 'authorization_code',
code,
redirect_uri: redirectURI,
scope: 'identify'
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
const { data: discordUser } = await axios.get<DiscordUser>(
`${DISCORD_BASE_URL}/users/@me`,
{
headers: {
Authorization: `${tokens.token_type} ${tokens.access_token}`
}
}
)
return discordUser
}
export const discordRouter = Router()
discordRouter.get(
`/users/oauth2/${DISCORD_PROVIDER}/add-strategy`,
authenticateUser,
[
query('redirectURI')
.notEmpty()
.trim()
.custom(isValidRedirectURIValidation)
],
validateRequest,
(req: Request, res: Response) => {
if (req.user == null) {
throw new ForbiddenError()
}
const { redirectURI } = req.query as { redirectURI: string }
const redirectCallback = `${process.env.API_BASE_URL}/users/oauth2/${DISCORD_PROVIDER}/callback-add-strategy?redirectURI=${redirectURI}`
const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&scope=identify&response_type=code&state=${req.user.accessToken}&redirect_uri=${redirectCallback}`
return res.json(url)
}
)
discordRouter.get(
`/users/oauth2/${DISCORD_PROVIDER}/callback-add-strategy`,
[
query('code').notEmpty(),
query('redirectURI')
.notEmpty()
.trim()
.custom(isValidRedirectURIValidation),
query('state')
.notEmpty()
.trim()
],
validateRequest,
async (req: Request, res: Response) => {
const { code, redirectURI, state: accessToken } = req.query as {
code: string
redirectURI: string
state: string
}
const userRequest = await getUserWithBearerToken(`Bearer ${accessToken}`)
const discordUser = await getDiscordUserData(
code,
`${process.env.API_BASE_URL}/users/oauth2/${DISCORD_PROVIDER}/callback-add-strategy?redirectURI=${redirectURI}`
)
const message = await discordStrategy.callbackAddStrategy(
{ name: discordUser.username, id: discordUser.id },
userRequest
)
return res.redirect(buildQueryURL(redirectURI, { message }))
}
)
discordRouter.get(
`/users/oauth2/${DISCORD_PROVIDER}/signin`,
[
query('redirectURI')
.notEmpty()
.trim()
.custom(isValidRedirectURIValidation)
],
validateRequest,
(req: Request, res: Response) => {
const { redirectURI } = req.query as { redirectURI: string }
const redirectCallback = `${process.env.API_BASE_URL}/users/oauth2/${DISCORD_PROVIDER}/callback?redirectURI=${redirectURI}`
const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirectCallback}`
return res.json(url)
}
)
discordRouter.get(
`/users/oauth2/${DISCORD_PROVIDER}/callback`,
[
query('code').notEmpty(),
query('redirectURI')
.notEmpty()
.trim()
.custom(isValidRedirectURIValidation)
],
validateRequest,
async (req: Request, res: Response) => {
const { code, redirectURI } = req.query as {
code: string
redirectURI: string
}
const discordUser = await getDiscordUserData(
code,
`${process.env.API_BASE_URL}/users/oauth2/${DISCORD_PROVIDER}/callback?redirectURI=${redirectURI}`
)
const responseJWT = await discordStrategy.callbackSignin({
name: discordUser.username,
id: discordUser.id
})
return res.redirect(buildQueryURL(redirectURI, responseJWT))
}
)