diff --git a/docs/pages/getting-started/authentication/email.mdx b/docs/pages/getting-started/authentication/email.mdx index 7e0248efda..e03241ad7f 100644 --- a/docs/pages/getting-started/authentication/email.mdx +++ b/docs/pages/getting-started/authentication/email.mdx @@ -162,6 +162,24 @@ export { handle } from "./auth" ``` + +```ts filename="./server.ts" +import express from 'express'; +import dotenv from 'dotenv'; +import authRoutes from './routes/authRoutes'; + +dotenv.config(); +const app = express(); + +app.use(express.json()); +app.use('/auth', authRoutes); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`Server running on http://localhost:${PORT}`); +}); +``` + ### Add Signin Button @@ -226,6 +244,21 @@ export default component$(() => { ``` + + +```ts filename="routes/authRoutes.ts" + +import { Router } from 'express'; +import { sendMagicLink, verifyMagicLink } from '../controllers/authController'; + +const router = Router(); + +router.post('/login', sendMagicLink); +router.get('/verify', verifyMagicLink); + +export default router; +``` + ### Signin @@ -316,6 +349,54 @@ export { handle } from "./auth" ``` + + +```ts filename="controllers/authController.ts" + +import { Request, Response } from 'express'; +import jwt from 'jsonwebtoken'; +import { Resend } from 'resend'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const resend = new Resend(process.env.RESEND_API_KEY || ''); + +export const sendMagicLink = async (req: Request, res: Response) => { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ error: 'Email is required' }); + } + + const token = jwt.sign({ email }, process.env.JWT_SECRET as string, { expiresIn: '15m' }); + const magicLink = `${process.env.BASE_URL}/auth/verify?token=${token}`; + + try { + await resend.emails.send({ + from: 'YourApp ', + to: email, + subject: 'Login Link', + html: `

Click to log in: ${magicLink}

`, + }); + res.status(200).json({ message: 'Magic link sent' }); + } catch (error) { + res.status(500).json({ error: 'Failed to send email' }); + } +}; + +export const verifyMagicLink = (req: Request, res: Response) => { + const { token } = req.query; + + try { + const decoded = jwt.verify(token as string, process.env.JWT_SECRET as string) as { email: string }; + res.status(200).json({ message: 'Authenticated', email: decoded.email }); + } catch (err) { + res.status(401).json({ error: 'Invalid or expired token' }); + } +}; +``` +
### Add Signin Button @@ -404,6 +485,43 @@ export default component$(() => { ``` + + +```tsx filename="Login.tsx" +import { useState } from 'react'; +import axios from 'axios'; + +const Login = () => { + const [email, setEmail] = useState(''); + const [message, setMessage] = useState(''); + + const handleSendLink = async () => { + try { + const res = await axios.post('/auth/login', { email }); + setMessage(res.data.message); + } catch (err: any) { + setMessage(err.response?.data?.error || 'Error sending link'); + } + }; + + return ( +
+

Login with Magic Link

+ setEmail(e.target.value)} + /> + +

{message}

+
+ ); +}; + +export default Login; +``` +
### Signin