Zero-trust auth for the Edge.
Drop Authepy directly into your Next.js Server Actions. Execute cryptographic OTP verification entirely on Vercel or Cloudflare's Edge network—keeping your client bundle at exactly 0kB.
The Server Actions
By utilizing Next.js Server Actions, your API keys never leave the server.
Authepy extracts the data directly from the native FormData object and communicates securely with our edge router using standard REST protocols.
Security Primitives
- ✓ Protected by sliding token-bucket rate limits
- ✓ Seamless integration with Next.js cookies
import { cookies } from 'next/headers';
const AUTHEPY_API = 'https://api.authepy.com/api';
// 1. Dispatch the OTP
export async function sendOTP(prevState: any, formData: FormData) {
const email = formData.get('email');
const res = await fetch(`${AUTHEPY_API}/otp/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ email })
});
const data = await res.json();
if (data.success) return { step: 'verify', email, requestId: data.requestId };
return { error: data.error };
}
// 2. Verify the User's Input
export async function verifyOTP(prevState: any, formData: FormData) {
const requestId = formData.get('requestId');
const userGuess = formData.get('code');
const res = await fetch(`${AUTHEPY_API}/otp/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ requestId, userGuess })
});
const data = await res.json();
if (data.success) {
const cookieStore = await cookies();
cookieStore.set('auth_session', 'secure-jwt', { httpOnly: true, secure: true });
return { success: true };
}
return { error: data.error || "Invalid verification code", step: 'verify', requestId };
} The React Form
Wire the actions directly into your React forms using useActionState.
Because we use native HTML forms, the authentication flow works even if JavaScript fails to load or hydrate on the client.
import { useActionState } from 'react';
import { sendOTP, verifyOTP } from '../actions/auth';
export default function Login() {
const [state, action, isPending] = useActionState(sendOTP, null);
const [verifyState, verifyAction, isVerifyPending] = useActionState(verifyOTP, null);
if (verifyState?.success) {
return <h2>Authentication Successful! Redirecting...</h2>;
}
if (state?.step === 'verify' || verifyState?.step === 'verify') {
return (
<form action={verifyAction} className="flex flex-col gap-4 max-w-sm">
<input type="hidden" name="requestId" value={state?.requestId || verifyState?.requestId || ''} />
<input name="code" placeholder="000000" maxLength={6} required />
<button type="submit" disabled={isVerifyPending}>
{isVerifyPending ? 'Verifying...' : 'Verify Code'}
</button>
{verifyState?.error && <p style={{ color: 'red' }}>{verifyState.error}</p>}
</form>
);
}
return (
<form action={action} className="flex flex-col gap-4 max-w-sm">
<input name="email" type="email" placeholder="name@company.com" required />
<button disabled={isPending}>{isPending ? 'Sending OTP...' : 'Login'}</button>
{state?.error && <p style={{ color: 'red' }}>{state.error}</p>}
</form>
);
} Using Auth.js?
Bypass complex Nodemailer database adapters. Drop Authepy into your existing NextAuth v5 configuration as a pure CredentialsProvider.
Initialize your Edge setup.
Stop managing unsecure database tokens. Generate your API keys and lock down your Next.js architecture in minutes.