diff --git a/README.md b/README.md
index 9a0618a..e5c29fb 100644
--- a/README.md
+++ b/README.md
@@ -1,48 +1,17 @@
-
-
-
- Ditch analysis paralysis and start shipping Epic Web apps.
-
-
- This is an opinionated project starter and reference that allows teams to
- ship their ideas to production faster and on a more stable foundation based
- on the experience of Kent C. Dodds and
- contributors.
-
-
-
-```sh
-npx create-remix@latest --typescript --install --template epicweb-dev/epic-stack
-```
-
-[![The Epic Stack](https://github.com/epicweb-dev/epic-stack/assets/1500684/345a3947-54ad-481d-888a-dbc1d1f313c1)](https://www.epicweb.dev/epic-stack)
-
-[The Epic Stack](https://www.epicweb.dev/epic-stack)
-
-
-
-## Watch Kent's Introduction to The Epic Stack
-
-[![screenshot of a YouTube video](https://github.com/epicweb-dev/epic-stack/assets/1500684/6beafa78-41c6-47e1-b999-08d3d3e5cb57)](https://www.youtube.com/watch?v=yMK5SVRASxM)
-
-["The Epic Stack" by Kent C. Dodds at #RemixConf 2023 💿](https://www.youtube.com/watch?v=yMK5SVRASxM)
-
-## Docs
-
-[Read the docs](https://github.com/epicweb-dev/epic-stack/blob/main/docs)
-(please 🙏).
-
-## Support
-
-- 🆘 Join the
- [discussion on GitHub](https://github.com/epicweb-dev/epic-stack/discussions)
- and the [KCD Community on Discord](https://kcd.im/discord).
-- 💡 Create an
- [idea discussion](https://github.com/epicweb-dev/epic-stack/discussions/new?category=ideas)
- for suggestions.
-- 🐛 Open a [GitHub issue](https://github.com/epicweb-dev/epic-stack/issues) to
- report a bug.
-
-## Thanks
-
-You rock 🪨
+# Epic Stack with CSRF Protection
+
+This is an example of how to integrate the
+[`remix-utils`](https://github.com/sergiodxa/remix-utils) package utilities for
+[Cross-Site Request Forgery (CSRF)](https://en.wikipedia.org/wiki/Cross-site_request_forgery)
+protection with the Epic Stack. The easiest way to explore the example is to
+pull up
+[the commit history](https://github.com/kentcdodds/epic-stack-with-csrf/commits/main).
+
+Following the steps laid out in the Remix Utils docs is sufficient for this:
+
+1. Install `remix-utils`
+2. Generate the authenticity token in the `root.tsx` loader (be certain to
+ commit the session to set the cookie)
+3. Wrap the App in the `` and provide the token
+4. Render a Form with the `` component
+5. Verify in the Action using `verifyAuthenticityToken` and the session.
diff --git a/app/root.tsx b/app/root.tsx
index 05973ee..e5f395a 100644
--- a/app/root.tsx
+++ b/app/root.tsx
@@ -34,6 +34,8 @@ import { getUserImgSrc } from './utils/misc.ts'
import { useNonce } from './utils/nonce-provider.ts'
import { makeTimings, time } from './utils/timing.server.ts'
import { useOptionalUser, useUser } from './utils/user.ts'
+import { commitSession, getSession } from './utils/session.server.ts'
+import { AuthenticityTokenProvider, createAuthenticityToken } from 'remix-utils'
export const links: LinksFunction = () => {
return [
@@ -74,6 +76,8 @@ export const meta: V2_MetaFunction = () => {
}
export async function loader({ request }: DataFunctionArgs) {
+ const cookieSession = await getSession(request.headers.get('Cookie'))
+ const token = createAuthenticityToken(cookieSession)
const timings = makeTimings('root loader')
const userId = await time(() => getUserId(request), {
timings,
@@ -100,6 +104,7 @@ export async function loader({ request }: DataFunctionArgs) {
return json(
{
+ csrf: token,
user,
requestInfo: {
hints: getHints(request),
@@ -114,6 +119,7 @@ export async function loader({ request }: DataFunctionArgs) {
{
headers: {
'Server-Timing': timings.toString(),
+ 'Set-Cookie': await commitSession(cookieSession),
},
},
)
@@ -185,7 +191,17 @@ function App() {