contrition/src/controllers/OAuth.ts

49 lines
1.8 KiB
TypeScript

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 });
};