Vorgefertigte Anmeldeseiten für Next.js
Acht kleine Dateien ermöglichen einer Next.js-App einen vollständigen Anmeldevorgang, einschließlich der Seiten, auf die die Links in Aurabase-E-Mails verweisen.
8 Min. gelesen·Ebene Mittelstufe·Überarbeitet am 25.10.2026
Dieser englische Text wurde automatisch aus dem französischen Original generiert und wurde noch nicht überprüft.
Die englische Quelle hat sich seit dieser Übersetzung geändert. Einige Informationen sind möglicherweise veraltet.
Diese Seite wurde automatisch übersetzt. Maßgeblich ist die englische Version.
#Einrichtung
1. Kunden und Proxy
Diese Seiten nutzen den App Router mit Cookie-Sitzungen (siehe Next.js SSR). Legen Sie NEXT_PUBLIC_AURABASE_URL und NEXT_PUBLIC_AURABASE_ANON_KEY fest und fügen Sie dann den Server-Client und den Proxy hinzu. Benennen Sie in Next.js 15 die Datei middleware.ts und die Funktion middleware.
lib/aurabase/server.tstypescript
import { cookies } from 'next/headers'
import { createServerClient } from '@aurabase/aurabase-js/ssr'
export async function serverClient() {
const store = await cookies()
return createServerClient(process.env.NEXT_PUBLIC_AURABASE_URL!, process.env.NEXT_PUBLIC_AURABASE_ANON_KEY!, {
cookies: {
getAll: () => store.getAll(),
// Serverkomponenten können keine Cookies schreiben: Der Proxy unten aktualisiert stattdessen die Sitzung.
setAll(list) {
try {
list.forEach(({ name, value, options }) => store.set(name, value, options))
} catch {}
},
},
})
}
proxy.tstypescript
import { NextResponse, type NextRequest } from 'next/server'
import { createServerClient } from '@aurabase/aurabase-js/ssr'
const PROTECTED = ['/dashboard']
export async function proxy(request: NextRequest) {
const response = NextResponse.next({ request })
const aura = createServerClient(process.env.NEXT_PUBLIC_AURABASE_URL!, process.env.NEXT_PUBLIC_AURABASE_ANON_KEY!, {
cookies: {
getAll: () => request.cookies.getAll(),
setAll(list) {
list.forEach(({ name, value, options }) => {
request.cookies.set(name, value)
response.cookies.set(name, value, options)
})
},
},
})
// getUser() aktualisiert die Token UND fragt den Server, wer der Benutzer ist. Autorisieren Sie niemals mit getSession().
const { data: user } = await aura.auth.getUser()
if (!user && PROTECTED.some((p) => request.nextUrl.pathname.startsWith(p))) {
return NextResponse.redirect(new URL('/login', request.url))
}
return response
}
export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] }
#Melden Sie sich an
2. Melden Sie sich an und registrieren Sie sich
app/login/actions.tstypescript
'use server'
import { redirect } from 'next/navigation'
import { serverClient } from '@/lib/aurabase/server'
export async function login(formData: FormData) {
const aura = await serverClient()
const { data, error } = await aura.auth.signInWithPassword({
email: String(formData.get('email')),
password: String(formData.get('password')),
})
if (error) redirect(`/login?error=${encodeURIComponent(error.code ?? 'ERROR')}`)
// Ein Benutzer mit aktiviertem MFA erhält eine Herausforderung anstelle einer Sitzung: Fragen Sie hier nach dem Code.
if (data && 'mfa_required' in data) redirect(`/login?error=MFA_REQUIRED`)
redirect('/dashboard')
}
export async function sendMagicLink(formData: FormData) {
const aura = await serverClient()
await aura.auth.signInWithMagicLink({ email: String(formData.get('email')) })
redirect('/login?sent=1')
}
app/login/page.tsxtsx
import Link from 'next/link'
import { login, sendMagicLink } from './actions'
export default async function LoginPage({ searchParams }: { searchParams: Promise<{ error?: string; sent?: string }> }) {
const { error, sent } = await searchParams
return (
<main>
<h1>Sign in</h1>
{error && <p role="alert">Sign-in failed: {error}</p>}
{sent && <p>If this address has an account, a sign-in link was sent.</p>}
<form action={login}>
<input name="email" type="email" placeholder="Email" required />
<input name="password" type="password" placeholder="Password" required />
<button type="submit">Sign in</button>
</form>
<form action={sendMagicLink}>
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Email me a sign-in link</button>
</form>
<p>
<Link href="/signup">Create an account</Link> · <Link href="/forgot-password">Forgot your password?</Link>
</p>
</main>
)
}
app/signup/actions.tstypescript
'use server'
import { redirect } from 'next/navigation'
import { serverClient } from '@/lib/aurabase/server'
export async function signup(formData: FormData) {
const aura = await serverClient()
const { data, error } = await aura.auth.signUp({
email: String(formData.get('email')),
password: String(formData.get('password')),
})
if (error) redirect(`/signup?error=${encodeURIComponent(error.message)}`)
// Wenn das Projekt eine E-Mail-Bestätigung erfordert, gibt es noch keine Sitzung: „data.session“ ist null.
redirect(data?.session ? '/dashboard' : '/signup?check=1')
}
app/signup/page.tsxtsx
import Link from 'next/link'
import { signup } from './actions'
export default async function SignupPage({ searchParams }: { searchParams: Promise<{ error?: string; check?: string }> }) {
const { error, check } = await searchParams
return (
<main>
<h1>Create an account</h1>
{error && <p role="alert">{error}</p>}
{check && <p>Check your inbox: confirm your email address to finish.</p>}
<form action={signup}>
<input name="email" type="email" placeholder="Email" required />
<input name="password" type="password" placeholder="Password" required minLength={8} />
<button type="submit">Sign up</button>
</form>
<p>
<Link href="/login">Already have an account?</Link>
</p>
</main>
)
}
#Zurücksetzen
3. Passwort zurücksetzen
Die E-Mail zum Zurücksetzen enthält einen Link zu <Site URL>/reset-password?token=…. Legen Sie die Site-URL in Studio › Authentifizierung › Konfiguration fest.
app/forgot-password/actions.tstypescript
'use server'
import { redirect } from 'next/navigation'
import { serverClient } from '@/lib/aurabase/server'
export async function requestReset(formData: FormData) {
const aura = await serverClient()
await aura.auth.resetPasswordForEmail(String(formData.get('email')))
// Gleiche Antwort, unabhängig davon, ob die Adresse ein Konto hat oder nicht (keine Kontoaufzählung).
redirect('/forgot-password?sent=1')
}
app/forgot-password/page.tsxtsx
import { requestReset } from './actions'
export default async function ForgotPasswordPage({ searchParams }: { searchParams: Promise<{ sent?: string }> }) {
const { sent } = await searchParams
return (
<main>
<h1>Reset your password</h1>
{sent && <p>If this address has an account, a reset link was sent.</p>}
<form action={requestReset}>
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Send the link</button>
</form>
</main>
)
}
app/reset-password/actions.tstypescript
'use server'
import { redirect } from 'next/navigation'
import { serverClient } from '@/lib/aurabase/server'
export async function resetPassword(formData: FormData) {
const token = String(formData.get('token'))
const aura = await serverClient()
const { error } = await aura.auth.resetPassword(token, String(formData.get('password')))
if (error) redirect(`/reset-password?token=${encodeURIComponent(token)}&error=${encodeURIComponent(error.message)}`)
// Durch das Zurücksetzen wird der Benutzer mit einer neuen Sitzung angemeldet. Ältere Sitzungen wurden widerrufen.
redirect('/dashboard')
}
app/reset-password/page.tsxtsx
import { resetPassword } from './actions'
export default async function ResetPasswordPage({ searchParams }: { searchParams: Promise<{ token?: string; error?: string }> }) {
const { token, error } = await searchParams
if (!token) return <main><p role="alert">This link is incomplete. Request a new one.</p></main>
return (
<main>
<h1>Choose a new password</h1>
{error && <p role="alert">{error}</p>}
<form action={resetPassword}>
<input type="hidden" name="token" value={token} />
<input name="password" type="password" placeholder="New password" required minLength={8} />
<button type="submit">Save</button>
</form>
</main>
)
}
#Links
4. E-Mail und magische Links
Bestätigungs- und Magic-Link-E-Mails verweisen auf <Site URL>/verify-email?token=… und <Site URL>/magic-link/verify?token=…. Ein Routenhandler verbraucht das Token und leitet es dann weiter.
app/verify-email/route.tstypescript
import { NextResponse, type NextRequest } from 'next/server'
import { serverClient } from '@/lib/aurabase/server'
export async function GET(request: NextRequest) {
const token = request.nextUrl.searchParams.get('token')
if (token) {
const aura = await serverClient()
const { error } = await aura.auth.verifyOtp({ type: 'signup', token })
if (!error) return NextResponse.redirect(new URL('/dashboard', request.url))
}
return NextResponse.redirect(new URL('/login?error=LINK_INVALID', request.url))
}
app/magic-link/verify/route.tstypescript
import { NextResponse, type NextRequest } from 'next/server'
import { serverClient } from '@/lib/aurabase/server'
export async function GET(request: NextRequest) {
const token = request.nextUrl.searchParams.get('token')
if (token) {
const aura = await serverClient()
const { error } = await aura.auth.verifyMagicLink(token)
if (!error) return NextResponse.redirect(new URL('/dashboard', request.url))
}
return NextResponse.redirect(new URL('/login?error=LINK_INVALID', request.url))
}
#Armaturenbrett
5. Geschützte Seite
app/dashboard/actions.tstypescript
'use server'
import { redirect } from 'next/navigation'
import { serverClient } from '@/lib/aurabase/server'
export async function logout() {
const aura = await serverClient()
await aura.auth.signOut()
redirect('/login')
}
app/dashboard/page.tsxtsx
import { redirect } from 'next/navigation'
import { serverClient } from '@/lib/aurabase/server'
import { logout } from './actions'
export default async function DashboardPage() {
const aura = await serverClient()
const { data: user } = await aura.auth.getUser()
if (!user) redirect('/login')
return (
<main>
<h1>Dashboard</h1>
<p>Signed in as {user.email}</p>
<form action={logout}>
<button type="submit">Sign out</button>
</form>
</main>
)
}
#Testen
6. Probieren Sie es vor Ort aus
Auf dem lokalen Stack werden E-Mails von Mailpit erfasst, anstatt sie zu versenden. Öffnen Sie den Posteingang mit kubectl -n aurabase-dev port-forward svc/aurabase-mailpit 8025:8025, dann http://localhost:8025. Fügen Sie den Ursprung Ihrer App (zum Beispiel http://localhost:3000/*) zu Studio › Authentifizierung › Konfiguration › URLs umleiten hinzu und legen Sie die Site-URL darauf fest.
Infos
proxy.ts leitet nur auf den in PROTECTED aufgeführten Routen weiter; Die Dashboard-Seite überprüft den Benutzer erneut mit getUser(), da ein Proxy keine eigenständige Autorisierungsgrenze darstellt.
Zuletzt aktualisiert · 25.10.2026