Skip to content

Instantly share code, notes, and snippets.

@dev-xo
Last active April 1, 2024 23:42
Show Gist options
  • Save dev-xo/5acb37002cf7ef2b02dfd8f6e6b8b720 to your computer and use it in GitHub Desktop.
Save dev-xo/5acb37002cf7ef2b02dfd8f6e6b8b720 to your computer and use it in GitHub Desktop.
Remix Auth TOTP - Single-Route Authentication
import type { LoaderFunctionArgs, ActionFunctionArgs } from '@remix-run/node'
import { Form, useLoaderData } from '@remix-run/react'
import { json } from '@remix-run/node'
// Authenticator / Email Templates:
// https://github.com/dev-xo/totp-starter-example/tree/main/app/modules
import { authenticator } from '~/modules/auth/auth.server.ts'
import { getSession, commitSession } from '~/modules/auth/auth-session.server.ts'
export async function loader({ request }: LoaderFunctionArgs) {
await authenticator.isAuthenticated(request, {
successRedirect: '/admin',
})
const cookie = await getSession(request.headers.get('cookie'))
const authEmail = cookie.get('auth:email')
const authError = cookie.get(authenticator.sessionErrorKey)
// Commit session to clear any `flash` error message.
return json({ authEmail, authError } as const, {
headers: {
'set-cookie': await commitSession(cookie),
},
})
}
export async function action({ request }: ActionFunctionArgs) {
await authenticator.authenticate('TOTP', request, {
// The `successRedirect` path will be used to verify the OTP code.
// This could be `/auth/verify` or any other route that renders the verification form.
successRedirect: '/auth/login',
// The `failureRedirect` path will be used to render any possible error.
// If not provided, ErrorBoundary will be rendered instead.
failureRedirect: '/auth/login',
})
}
export default function Login() {
const { authEmail, authError } = useLoaderData<typeof loader>()
return (
<div className="mx-auto flex h-screen w-screen flex-col items-center justify-center">
{/* Email */}
{!authEmail && (
<Form method="POST">
<div className="flex flex-col">
<input type="email" name="email" placeholder="[email protected]" required />
</div>
<button type="submit">Continue with Email</button>
</Form>
)}
{/* Code Verification */}
{authEmail && (
<div className="flex flex-col">
<Form method="POST" autoComplete="off">
<input type="text" name="code" placeholder="Enter code..." required />
<button type="submit">Continue</button>
</Form>
{/* Request New Code. */}
{/* Email is already in session, so no input it's required. */}
<Form method="POST" autoComplete="off">
<button type="submit">Request New Code</button>
</Form>
</div>
)}
{/* Errors Handling. */}
{!authEmail && authError && <span>{authError.message}</span>}
{authEmail && authError && <span>{authError?.message}</span>}
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment