0.1.0 • Published 6 months ago

remix-session-csrf v0.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

remix-session-csrf

The CSRF helper from v6 of remix-utils published as a standalone package for Node.

Installation

npm install remix-session-csrf

Usage

Set up a session store:

// app/utils/session.server.ts

import { createCookieSessionStorage } from '@remix-run/node';

export const sessionStorage = createCookieSessionStorage({
    cookie: {
        name: '__session', // use any name you want here
        sameSite: 'lax', 
        path: '/',
        httpOnly: true, // for security reasons, make this cookie http only
        secrets: ['s3cr3t'], // replace this with an actual secret from env variable
        secure: process.env.NODE_ENV === 'production', // enable this in prod only
    },
});

export const { getSession, commitSession, destroySession } = sessionStorage;

Configure your root.tsx to generate a csrf token, and pass it to the client.

// app/root.tsx

import { json, type LoaderFunctionArgs } from '@remix-run/node';
import {
    AuthenticityTokenProvider,
    verifyAuthenticityToken,
} from 'remix-session-csrf';
import {
    // rest of imports
    useLoaderData,
} from '@remix-run/react';

import { commitSession, getSession } from '~/utils/session.server';

export async function loader({ request }: LoaderFunctionArgs) {
    const session = await getSession(request.headers.get('cookie'));
    const csrfToken = createAuthenticityToken(session);

    return json({
        csrfToken,
    }, {
        headers: {
            'Set-Cookie': await commitSession(session),
        },
    })
}

export default function App() {
    const { csrfToken } = await useLoaderData<typeof loader>();

    return (
        <AuthenticityTokenProvider token={csrfToken}>
            {/* App markup */}
        </AuthenticityTokenProvider>
    )
}

Then, add a token and validation to your form + action:

// app/routes/some-route.tsx

import { type ActionFunctionArgs } from '@remix-run/node';
import { Form } from '@remix-run/react';
import { 
    AuthenticityTokenInput, 
    verifyAuthenticityToken 
} from 'remix-session-csrf';

import { getSession } from '~/utils/session.server';

export async function action({ request }: ActionFunctionArgs)  {
    const session = await getSession(request.headers.get('Cookie'));
    await verifyAuthenticityToken(request, session);

    return {}
}

export default function Component() {
    return (
        <Form method="POST">
            <AuthenticityTokenInput />
            <input type="text" name="name">
            <button type="submit">Submit</button>
        </Form>
    )
}

You can also use the useAuthenticityToken hook to get the token and validation function:

// app/routes/some-route.tsx

import { type ActionFunctionArgs } from '@remix-run/node';
import { useFetcher } from '@remix-run/react';
import {
    useAuthenticityToken,
    verifyAuthenticityToken,
} from 'remix-session-csrf';

import { getSession } from '~/utils/session.server';

export async function action({ request }: ActionFunctionArgs) {
    const session = await getSession(request.headers.get('Cookie'));
    await verifyAuthenticityToken(request, session);

    return null;
}

export default function Component() {
    const fetcher = useFetcher();
    const csrf = useAuthenticityToken();

    return (
        <div>
            <button
                type="button"
                onClick={() => {
                    fetcher.submit({ csrf, name: 'Steve' }, { method: 'POST' });
                }}
            >
                Submit
            </button>
        </div>
    );
}
0.1.0

6 months ago

0.0.0

6 months ago