Gotowe strony logowania do Next.js
Osiem małych plików zapewnia aplikacji Next.js pełny proces logowania, w tym strony, do których prowadzą łącza w wiadomościach e-mail Aurabase.
8 min przeczytane·Poziom mediator·Zmieniono 25 paź 2026
Ten tekst w języku angielskim został wygenerowany automatycznie na podstawie francuskiego oryginału i nie był jeszcze recenzowany.
Źródło angielskie uległo zmianie od czasu tego tłumaczenia. Niektóre informacje mogą być nieaktualne.
Ta strona została przetłumaczona automatycznie. Wersja angielska jest miarodajna.
#Organizować coś
1. Klienci i pełnomocnik
Strony te korzystają z App Router z sesjami plików cookie (patrz Next.js SSR). Ustaw NEXT_PUBLIC_AURABASE_URL i NEXT_PUBLIC_AURABASE_ANON_KEY, a następnie dodaj klienta serwera i serwer proxy. W Next.js 15 nazwij plik middleware.ts i funkcję 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(),
// Składniki serwera nie mogą zapisywać plików cookie: zamiast tego serwer proxy poniżej odświeża sesję.
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() odświeża tokeny ORAZ pyta serwer, kim jest użytkownik. Nigdy nie autoryzuj za pomocą 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).*)'] }
#Zalogować się
2. Zaloguj się i zarejestruj
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')}`)
// Użytkownik z włączoną opcją MFA zamiast sesji otrzymuje wyzwanie: poproś o kod tutaj.
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)}`)
// Gdy projekt wymaga potwierdzenia e-mailem, nie ma jeszcze sesji: `data.session` ma wartość 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>
)
}
Zresetowany e-mail zawiera łącze do <Site URL>/reset-password?token=…. Ustaw adres URL witryny w Studio > Uwierzytelnianie > Konfiguracja.
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')))
// Ta sama odpowiedź niezależnie od tego, czy pod adresem znajduje się konto (brak wyliczenia kont).
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)}`)
// Reset powoduje zalogowanie użytkownika w nowej sesji; starsze sesje zostały odwołane.
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>
)
}
#Spinki do mankietów
4. E-mail i magiczne linki
E-maile z potwierdzeniem i magicznym łączem zawierają linki do <Site URL>/verify-email?token=… i <Site URL>/magic-link/verify?token=…. Procedura obsługi trasy zużywa token, a następnie przekierowuje.
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))
}
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>
)
}
Na stosie lokalnym wiadomości e-mail są przechwytywane przez Mailpit zamiast wysyłane. Otwórz jego skrzynkę odbiorczą za pomocą kubectl -n aurabase-dev port-forward svc/aurabase-mailpit 8025:8025, a następnie http://localhost:8025. Dodaj źródło swojej aplikacji (na przykład http://localhost:3000/*) do Studio › Uwierzytelnianie › Konfiguracja › Adresy URL przekierowań i ustaw dla niego adres URL witryny.
Informacje
proxy.ts przekierowuje tylko na trasach wymienionych w PROTECTED; strona panelu kontrolnego ponownie sprawdza użytkownika za pomocą getUser(), ponieważ serwer proxy nie jest sam w sobie granicą autoryzacji.
Ostatnia aktualizacja · 25 paź 2026