Port auth

This commit is contained in:
Kalle (Sendou) 2021-11-27 16:08:32 +02:00
parent 3007c6aa38
commit 4dbc56e98c
10 changed files with 597 additions and 16 deletions

2
.env
View File

@ -5,7 +5,7 @@ DATABASE_URL=postgresql://sendou@localhost:5432/sendou_ink_trpc?schema=public
// you can get them by making an application on https://discord.com/developers
DISCORD_CLIENT_ID=581483359159582722
DISCORD_CLIENT_SECRET=qxFxBlALcRsqUG2m7WYLHJ8rzjbddBTx
DISCORD_CALLBACK_URL=http://localhost:3001/auth/discord/callback
DISCORD_CALLBACK_URL=http://localhost:3000/auth/discord/callback
COOKIE_SECRET=secret
FRONTEND_URL=http://localhost:3000

View File

@ -5,8 +5,5 @@ DATABASE_URL=postgresql://sendou@localhost:5432/sendou_ink?schema=public
// you can get them by making an application on https://discord.com/developers
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
DISCORD_CALLBACK_URL=http://localhost:3001/auth/discord/callback
DISCORD_CALLBACK_URL=http://localhost:3000/auth/discord/callback
COOKIE_SECRET=
FRONTEND_URL=http://localhost:3000
VITE_BACKEND_URL=http://localhost:3001

View File

@ -1,19 +1,20 @@
import { useUserContext } from "~/root";
import { DiscordIcon } from "../icons/Discord";
// TODO: redirect to same page on login
export function UserItem() {
const user = null;
const user = useUserContext();
if (user)
return (
<img
className="layout__avatar"
src={`https://cdn.discordapp.com/avatars/discordId/discordAvatar.png?size=80`}
src={`https://cdn.discordapp.com/avatars/${user.discordId}/${user.discordAvatar}.png?size=80`}
/>
);
return (
<form action="/" method="post" data-cy="log-in-form">
<form action="/auth/discord" method="post" data-cy="log-in-form">
<button
type="submit"
className="layout__log-in-button"

View File

@ -7,8 +7,9 @@ import {
Scripts,
ScrollRestoration,
useCatch,
useLoaderData,
} from "remix";
import type { LinksFunction } from "remix";
import type { LinksFunction, LoaderFunction } from "remix";
import normalizeStylesUrl from "~/styles/normalize.css";
import globalStylesUrl from "~/styles/global.css";
@ -23,12 +24,33 @@ export const links: LinksFunction = () => {
];
};
export const loader: LoaderFunction = ({ context }) => {
const { user } = context;
console.log({ user });
return user ?? null;
};
type LoggedInUser = {
id: number;
discordId: string;
discordAvatar: string;
} | null;
const UserContext = React.createContext<LoggedInUser>(null);
export const useUserContext = () => React.useContext<LoggedInUser>(UserContext);
export default function App() {
const data = useLoaderData();
return (
<Document>
<Layout>
<Outlet />
</Layout>
<UserContext.Provider value={data}>
<Layout>
<Outlet />
</Layout>
</UserContext.Provider>
</Document>
);
}

62
app/services/user.ts Normal file
View File

@ -0,0 +1,62 @@
import prisma from "../../prisma/client";
import type { Strategy as DiscordStrategy } from "passport-discord";
export async function upsertUser({
loggedInUser,
refreshToken,
}: {
loggedInUser: DiscordStrategy.Profile;
refreshToken: string;
}) {
return prisma.user.upsert({
create: {
discordId: loggedInUser.id,
discordName: loggedInUser.username,
discordDiscriminator: loggedInUser.discriminator,
discordAvatar: loggedInUser.avatar,
discordRefreshToken: refreshToken,
...parseConnections(loggedInUser.connections),
},
update: {
discordName: loggedInUser.username,
discordDiscriminator: loggedInUser.discriminator,
discordAvatar: loggedInUser.avatar,
discordRefreshToken: refreshToken,
...parseConnections(loggedInUser.connections),
},
where: {
discordId: loggedInUser.id,
},
});
}
function parseConnections(
connections: DiscordStrategy.ConnectionInfo[] | undefined
) {
if (!connections) return null;
const result: {
twitch?: string;
twitter?: string;
youtubeId?: string;
youtubeName?: string;
} = {};
for (const connection of connections) {
if (connection.visibility !== 1 || !connection.verified) continue;
switch (connection.type) {
case "twitch":
result.twitch = connection.name;
break;
case "twitter":
result.twitter = connection.name;
break;
case "youtube":
result.youtubeId = connection.id;
result.youtubeName = connection.name;
}
}
return result;
}

399
package-lock.json generated
View File

@ -12,9 +12,13 @@
"@remix-run/react": "^1.0.6",
"classnames": "^2.3.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"cross-env": "^7.0.3",
"express": "^4.17.1",
"express-session": "^1.17.2",
"morgan": "^1.10.0",
"passport": "^0.5.0",
"passport-discord": "^0.1.4",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"remix": "^1.0.6",
@ -23,9 +27,13 @@
"devDependencies": {
"@remix-run/dev": "^1.0.6",
"@types/compression": "^1.7.2",
"@types/cookie-parser": "^1.4.2",
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.4",
"@types/faker": "^5.5.9",
"@types/morgan": "^1.9.3",
"@types/passport": "^1.0.7",
"@types/passport-discord": "^0.1.5",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"concurrently": "^6.4.0",
@ -403,6 +411,15 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"node_modules/@types/cookie-parser": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
"integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/debug": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
@ -450,6 +467,15 @@
"@types/range-parser": "*"
}
},
"node_modules/@types/express-session": {
"version": "1.17.4",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz",
"integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/faker": {
"version": "5.5.9",
"resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.9.tgz",
@ -542,12 +568,52 @@
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true
},
"node_modules/@types/oauth": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz",
"integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"node_modules/@types/passport": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
"integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/passport-discord": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@types/passport-discord/-/passport-discord-0.1.5.tgz",
"integrity": "sha512-hq/EcxU+gKaGdgTAX9LDMEt+/FmDJphq84qRUt5jt553a5RPCwxonb7QwOZUO3XBhzLTXIbJmPQd5/5bTXJnyA==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/passport": "*",
"@types/passport-oauth2": "*"
}
},
"node_modules/@types/passport-oauth2": {
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.11.tgz",
"integrity": "sha512-KUNwmGhe/3xPbjkzkPwwcPmyFwfyiSgtV1qOrPBLaU4i4q9GSCdAOyCbkFG0gUxAyEmYwqo9OAF/rjPjJ6ImdA==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/oauth": "*",
"@types/passport": "*"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.4",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
@ -796,6 +862,14 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/base64url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -1318,6 +1392,23 @@
"node": ">= 0.6"
}
},
"node_modules/cookie-parser": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
"dependencies": {
"cookie": "0.4.1",
"cookie-signature": "1.0.6"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/cookie-parser/node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/cookie-signature": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.1.0.tgz",
@ -2040,6 +2131,56 @@
"node": ">= 0.10.0"
}
},
"node_modules/express-session": {
"version": "1.17.2",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
"integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
"dependencies": {
"cookie": "0.4.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/express-session/node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/express-session/node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/express-session/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/express/node_modules/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
@ -4318,6 +4459,11 @@
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
"dev": true
},
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -4481,6 +4627,57 @@
"node": ">= 0.8"
}
},
"node_modules/passport": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz",
"integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==",
"dependencies": {
"passport-strategy": "1.x.x",
"pause": "0.0.1"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-discord": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/passport-discord/-/passport-discord-0.1.4.tgz",
"integrity": "sha512-VJWPYqSOmh7SaCLw/C+k1ZqCzJnn2frrmQRx1YrcPJ3MQ+Oa31XclbbmqFICSvl8xv3Fqd6YWQ4H4p1MpIN9rA==",
"dependencies": {
"passport-oauth2": "^1.5.0"
}
},
"node_modules/passport-oauth2": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz",
"integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==",
"dependencies": {
"base64url": "3.x.x",
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x",
"utils-merge": "1.x.x"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -4527,6 +4724,11 @@
"node": ">=8"
}
},
"node_modules/pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
},
"node_modules/periscopic": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.0.4.tgz",
@ -4745,6 +4947,14 @@
"node": ">=8"
}
},
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -5954,6 +6164,22 @@
"node": ">=4.2.0"
}
},
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/uid2": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
"integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
},
"node_modules/unified": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz",
@ -6768,6 +6994,15 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"@types/cookie-parser": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
"integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/debug": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz",
@ -6815,6 +7050,15 @@
"@types/range-parser": "*"
}
},
"@types/express-session": {
"version": "1.17.4",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz",
"integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/faker": {
"version": "5.5.9",
"resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.9.tgz",
@ -6907,12 +7151,52 @@
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true
},
"@types/oauth": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz",
"integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"@types/passport": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
"integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
"dev": true,
"requires": {
"@types/express": "*"
}
},
"@types/passport-discord": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@types/passport-discord/-/passport-discord-0.1.5.tgz",
"integrity": "sha512-hq/EcxU+gKaGdgTAX9LDMEt+/FmDJphq84qRUt5jt553a5RPCwxonb7QwOZUO3XBhzLTXIbJmPQd5/5bTXJnyA==",
"dev": true,
"requires": {
"@types/express": "*",
"@types/passport": "*",
"@types/passport-oauth2": "*"
}
},
"@types/passport-oauth2": {
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.11.tgz",
"integrity": "sha512-KUNwmGhe/3xPbjkzkPwwcPmyFwfyiSgtV1qOrPBLaU4i4q9GSCdAOyCbkFG0gUxAyEmYwqo9OAF/rjPjJ6ImdA==",
"dev": true,
"requires": {
"@types/express": "*",
"@types/oauth": "*",
"@types/passport": "*"
}
},
"@types/prop-types": {
"version": "15.7.4",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
@ -7115,6 +7399,11 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"base64url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -7513,6 +7802,22 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"cookie-parser": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
"requires": {
"cookie": "0.4.1",
"cookie-signature": "1.0.6"
},
"dependencies": {
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
}
}
},
"cookie-signature": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.1.0.tgz",
@ -8023,6 +8328,38 @@
}
}
},
"express-session": {
"version": "1.17.2",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
"integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
"requires": {
"cookie": "0.4.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"dependencies": {
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@ -9622,6 +9959,11 @@
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
"dev": true
},
"oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -9736,6 +10078,40 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"passport": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.5.0.tgz",
"integrity": "sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg==",
"requires": {
"passport-strategy": "1.x.x",
"pause": "0.0.1"
}
},
"passport-discord": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/passport-discord/-/passport-discord-0.1.4.tgz",
"integrity": "sha512-VJWPYqSOmh7SaCLw/C+k1ZqCzJnn2frrmQRx1YrcPJ3MQ+Oa31XclbbmqFICSvl8xv3Fqd6YWQ4H4p1MpIN9rA==",
"requires": {
"passport-oauth2": "^1.5.0"
}
},
"passport-oauth2": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz",
"integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==",
"requires": {
"base64url": "3.x.x",
"oauth": "0.9.x",
"passport-strategy": "1.x.x",
"uid2": "0.0.x",
"utils-merge": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -9770,6 +10146,11 @@
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
"pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
},
"periscopic": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.0.4.tgz",
@ -9917,6 +10298,11 @@
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -10825,6 +11211,19 @@
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
"dev": true
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"uid2": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
"integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
},
"unified": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.1.tgz",

View File

@ -25,9 +25,13 @@
"@remix-run/react": "^1.0.6",
"classnames": "^2.3.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"cross-env": "^7.0.3",
"express": "^4.17.1",
"express-session": "^1.17.2",
"morgan": "^1.10.0",
"passport": "^0.5.0",
"passport-discord": "^0.1.4",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"remix": "^1.0.6",
@ -36,9 +40,13 @@
"devDependencies": {
"@remix-run/dev": "^1.0.6",
"@types/compression": "^1.7.2",
"@types/cookie-parser": "^1.4.2",
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.4",
"@types/faker": "^5.5.9",
"@types/morgan": "^1.9.3",
"@types/passport": "^1.0.7",
"@types/passport-discord": "^0.1.5",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"concurrently": "^6.4.0",

View File

@ -1,5 +1,4 @@
import pkg from "@prisma/client";
const { PrismaClient } = pkg;
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();

74
server/auth.ts Normal file
View File

@ -0,0 +1,74 @@
import type { Express } from "express";
import invariant from "tiny-invariant";
import session from "express-session";
import cookieParser from "cookie-parser";
import passport from "passport";
import { Strategy as DiscordStrategy } from "passport-discord";
import { upsertUser } from "../app/services/user";
export function setUpAuth(app: Express): void {
invariant(
process.env.DISCORD_CLIENT_ID,
"env var DISCORD_CLIENT_ID undefined"
);
invariant(
process.env.DISCORD_CLIENT_SECRET,
"env var DISCORD_CLIENT_SECRET undefined"
);
invariant(
process.env.DISCORD_CALLBACK_URL,
"env var DISCORD_CALLBACK_URL undefined"
);
invariant(process.env.COOKIE_SECRET, "env var COOKIE_SECRET undefined");
passport.use(
new DiscordStrategy(
{
clientID: process.env.DISCORD_CLIENT_ID,
clientSecret: process.env.DISCORD_CLIENT_SECRET,
callbackURL: process.env.DISCORD_CALLBACK_URL,
scope: ["identify", "connections"],
},
function (_accessToken, refreshToken, loggedInUser, cb) {
upsertUser({ loggedInUser, refreshToken })
.then((user) => {
return cb(null, {
id: user.id,
discordId: user.discordId,
discordAvatar: user.discordAvatar,
});
})
.catch((err) => cb(err));
}
)
);
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
// @ts-expect-error it is guaranteed it's of a certain shape without an extra check
done(null, user);
});
app.use(cookieParser());
app.use(
session({
secret: process.env.COOKIE_SECRET,
resave: true,
saveUninitialized: true,
})
);
app.use(passport.initialize());
app.use(passport.session());
app.post("/auth/discord", passport.authenticate("discord"));
app.get(
"/auth/discord/callback",
passport.authenticate("discord", {
failureRedirect: "/login",
// TODO: fix for prod + redirect to same page
successRedirect: "http://localhost:3000",
})
);
}

View File

@ -3,6 +3,7 @@ import express from "express";
import compression from "compression";
import morgan from "morgan";
import { createRequestHandler } from "@remix-run/express";
import { setUpAuth } from "./auth";
const MODE = process.env.NODE_ENV;
const BUILD_DIR = path.join(process.cwd(), "server/build");
@ -17,14 +18,32 @@ app.use(express.static("public", { maxAge: "1h" }));
app.use(express.static("public/build", { immutable: true, maxAge: "1y" }));
app.use(morgan("tiny"));
try {
setUpAuth(app);
} catch (err) {
console.error(err);
}
function userToContext(req: Express.Request) {
return { user: req.user };
}
app.all(
"*",
MODE === "production"
? createRequestHandler({ build: require("./build") })
? createRequestHandler({
build: require("./build"),
getLoadContext: userToContext,
})
: (req, res, next) => {
purgeRequireCache();
const build = require("./build");
return createRequestHandler({ build, mode: MODE })(req, res, next);
return createRequestHandler({
build,
mode: MODE,
getLoadContext: userToContext,
})(req, res, next);
}
);