util.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. /* eslint-disable camelcase */
  2. import * as querystring from "querystring";
  3. import { Option, tryDo, isHttpError } from "@shared/common/async_utils";
  4. import got, { HTTPError } from "got";
  5. const OAUTH_API = "https://discord.com/api/oauth2";
  6. export interface AccessTokenResponse {
  7. access_token: string;
  8. token_type: string;
  9. expires_in: number;
  10. refresh_token: string;
  11. scope: string;
  12. }
  13. type GrantType = "authorization_code";
  14. type TokenType = "code" | "token";
  15. export interface AccessTokenRequest {
  16. client_id: string;
  17. client_secret: string;
  18. grant_type: GrantType;
  19. code: string;
  20. scope: string;
  21. redirect_uri: string;
  22. }
  23. export type AuthorizeRequest = {
  24. client_id: string,
  25. redirect_url: string,
  26. response_type: TokenType,
  27. scope: string,
  28. }
  29. export class OAuth2 {
  30. static getAuthUrl(opts: AuthorizeRequest): string {
  31. return `${OAUTH_API}/authorize?${querystring.stringify(opts)}`;
  32. }
  33. static async getToken(opts: AccessTokenRequest):
  34. Promise<Option<AccessTokenResponse, { error: string }>> {
  35. const result = await tryDo(got<AccessTokenResponse>(`${OAUTH_API}/token`, {
  36. method: "post",
  37. form: opts,
  38. }));
  39. if (!result.ok) {
  40. if (isHttpError<HTTPError>(result.error)) {
  41. return { error: `Failed to authenticate. Error: ${result.error.message}`, ok: false };
  42. }
  43. return { error: "Unexpected error", ok: false };
  44. }
  45. return { ok: true, ...result.result.body };
  46. }
  47. }