diff --git a/src/api/util/handlers/Oauth.ts b/src/api/util/handlers/Oauth.ts
new file mode 100644
index 00000000..cc662161
--- /dev/null
+++ b/src/api/util/handlers/Oauth.ts
@@ -0,0 +1,83 @@
+// TODO: Puyo's connections PR would replace this file
+
+import { Config } from "@fosscord/util";
+import fetch from "node-fetch";
+
+export interface OauthAccessToken {
+ access_token: string;
+ token_type: string;
+ expires_in: string;
+ refresh_token: string;
+ scope: string;
+};
+
+export interface OauthUserDetails {
+ id: string;
+ email: string;
+ username: string;
+ avatar_url: string | null;
+}
+
+interface Connection {
+ getAccessToken: (code: string) => Promise<OauthAccessToken>;
+ getUserDetals: (token: string) => Promise<OauthUserDetails>;
+}
+
+const DiscordConnection: Connection = {
+ getAccessToken: async (code) => {
+ const { external } = Config.get();
+ const { discord } = external;
+
+ if (!discord.id || !discord.secret || !discord.redirect)
+ throw new Error("Discord Oauth has not been configured.")
+
+ const body = new URLSearchParams(
+ Object.entries({
+ client_id: discord.id as string,
+ client_secret: discord.secret as string,
+ redirect_uri: discord.redirect as string,
+ code: code as string,
+ grant_type: "authorization_code",
+ })
+ ).toString();
+
+ const resp = await fetch("https://discord.com/api/oauth2/token", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ body: body,
+ });
+ if (resp.status !== 200) throw new Error(`Failed to get access token.`,);
+
+ const json = await resp.json();
+
+ return json;
+ },
+
+ getUserDetals: async (token) => {
+ const resp = await fetch("https://discord.com/api/users/@me", {
+ headers: {
+ Authorization: `Bearer ${token}`
+ },
+ });
+
+ const json = await resp.json();
+ if (!json.username || !json.email) throw new Error("Failed to get user details via oauth");
+
+ return {
+ id: json.id,
+ email: json.email,
+ username: json.username,
+ avatar_url: json.avatar
+ ? `https://cdn.discordapp.com/avatars/${json.id}/${json.avatar}?size=2048`
+ : null,
+ };
+ }
+};
+
+const OauthCallbackHandlers: { [key: string]: Connection; } = {
+ discord: DiscordConnection
+};
+
+export { OauthCallbackHandlers };
\ No newline at end of file
diff --git a/src/api/util/index.ts b/src/api/util/index.ts
index ffad0607..49542ceb 100644
--- a/src/api/util/index.ts
+++ b/src/api/util/index.ts
@@ -7,4 +7,5 @@ export * from "./handlers/route";
export * from "./utility/String";
export * from "./handlers/Voice";
export * from "./utility/captcha";
-export * from "./utility/EmbedHandlers";
\ No newline at end of file
+export * from "./utility/EmbedHandlers";
+export * from "./handlers/Oauth";
\ No newline at end of file
|