import base64url from "base64url"; // Module crypto gives an RNG source that is more secure (slower, more // entropy), and better for security. import * as crypto from "crypto"; import { Request, Response } from "express"; import { OAUTH_SCOPE } from "../config/Constants"; import { StateToken } from "../entity/StateToken"; import { respond } from "./Util"; /** * genOauth is the endpoint for users to visit and authorize themselves with Discord. * No passwords are exchanged with our servers, only receiving acknowledgement * and identification of users, meaning that we can access their basic * information in a safe manner. * * @route /api/oauth * */ export const genOAuth = async (req: Request, res: Response) => { // Get application core configuration const { api } = req.app.get("config"); // Generate secure token with package crypto. Make sure the token doesn't // already exist using a simple do while {}. If the token exists, simply // regenerate. Should not take more than 1 iteration in almost all // situations. let token: string; do { token = base64url(crypto.randomBytes(18)); } while ((await StateToken.count({ where: { token } })) !== 0); // Save the generated token to the database, so it can be validated when we // get the response back from Discord. new StateToken(token).save(); // Generate the redirect URL with the relevant information for the OAuth2 // Auth flow. let url = "https://canary.discord.com/api/oauth2/authorize" + `?response_type=code` + `&prompt=consent` + `&client_id=${api.client_id}` + `&scope=${encodeURIComponent(OAUTH_SCOPE)}` + // Only get the relevant info - identity `&state=${token}` + // Prevent impersonation with temporary state token `&redirect_uri=${encodeURIComponent(api.redirect_uri)}`; // {baseurl}/authorize respond(res, { redirectURL: url }); };