more csrf protection (i'm paranoid)
This commit is contained in:
parent
60f1beff17
commit
7b861454ff
23
app/root.jsx
23
app/root.jsx
|
@ -5,11 +5,16 @@ import {
|
|||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
useLoaderData,
|
||||
} from "@remix-run/react";
|
||||
import { useState } from "react";
|
||||
|
||||
import {
|
||||
AuthenticityTokenProvider,
|
||||
createAuthenticityToken,
|
||||
} from "remix-utils";
|
||||
import styles from "./styles/app.css";
|
||||
|
||||
import { json } from "@remix-run/node";
|
||||
import { getSession, commitSession } from "./sessions";
|
||||
export function links() {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
}
|
||||
|
@ -20,7 +25,17 @@ export const meta = () => ({
|
|||
viewport: "width=device-width,initial-scale=1",
|
||||
});
|
||||
|
||||
export async function loader({ request }) {
|
||||
let session = await getSession(request.headers.get("cookie"));
|
||||
let token = createAuthenticityToken(session);
|
||||
return json(
|
||||
{ csrf: token },
|
||||
{ headers: { "Set-Cookie": await commitSession(session) } }
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
let { csrf } = useLoaderData();
|
||||
const [dark, setDark] = useState(true);
|
||||
|
||||
return (
|
||||
|
@ -53,7 +68,9 @@ export default function App() {
|
|||
</svg>
|
||||
)}
|
||||
</button>
|
||||
<Outlet />
|
||||
<AuthenticityTokenProvider token={csrf}>
|
||||
<Outlet />
|
||||
</AuthenticityTokenProvider>
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
|
|
|
@ -5,6 +5,7 @@ import { prisma } from "~/db.server";
|
|||
import { Form } from "@remix-run/react";
|
||||
import { json } from "@remix-run/node";
|
||||
import { County } from "~/components/County";
|
||||
import { AuthenticityTokenInput, verifyAuthenticityToken } from "remix-utils";
|
||||
|
||||
const countys = [
|
||||
"Ahvenanmaa",
|
||||
|
@ -28,13 +29,19 @@ const countys = [
|
|||
];
|
||||
|
||||
export async function action({ request }) {
|
||||
const session = await getSession(request.headers.get("Cookie"));
|
||||
try {
|
||||
await verifyAuthenticityToken(request, session);
|
||||
} catch {
|
||||
throw new Error("something went wrong");
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const county = formData.get("county");
|
||||
|
||||
if (!county || typeof county !== "string" || !countys.includes(county))
|
||||
throw new Error("bad county");
|
||||
|
||||
const session = await getSession(request.headers.get("Cookie"));
|
||||
if (!session.has("userId"))
|
||||
throw new Error("OAuth token not found in cookie");
|
||||
|
||||
|
@ -149,25 +156,29 @@ export default function Index() {
|
|||
/>
|
||||
<h1>{data.me.username}</h1>
|
||||
{data.selfData ? (
|
||||
<Link
|
||||
<Form
|
||||
method="post"
|
||||
className="text-sky-400 hover:text-sky-600 hover:underline"
|
||||
to={"/update"}
|
||||
action="/update"
|
||||
>
|
||||
Update stats
|
||||
</Link>
|
||||
<AuthenticityTokenInput />
|
||||
<button type="submit">Update stats</button>
|
||||
</Form>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<Link
|
||||
<Form
|
||||
method="post"
|
||||
className="text-sky-400 hover:text-sky-600 hover:underline"
|
||||
to={"/logout"}
|
||||
action="/logout"
|
||||
>
|
||||
Logout
|
||||
</Link>
|
||||
<AuthenticityTokenInput />
|
||||
<button type="submit">Logout</button>
|
||||
</Form>
|
||||
</div>
|
||||
) : (
|
||||
<a
|
||||
href="https://osu.ppy.sh/oauth/authorize?client_id=20062&redirect_uri=http://localhost:3000/auth&response_type=code"
|
||||
href="https://osu.ppy.sh/oauth/authorize?client_id=20062&redirect_uri=http://localhost:3000/auth&response_type=code&state=dsadfwoefwpkf"
|
||||
referrerPolicy="no-referrer"
|
||||
className="rounded-full border border-ctp-surface0 bg-ctp-surface0 px-4 py-2 shadow-md
|
||||
shadow-ctp-overlay0 hover:bg-ctp-surface1 hover:shadow-ctp-overlay2"
|
||||
|
@ -186,6 +197,7 @@ export default function Index() {
|
|||
className="flex flex-col items-center justify-center space-y-4 p-4 text-center"
|
||||
method="post"
|
||||
>
|
||||
<AuthenticityTokenInput />
|
||||
<label>
|
||||
<details>
|
||||
<summary className="cursor-pointer text-2xl font-semibold tracking-wide">
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import { redirect } from "@remix-run/node";
|
||||
import { verifyAuthenticityToken } from "remix-utils";
|
||||
import { destroySession, getSession } from "~/sessions";
|
||||
|
||||
export async function loader({ request }) {
|
||||
export async function action({ request }) {
|
||||
const session = await getSession(request.headers.get("Cookie"));
|
||||
|
||||
try {
|
||||
await verifyAuthenticityToken(request, session);
|
||||
} catch {
|
||||
throw new Error("something went wrong");
|
||||
}
|
||||
|
||||
return redirect("/", {
|
||||
headers: {
|
||||
"Set-Cookie": await destroySession(session),
|
||||
|
|
|
@ -2,9 +2,16 @@ import { redirect } from "@remix-run/node";
|
|||
import { getSession } from "~/sessions";
|
||||
import { getMe } from "~/utils";
|
||||
import { prisma } from "~/db.server";
|
||||
import { verifyAuthenticityToken } from "remix-utils";
|
||||
|
||||
export async function loader({ request }) {
|
||||
export async function action({ request }) {
|
||||
const session = await getSession(request.headers.get("Cookie"));
|
||||
try {
|
||||
await verifyAuthenticityToken(request, session);
|
||||
} catch {
|
||||
throw new Error("something went wrong");
|
||||
}
|
||||
|
||||
if (!session.has("userId"))
|
||||
throw new Error("OAuth token not found in cookie");
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ const secret = process.env.COOKIE_SECRET;
|
|||
const { getSession, commitSession, destroySession } =
|
||||
createCookieSessionStorage({
|
||||
cookie: {
|
||||
name: "oauth",
|
||||
maxAge: 86400,
|
||||
secrets: [secret],
|
||||
path: "/",
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
"dotenv-cli": "^7.0.0",
|
||||
"isbot": "^3.6.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"remix-utils": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@catppuccin/tailwindcss": "^0.1.1",
|
||||
|
|
|
@ -17,6 +17,7 @@ specifiers:
|
|||
prisma: ^4.9.0
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
remix-utils: ^6.0.0
|
||||
tailwindcss: ^3.2.4
|
||||
|
||||
dependencies:
|
||||
|
@ -28,6 +29,7 @@ dependencies:
|
|||
isbot: 3.6.5
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
remix-utils: 6.0.0_gd6xnhzlbmgwjklncuy626fj3e
|
||||
|
||||
devDependencies:
|
||||
'@catppuccin/tailwindcss': 0.1.1
|
||||
|
@ -4665,6 +4667,16 @@ packages:
|
|||
side-channel: 1.0.4
|
||||
dev: true
|
||||
|
||||
/intl-parse-accept-language/1.0.0:
|
||||
resolution: {integrity: sha512-YFMSV91JNBOSjw1cOfw2tup6hDP7mkz+2AUV7W1L1AM6ntgI75qC1ZeFpjPGMrWp+upmBRTX2fJWQ8c7jsUWpA==}
|
||||
engines: {node: '>=14'}
|
||||
dev: false
|
||||
|
||||
/ip-regex/4.3.0:
|
||||
resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/ip/1.1.8:
|
||||
resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==}
|
||||
dev: true
|
||||
|
@ -4859,6 +4871,13 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-ip/3.1.0:
|
||||
resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ip-regex: 4.3.0
|
||||
dev: false
|
||||
|
||||
/is-map/2.0.2:
|
||||
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
|
||||
dev: true
|
||||
|
@ -6794,6 +6813,26 @@ packages:
|
|||
unified: 10.1.2
|
||||
dev: true
|
||||
|
||||
/remix-utils/6.0.0_gd6xnhzlbmgwjklncuy626fj3e:
|
||||
resolution: {integrity: sha512-S7Xec0YHZxGFEDawWpIbU7HQAZC0j51FmAvcCyBRuxjo71aAIMdmez47dgF8T91yxpHV1xlIKPL7LhBzmYsOZw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@remix-run/react': ^1.10.0
|
||||
'@remix-run/server-runtime': ^1.10.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
zod: ^3.19.1
|
||||
dependencies:
|
||||
'@remix-run/react': 1.10.1_biqbaboplfbrettd7655fr4n2y
|
||||
intl-parse-accept-language: 1.0.0
|
||||
is-ip: 3.1.0
|
||||
react: 18.2.0
|
||||
schema-dts: 1.1.0
|
||||
type-fest: 2.19.0
|
||||
uuid: 8.3.2
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
/repeat-element/1.1.4:
|
||||
resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -6957,6 +6996,12 @@ packages:
|
|||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
|
||||
/schema-dts/1.1.0:
|
||||
resolution: {integrity: sha512-vdmbs/5ycj4zyKpZIDqTcy+IZi4s7c38RVAYuDmRi7zgxUT8wRWPMLzg0jr7FjdVunYu9yZ00F3+XcZTTFcTOQ==}
|
||||
peerDependencies:
|
||||
typescript: '>=4.1.0'
|
||||
dev: false
|
||||
|
||||
/semver/5.7.1:
|
||||
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
|
||||
hasBin: true
|
||||
|
@ -7574,6 +7619,11 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/type-fest/2.19.0:
|
||||
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
||||
engines: {node: '>=12.20'}
|
||||
dev: false
|
||||
|
||||
/type-is/1.6.18:
|
||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
@ -7783,6 +7833,11 @@ packages:
|
|||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
/uuid/8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/uvu/0.5.6:
|
||||
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
Loading…
Reference in New Issue