Skip to content

Commit

Permalink
add discord oauth provider
Browse files Browse the repository at this point in the history
  • Loading branch information
nichtsam committed Mar 23, 2024
1 parent b47977f commit 4d3b575
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 56 deletions.
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ CSRF_SECRET="csrf_secret"
GITHUB_CLIENT_ID="MOCK_GITHUB_CLIENT_ID"
GITHUB_CLIENT_SECRET="MOCK_GITHUB_CLIENT_SECRET"

# Discord Developer Portal > Applications > New Application
# https://discord.com/developers/applications
DISCORD_CLIENT_ID="MOCK_DISCORD_CLIENT_ID"
DISCORD_CLIENT_SECRET="MOCK_DISCORD_CLIENT_SECRET"
DISCORD_BOT_TOKEN="MOCK_DISCORD_BOT_TOKEN"

# turso db show --url <database-name>
TURSO_DB_URL="file:local/dev.db"
# turso db tokens create <database-name>
Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth.login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
providerNames,
} from "#app/utils/connections.tsx";
import type {
DataFunctionArgs,
HeadersFunction,
LoaderFunctionArgs,
MetaFunction,
} from "@remix-run/node";

Expand All @@ -31,7 +31,7 @@ export const headers: HeadersFunction = () => ({
Vary: "Cookie",
});

export async function loader({ request }: DataFunctionArgs) {
export async function loader({ request }: LoaderFunctionArgs) {
await requireAnonymous(request);

return null;
Expand Down
4 changes: 2 additions & 2 deletions app/routes/_auth.onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { onboardingCookie } from "#app/utils/auth.onboarding.server.ts";
import type { SubmissionResult } from "@conform-to/react";
import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import type { ActionFunctionArgs, DataFunctionArgs } from "@remix-run/node";
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form, useActionData, useLoaderData } from "@remix-run/react";
import { z } from "zod";
Expand All @@ -47,7 +47,7 @@ const requireData = async (request: Request) => {
}
};

export const loader = async ({ request }: DataFunctionArgs) => {
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { profile } = await requireData(request);

const prefill = profile;
Expand Down
4 changes: 2 additions & 2 deletions app/routes/auth.$provider.callback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import {
import { ProviderNameSchema } from "#app/utils/connections.tsx";
import { db } from "#app/utils/db.server.ts";
import { combineHeaders, destroySession } from "#app/utils/misc.ts";
import { redirect, type DataFunctionArgs } from "@remix-run/node";
import { redirect, type LoaderFunctionArgs } from "@remix-run/node";
import { connectionTable, sessionTable } from "#drizzle/schema.ts";
import { onboardingCookie } from "#app/utils/auth.onboarding.server.ts";

export const loader = async ({ request, params }: DataFunctionArgs) => {
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const providerName = ProviderNameSchema.parse(params.provider);

const profile = await authenticator.authenticate(providerName, request, {
Expand Down
4 changes: 2 additions & 2 deletions app/routes/auth.$provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import {
} from "#app/components/error-boundary.tsx";
import { authenticator } from "#app/utils/connections.server.ts";
import { ProviderNameSchema } from "#app/utils/connections.tsx";
import type { DataFunctionArgs } from "@remix-run/node";
import type { ActionFunctionArgs } from "@remix-run/node";

export const action = async ({ request, params }: DataFunctionArgs) => {
export const action = async ({ request, params }: ActionFunctionArgs) => {
const providerName = ProviderNameSchema.parse(params.provider);

return await authenticator.authenticate(providerName, request);
Expand Down
4 changes: 2 additions & 2 deletions app/routes/settings.profile.connections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useDoubleCheck } from "#app/utils/misc.ts";
import { json } from "@remix-run/node";
import type {
MetaFunction,
DataFunctionArgs,
LoaderFunctionArgs,
ActionFunctionArgs,
} from "@remix-run/node";
import { AuthenticityTokenInput } from "remix-utils/csrf/react";
Expand Down Expand Up @@ -64,7 +64,7 @@ export const meta: MetaFunction = () => {
];
};

export async function loader({ request }: DataFunctionArgs) {
export async function loader({ request }: LoaderFunctionArgs) {
const userId = await requireUserId(request);
const connections = await getConnections(userId);

Expand Down
2 changes: 2 additions & 0 deletions app/utils/connections.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { ProviderName } from "./connections.tsx";
import { GitHubProvider } from "./providers/github.server.ts";
import type { AuthProvider, ProviderUser } from "./providers/model.ts";
import { Authenticator } from "remix-auth";
import { DiscordProvider } from "./providers/discord.server.ts";

export const connectionSessionStorage = createCookieSessionStorage({
cookie: {
Expand All @@ -18,6 +19,7 @@ export const connectionSessionStorage = createCookieSessionStorage({

export const providers: Record<ProviderName, AuthProvider> = {
github: new GitHubProvider(),
discord: new DiscordProvider(),
};

export const authenticator = new Authenticator<ProviderUser>(
Expand Down
16 changes: 12 additions & 4 deletions app/utils/connections.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { StatusButton } from "#app/components/status-button.tsx";
import { GitHubLogoIcon } from "@radix-ui/react-icons";
import { DiscordLogoIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
import { Form } from "@remix-run/react";
import { GitHubStrategyDefaultName } from "remix-auth-github";
import { SocialsProvider } from "remix-auth-socials";
import { z } from "zod";
import { useIsPending } from "./misc.ts";

export const GITHUB_PROVIDER_NAME = GitHubStrategyDefaultName;
export const GITHUB_PROVIDER_NAME = SocialsProvider.GITHUB;
export const DISCORD_PROVIDER_NAME = SocialsProvider.DISCORD;

export const providerNames = [GITHUB_PROVIDER_NAME] as const;
export const providerNames = [
GITHUB_PROVIDER_NAME,
DISCORD_PROVIDER_NAME,
] as const;
export const ProviderNameSchema = z.enum(providerNames);
export type ProviderName = z.infer<typeof ProviderNameSchema>;

Expand All @@ -20,6 +24,10 @@ export const providerConfigs: Record<ProviderName, ProviderConfig> = {
label: "Github",
icon: <GitHubLogoIcon className="inline-block" />,
},
[DISCORD_PROVIDER_NAME]: {
label: "Discord",
icon: <DiscordLogoIcon className="inline-block" />,
},
};

export const ProviderConnectionForm = ({
Expand Down
4 changes: 4 additions & 0 deletions app/utils/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const envSchema = z.object({
GITHUB_CLIENT_ID: z.string(),
GITHUB_CLIENT_SECRET: z.string(),

DISCORD_CLIENT_ID: z.string(),
DISCORD_CLIENT_SECRET: z.string(),
DISCORD_BOT_TOKEN: z.string(),

TURSO_DB_URL: z.string().url(),
TURSO_DB_AUTH_TOKEN: z.string(),
});
Expand Down
73 changes: 73 additions & 0 deletions app/utils/providers/discord.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { type DiscordProfile, DiscordStrategy } from "remix-auth-socials";
import type { AuthProvider } from "./model.ts";
import { env } from "../env.server.ts";
import { z } from "zod";

const DiscordUserSchema = z.object({ username: z.string() });
type DiscordUser = z.infer<typeof DiscordUserSchema>;
const getDisplayName = (user: DiscordUser) => user.username;
const getImageUr = (profile: DiscordProfile) => {
const avaterHash = profile.photos?.[0].value;
if (!avaterHash) {
return undefined;
}

return `https://cdn.discordapp.com/avatars/${profile.id}/${avaterHash}.png`;
};

export class DiscordProvider implements AuthProvider {
getAuthStrategy() {
return new DiscordStrategy(
{
clientID: env.DISCORD_CLIENT_ID,
clientSecret: env.DISCORD_CLIENT_SECRET,
callbackURL: "/auth/discord/callback",
scope: ["identify", "email", "guilds"],
},
async ({ profile }) => {
const id = profile.id;
const email = profile.__json.email;
if (!email) {
throw new Error("Email is a must");
}
const username = profile.__json.username;
const name = profile.displayName;
const imageUrl = getImageUr(profile);

return {
id,
email,
username,
name,
imageUrl,
};
},
);
}

async resolveConnectionInfo(providerId: string) {
// TODO: cache this
const response = await fetch(
`https://discord.com/api/users/${providerId}`,
{
headers: {
Authorization: `Bot ${env.DISCORD_BOT_TOKEN}`,
},
},
);
const rawJson = await response.json();
const result = DiscordUserSchema.safeParse(rawJson);

if (!result.success) {
return {
connectionUserDisplayName: "Unknown" as const,
profileLink: null,
};
}

return {
connectionUserDisplayName: getDisplayName(result.data),
profileLink: null,
};
}
}
2 changes: 1 addition & 1 deletion app/utils/providers/github.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GitHubStrategy } from "remix-auth-github";
import { GitHubStrategy } from "remix-auth-socials";
import type { AuthProvider } from "./model.ts";
import { env } from "../env.server.ts";
import { z } from "zod";
Expand Down
2 changes: 1 addition & 1 deletion app/utils/providers/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface AuthProvider {
resolveConnectionInfo(profileId: string): Promise<
| {
connectionUserDisplayName: string;
profileLink: string;
profileLink: string | null;
}
| {
connectionUserDisplayName: "Unknown";
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"rehype-slug": "6.0.0",
"remark-mdx-images": "2.0.0",
"remix-auth": "3.6.0",
"remix-auth-github": "1.6.0",
"remix-auth-socials": "2.0.6",
"remix-utils": "7.5.0",
"source-map-support": "0.5.21",
"spin-delay": "1.2.0",
Expand Down
Loading

0 comments on commit 4d3b575

Please sign in to comment.