Supabase magic link auth flow lengkap
Login passwordless dengan Supabase magic link — kirim email, verify token, set session. Cocok buat MVP yang gak mau ribet password reset.
Dipublikasikan 6 Juni 2026
Password lupa, lupa lagi, reset email gak dateng. Magic link bikin login flow yang lebih simpel: tinggal kasih email, cek inbox, klik link, masuk. Snippet ini integrasi Supabase Auth di Next.js — server-side aman, client-side smooth.
Kode
// lib/supabase.ts
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";
export function createSupabaseServer() {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
cookieStore.set({ name, value, ...options });
},
remove(name: string, options: CookieOptions) {
cookieStore.set({ name, value: "", ...options, maxAge: 0 });
},
},
}
);
}
// app/login/actions.ts — Server Action untuk kirim magic link
"use server";
import { createSupabaseServer } from "@/lib/supabase";
import { redirect } from "next/navigation";
export async function sendMagicLink(formData: FormData) {
const email = String(formData.get("email") ?? "").trim();
if (!email || !email.includes("@")) {
return { error: "Email tidak valid" };
}
const supabase = createSupabaseServer();
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
shouldCreateUser: true,
emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
},
});
if (error) return { error: error.message };
redirect(`/login/cek-email?email=${encodeURIComponent(email)}`);
}
// app/auth/callback/route.ts — handle redirect dari email
import { createSupabaseServer } from "@/lib/supabase";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
const next = searchParams.get("next") ?? "/dashboard";
if (!code) {
return NextResponse.redirect(`${origin}/login?error=missing-code`);
}
const supabase = createSupabaseServer();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (error) {
return NextResponse.redirect(`${origin}/login?error=invalid-link`);
}
return NextResponse.redirect(`${origin}${next}`);
}
Pemakaian
// app/login/page.tsx — form login
import { sendMagicLink } from "./actions";
export default function LoginPage() {
return (
<form action={sendMagicLink}>
<input
name="email"
type="email"
required
placeholder="[email protected]"
autoComplete="email"
/>
<button type="submit">Kirim link login</button>
</form>
);
}
// app/login/cek-email/page.tsx — instruksi setelah submit
export default function CekEmailPage({
searchParams,
}: {
searchParams: { email?: string };
}) {
return (
<div>
<h1>Cek inbox kamu</h1>
<p>Link login dikirim ke <strong>{searchParams.email}</strong>.</p>
<p>Klik link di email untuk masuk. Link valid 1 jam.</p>
</div>
);
}
Kapan dipakai
- MVP / produk awal yang fokus iterasi cepat, belum mau handle reset password.
- B2B SaaS — user korporat biasanya bisa akses email kerja dengan cepat.
- Aplikasi internal team — kombinasi dengan domain restriction (
@perusahaan.idonly). - Onboarding flow — kurangi friction signup, password bisa di-add belakangan.
Catatan
- Email deliverability krusial — Supabase default SMTP rate-limited (3 email/jam). Setup custom SMTP (Resend, Postmark, SendGrid) sebelum production.
- Same-device limit — magic link expect dibuka di browser yang sama dengan submit form. Kalau user submit di desktop tapi buka email di HP, butuh OTP flow alternatif.
- PKCE flow —
@supabase/ssrpakai PKCE by default. Code di URL bukan token, jadi aman walaupun di-share. Token exchange terjadi server-side. - RLS check — pastikan Row Level Security policies aktif di table sensitif, jangan andalkan auth check di app saja.
Kalau user laporan “link expired” padahal baru terima — cek timezone server. Default Supabase TTL 1 jam dari email sent, kalau server clock drift bisa kacau.
# tags
supabasemagic-linkpasswordlessauthemail
Ditulis oleh Asti Larasati · 6 Juni 2026