Skip to content

Commit 62714ee

Browse files
committed
docs: update documentation
1 parent b64f4c0 commit 62714ee

File tree

2 files changed

+69
-59
lines changed

2 files changed

+69
-59
lines changed

docs/README.md

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ Welcome to the Remix Auth TOTP Documentation!
44

55
## List of Contents
66

7-
- [Live Demo](https://totp.devxo.workers.dev) - A live demo that displays the authentication flow.
8-
- [Getting Started](https://github.com/dev-xo/remix-auth-totp/tree/main/docs#getting-started) - A quick start guide to get you up and running.
9-
- [Examples](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/examples.md) - A list of community examples using Remix Auth TOTP.
10-
- [Customization](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md) - A detailed guide of all the available options and customizations.
11-
- [Cloudflare](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/cloudflare.md) - A guide to using Remix Auth TOTP with Cloudflare Workers.
7+
- [Live Demo](https://totp.devxo.workers.dev) - See it in action.
8+
- [Getting Started](https://github.com/dev-xo/remix-auth-totp/tree/main/docs#getting-started) - Quick setup guide.
9+
- [Examples](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/examples.md) - Community examples.
10+
- [Customization](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md) - Configuration options.
11+
- [Cloudflare](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/cloudflare.md) - Cloudflare Workers setup.
1212

1313
## Getting Started
1414

1515
Remix Auth TOTP exports one required method:
1616

1717
- `sendTOTP` - Sends the TOTP code to the user via email or any other method.
1818

19-
Here's a basic overview of the authentication flow.
19+
The authentication flow is simple:
2020

21-
1. Users Sign Up or Log In via email.
22-
2. The Strategy generates and securely sends a Time-Based One-Time Password (TOTP) to the user.
23-
3. The user submits the Code through a Form or Magic Link.
24-
4. The Strategy validates the TOTP Code, ensuring a secure authentication process.
21+
1. User enters their email.
22+
2. User receives a one-time code via email.
23+
3. User submits the code or clicks magic link.
24+
4. Code is validated and user is authenticated.
2525
<br />
2626

2727
> [!NOTE]
28-
> Remix Auth TOTP is Remix v2.0+ and React Router v7 compatible.
28+
> Remix Auth TOTP is compatible with Remix v2.0+ and React Router v7.
2929
3030
Let's see how we can implement the Strategy into our Remix App.
3131

@@ -37,48 +37,53 @@ Feel free to use any service of choice, such as [Resend](https://resend.com), [M
3737

3838
```ts
3939
export type SendEmailBody = {
40-
to: string | string[]
40+
sender: {
41+
name: string
42+
email: string
43+
}
44+
to: {
45+
name?: string
46+
email: string
47+
}[]
4148
subject: string
42-
html: string
43-
text?: string
49+
htmlContent: string
4450
}
4551

4652
export async function sendEmail(body: SendEmailBody) {
53+
const sender = {
54+
name: 'Your Name',
55+
email: 'your-email@example.com',
56+
}
57+
4758
return fetch(`https://any-email-service.com`, {
4859
method: 'POST',
4960
headers: {
50-
Authorization: `Bearer ${process.env.EMAIL_PROVIDER_API_KEY}`,
51-
'Content-Type': 'application/json',
61+
Accept: 'application/json',
62+
'content-type': 'application/json',
63+
'api-key': process.env.EMAIL_API_KEY as string,
5264
},
5365
body: JSON.stringify({ ...body }),
5466
})
5567
}
5668
```
5769

58-
For a simple implementation, check out the [Remix Starter Example](https://github.com/dev-xo/totp-starter-example/blob/main/app/modules/email/email.server.ts), which provides a clean and straightforward `sendEmail` function using [Resend](https://resend.com).
59-
60-
This implementation works with both Remix and React Router v7 applications.
70+
For a working example, see the [Remix Saas - Email](https://github.com/dev-xo/remix-saas/blob/main/app/lib/email/email.server.ts) implementation using Resend API.
6171

6272
## Session Storage
6373

64-
We'll require to initialize a new Session Storage to work with. This Session will store user data and everything related to authentication.
65-
66-
Create a file called `session.server.ts` wherever you want.<br />
67-
Implement the following code and replace the `secrets` property with a strong string into your `.env` file.
68-
69-
Same applies for Remix or React Router v7.
74+
We'll require to initialize a new Session Storage to work with. This Session will store user data and everything related to the TOTP authentication.
7075

7176
```ts
72-
// app/modules/auth/session.server.ts
73-
import { createCookieSessionStorage } from '@remix-run/node' // Or 'react-router'.
77+
// app/lib/auth-session.server.ts
78+
import { createCookieSessionStorage } from 'react-router' // Or '@remix-run'.
7479
7580
export const sessionStorage = createCookieSessionStorage({
7681
cookie: {
7782
name: '_auth',
7883
sameSite: 'lax',
7984
path: '/',
8085
httpOnly: true,
81-
secrets: [process.env.SESSION_SECRET || 'MY_STRONG_SECRET'],
86+
secrets: [process.env.SESSION_SECRET],
8287
secure: process.env.NODE_ENV === 'production',
8388
},
8489
})
@@ -92,28 +97,25 @@ Now that we have everything set up, we can start implementing the Strategy Insta
9297

9398
### 1. Implementing the Strategy Instance.
9499

95-
Create a file called `auth.server.ts` wherever you want. <br />
96-
97100
> [!IMPORTANT]
98-
> A random 64-character hexadecimal string is required to generate the TOTP codes. This string should be stored securely and not shared with anyone.
101+
> A random 64-character hexadecimal string is required to generate the TOTP codes.
99102
> You can use a site like https://www.grc.com/passwords.htm to generate a strong secret.
100103
101-
Implement the following code and replace the `secret` property with a string containing exactly 64 random hexadecimal characters (0-9 and A-F) into your `.env` file. An example is `928F416BAFC49B969E62052F00450B6E974B03E86DC6984D1FA787B7EA533227`.
104+
Add a 64-character hex string (0-9, A-F) as the `secret` property in your `.env` file. Example:
105+
`ENCRYPTION_SECRET=928F416BAFC49B969E62052F00450B6E974B03E86DC6984D1FA787B7EA533227`
102106

103107
```ts
104-
// app/modules/auth/auth.server.ts
108+
// app/lib/auth.server.ts
105109
import { Authenticator } from 'remix-auth'
106110
import { TOTPStrategy } from 'remix-auth-totp'
107-
import { sessionStorage } from './session.server'
108-
import { sendEmail } from './email.server'
109-
import { db } from '~/db'
111+
import { redirect } from 'react-router'
112+
import { getSession, commitSession } from '~/lib/auth-session.server'
110113

111114
type User = {
112-
id: string
113115
email: string
114116
}
115117

116-
export let authenticator = new Authenticator<User>(sessionStorage)
118+
export const authenticator = new Authenticator<User>()
117119

118120
authenticator.use(
119121
new TOTPStrategy(
@@ -131,7 +133,7 @@ authenticator.use(
131133
```
132134

133135
> [!TIP]
134-
> You can customize the cookie behavior by passing `cookieOptions` to the `sessionStorage` instance. Check [Customization](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md) to learn more.
136+
> You can customize the cookie behavior by passing `cookieOptions` to the `TOTPStrategy` instance. Check [Customization](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md) to learn more.
135137
136138
### 2: Implementing the Strategy Logic.
137139

@@ -143,8 +145,8 @@ authenticator.use(
143145
{
144146
...
145147
sendTOTP: async ({ email, code, magicLink }) => {
146-
// Send the TOTP code to the user.
147-
await sendEmail({ email, code, magicLink })
148+
// Send email with TOTP code.
149+
await sendAuthEmail({ email, code, magicLink })
148150
},
149151
},
150152
async ({ email }) => {},
@@ -156,6 +158,8 @@ authenticator.use(
156158

157159
The Strategy returns a `verify` method that allows handling our own logic. This includes creating the user, updating the session, etc.<br />
158160

161+
When using Cloudflare D1, you may want to perform the lookup in `action` or `loader` after committing the session, by passing the `context` binding to a `findOrCreateUserByEmail` function.
162+
159163
This should return the user data that will be stored in Session.
160164

161165
```ts
@@ -198,23 +202,21 @@ authenticator.use(
198202

199203
## Auth Routes
200204

201-
Last but not least, we'll require to create the routes that will handle the authentication flow. Create the following files inside the `app/routes` folder.
202-
203205
### `login.tsx`
204206

205207
```tsx
206208
// app/routes/login.tsx
207209
import { redirect } from 'react-router'
208210
import { useFetcher } from 'react-router'
209-
import { getSession } from '~/lib/session.server'
210-
import { authenticator } from '~/lib/auth.server'
211+
import { getSession } from '~/lib/auth-session.server'
212+
import { authenticator } from '~/lib/auth-server'
211213

212214
export async function loader({ request }: Route.LoaderArgs) {
213215
// Check for existing session.
214216
const session = await getSession(request.headers.get('Cookie'))
215217
const user = session.get('user')
216218

217-
// If the user is already authenticated, redirect to dashboard.
219+
// If the user is already authenticated, redirect to your authenticated route.
218220
if (user) return redirect('/dashboard')
219221

220222
return null
@@ -225,14 +227,14 @@ export async function action({ request }: Route.ActionArgs) {
225227
// Authenticate the user via TOTP (Form submission).
226228
return await authenticator.authenticate('TOTP', request)
227229
} catch (error) {
228-
console.log('error', error)
229-
230230
// The error from TOTP includes the redirect Response with the cookie.
231231
if (error instanceof Response) {
232232
return error
233233
}
234234

235235
// For other errors, return with error message.
236+
console.log('error', error)
237+
236238
return {
237239
error: 'An error occurred during login. Please try again.',
238240
}
@@ -267,13 +269,15 @@ export default function Login() {
267269

268270
### `verify.tsx`
269271

272+
For the verify route, we are leveraging `@mjackson/headers` to parse the cookie. Created by Michael Jackson, the CO-Founder of Remix/React Router.
273+
270274
```tsx
271275
// app/routes/verify.tsx
272276
import { redirect, useLoaderData } from 'react-router'
273277
import { Cookie } from '@mjackson/headers'
274278
import { Link, useFetcher } from 'react-router'
275279
import { useState } from 'react'
276-
import { getSession } from '~/lib/session.server'
280+
import { getSession } from '~/lib/auth-session.server'
277281
import { authenticator } from '~/lib/auth.server'
278282

279283
/**
@@ -300,7 +304,7 @@ export async function loader({ request }: Route.LoaderArgs) {
300304
if (token) {
301305
try {
302306
return await authenticator.authenticate('TOTP', request)
303-
} catch (error) {
307+
} catch (error: unknown) {
304308
if (error instanceof Response) return error
305309
if (error instanceof Error) return { error: error.message }
306310
return { error: 'Invalid TOTP' }
@@ -333,6 +337,7 @@ export async function action({ request }: Route.ActionArgs) {
333337
if (error instanceof Response) {
334338
const cookie = new Cookie(error.headers.get('Set-Cookie') || '')
335339
const totpCookie = cookie.get('_totp')
340+
336341
if (totpCookie) {
337342
const params = new URLSearchParams(totpCookie)
338343
return { error: params.get('error') }
@@ -351,7 +356,6 @@ export default function Verify() {
351356
const fetcher = useFetcher()
352357
const isSubmitting = fetcher.state !== 'idle' || fetcher.formData != null
353358

354-
const code = 'code' in loaderData ? loaderData.code : undefined
355359
const email = 'email' in loaderData ? loaderData.email : undefined
356360
const error = 'error' in loaderData ? loaderData.error : null
357361
const errors = fetcher.data?.error || error
@@ -388,7 +392,7 @@ export default function Verify() {
388392
```tsx
389393
// app/routes/dashboard.tsx
390394
import { Link } from 'react-router'
391-
import { getSession } from '../lib/session.server'
395+
import { getSession } from '~/lib/auth-session.server'
392396
import { redirect } from 'react-router'
393397
import { useLoaderData } from 'react-router'
394398

@@ -420,7 +424,7 @@ export default function Account() {
420424

421425
```tsx
422426
// app/routes/logout.tsx
423-
import { sessionStorage } from '~/lib/session.server'
427+
import { sessionStorage } from '~/lib/auth-session.server'
424428
import { redirect } from 'react-router'
425429

426430
export async function loader({ request }: Route.LoaderArgs) {
@@ -436,10 +440,16 @@ export async function loader({ request }: Route.LoaderArgs) {
436440
}
437441
```
438442

439-
Done! 🎉 Feel free to check the [Starter Example for React Router v7](https://github.com/dev-xo/remix-auth-totp-v4-starter) for a detailed implementation.
443+
## Next Steps
444+
445+
🎉 Done! You've completed the basic setup.
446+
447+
For a complete implementation example, check out the [React Router v7 Starter Template](https://github.com/dev-xo/remix-auth-totp-v4-starter).
448+
449+
## Configuration Options
440450

441-
## [Options and Customization](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md)
451+
The TOTP Strategy can be customized with various options to fit your needs. See the [customization documentation](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md) for:
442452

443-
The Strategy includes a few options that can be customized.
453+
## Support
444454

445-
You can find a detailed list of all the available options in the [customization](https://github.com/dev-xo/remix-auth-totp/blob/main/docs/customization.md) documentation.
455+
If you found **Remix Auth TOTP** helpful, please consider supporting it with a ⭐ [Star](https://github.com/dev-xo/remix-auth-totp).

docs/examples.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ A list of community examples using Remix Auth TOTP.
44

55
## v4.0 Examples
66

7-
- [React Router v7 - Starter](https://github.com/dev-xo/remix-auth-totp-v4-starter) by [@dev-xo](https://github.com/dev-xo): A straightforward database-less example App. It can also serve as a foundation for your Remix App or other projects.
7+
- [React Router v7 - Starter](https://github.com/dev-xo/remix-auth-totp-v4-starter) by [@dev-xo](https://github.com/dev-xo): A straightforward database-less example App. It can also serve as a foundation for your RR7 App or other projects.
88
- [React Router v7 - Drizzle ORM](https://github.com/CyrusVorwald/react-router-playground) by [@CyrusVorwald](https://github.com/CyrusVorwald): A simple to start, Drizzle + PostgreSQL example, that aims to be a playground for your React Router v7 app.
99

1010
## v3.0 Examples
1111

1212
These are examples for the v3.0+ release. The implementation is mostly compatible with v4.0, with slight differences in how the session is handled.
1313

14-
- [Remix Auth TOTP - Starter](https://github.com/dev-xo/totp-starter-example) by [@dev-xo](https://github.com/dev-xo): A straightforward Prisma ORM + SQLite example App. It can also serve as a foundation for your own projects or other examples.
14+
- [Remix Auth TOTP - Starter](https://github.com/dev-xo/totp-starter-example) by [@dev-xo](https://github.com/dev-xo): A straightforward Prisma ORM + SQLite example App. It can also serve as a foundation for your Remix App or other projects.
1515
- [Remix Auth TOTP + Remix Flat Routes](https://github.com/dev-xo/totp-flat-routes-example) by [@dev-xo](https://github.com/dev-xo): Remix Auth TOTP + Remix Flat Routes example App.
1616
- [Remix Auth TOTP + Conform](https://github.com/dev-xo/totp-conform-example) by [@dev-xo](https://github.com/dev-xo): Remix Auth TOTP + Conform example App.
1717
- [Remix Auth TOTP + Cloudflare](https://github.com/mw10013/remix-auth-totp-cloudflare-example) by [@mw10013](https://github.com/mw10013): Remix Auth TOTP + Cloudflare example App.

0 commit comments

Comments
 (0)