Uitleg — Overzicht

Je hebt twee aparte flows nodig:

Flow Wat er gebeurt
Registreren Eigen API route die de gebruiker opslaat in MongoDB met een gehashed wachtwoord
Inloggen NextAuth signIn() die de authorize functie aanroept en een sessie aanmaakt

💡 Mappenstructuur voor auth

app/
├── api/
│   ├── auth/
│   │   └── [...nextauth]/
│   │       └── route.js      ← al aangemaakt
│   └── register/
│       └── route.js          ← nieuw
├── register/
│   └── page.js               ← nieuw
└── login/
    └── page.js               ← nieuw

Stap 1 — Maak de registratie API route aan

Maak een nieuw bestand app/api/register/route.js aan:

app/api/register/route.js
// app/api/register/route.js
import { NextResponse } from 'next/server';
import bcrypt from 'bcryptjs';
import connectDB from '../../../lib/mongodb';
import User from '../../../models/User';

export async function POST(request) {
  const { name, email, password } = await request.json();

  // Validatie
  if (!name || !email || !password) {
    return NextResponse.json(
      { error: 'Vul alle velden in' },
      { status: 400 }
    );
  }

  if (password.length < 6) {
    return NextResponse.json(
      { error: 'Wachtwoord moet minimaal 6 tekens zijn' },
      { status: 400 }
    );
  }

  await connectDB();

  // Check of email al bestaat
  const exists = await User.findOne({ email });
  if (exists) {
    return NextResponse.json(
      { error: 'Dit e-mailadres is al in gebruik' },
      { status: 400 }
    );
  }

  // Wachtwoord hashen voor opslag
  const hashedPassword = await bcrypt.hash(password, 10);

  await User.create({ name, email, password: hashedPassword });

  return NextResponse.json(
    { message: 'Account aangemaakt' },
    { status: 201 }
  );
}
Wat doet bcrypt.hash(password, 10)?
  • Zet het wachtwoord om in een hash: "geheim123" → "$2b$10$abc..."
  • De 10 is het aantal "salt rounds" — hoe hoger, hoe veiliger maar langzamer
  • Elke hash is uniek, ook als twee gebruikers hetzelfde wachtwoord hebben

Stap 2 — Maak het registratieformulier aan

Maak een nieuw bestand app/components/RegisterForm.js aan:

// app/components/RegisterForm.js
'use client';

import { useState } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

export default function RegisterForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState(null);
  const router = useRouter();

  async function handleSubmit(e) {
    e.preventDefault();
    setError(null);

    const res = await fetch('/api/register', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name, email, password }),
    });

    const data = await res.json();

    if (!res.ok) {
      setError(data.error);
      return;
    }

    // Account aangemaakt — stuur door naar inlogpagina
    router.push('/login');
  }

  return (
    <form onSubmit={handleSubmit} style={{ maxWidth: '400px', margin: '0 auto' }}>
      <h1>Account aanmaken</h1>

      <div>
        <label>Naam</label>
        <input
          type="text"
          placeholder="Jouw naam"
          value={name}
          onChange={(e) => setName(e.target.value)}
          required
        />
      </div>

      <div>
        <label>E-mailadres</label>
        <input
          type="email"
          placeholder="naam@voorbeeld.nl"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
      </div>

      <div>
        <label>Wachtwoord</label>
        <input
          type="password"
          placeholder="Minimaal 6 tekens"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
      </div>

      {error && <p style={{ color: 'red' }}>{error}</p>}

      <button type="submit">Account aanmaken</button>

      <p>Al een account? <Link href="/login">Inloggen</Link></p>
    </form>
  );
}

Stap 3 — Maak de registratiepagina aan

Maak een nieuw bestand app/register/page.js aan:

// app/register/page.js
import RegisterForm from '../components/RegisterForm';

export default function RegisterPage() {
  return <RegisterForm />;
}

✅ Test het

Ga naar http://localhost:3000/register en maak een account aan. Controleer in MongoDB Atlas of de gebruiker is opgeslagen — het wachtwoord moet een hash zijn, niet de platte tekst.

Stap 4 — Maak het loginformulier aan

Maak een nieuw bestand app/components/LoginForm.js aan. Dit gebruikt signIn van NextAuth — geen eigen API route nodig:

// app/components/LoginForm.js
'use client';

import { useState } from 'react';
import { signIn } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

export default function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState(null);
  const router = useRouter();

  async function handleSubmit(e) {
    e.preventDefault();
    setError(null);

    const result = await signIn('credentials', {
      email,
      password,
      redirect: false, // Wij handelen de redirect zelf af
    });

    if (result?.error) {
      setError('Ongeldig e-mailadres of wachtwoord');
      return;
    }

    // Ingelogd! Stuur door naar workouts
    router.push('/workouts');
    router.refresh();
  }

  return (
    <form onSubmit={handleSubmit} style={{ maxWidth: '400px', margin: '0 auto' }}>
      <h1>Inloggen</h1>

      <div>
        <label>E-mailadres</label>
        <input
          type="email"
          placeholder="naam@voorbeeld.nl"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
      </div>

      <div>
        <label>Wachtwoord</label>
        <input
          type="password"
          placeholder="Jouw wachtwoord"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
      </div>

      {error && <p style={{ color: 'red' }}>{error}</p>}

      <button type="submit">Inloggen</button>

      <p>Nog geen account? <Link href="/register">Registreren</Link></p>
    </form>
  );
}
redirect: false — waarom?
  • Met redirect: true laat NextAuth de redirect afhandelen — je hebt minder controle
  • Met redirect: false krijg je een result object terug en handel je de redirect zelf af
  • Dit is handiger omdat je dan een foutmelding kunt tonen zonder pagina-herlaad

Stap 5 — Maak de loginpagina aan

Maak een nieuw bestand app/login/page.js aan:

// app/login/page.js
import LoginForm from '../components/LoginForm';

export default function LoginPage() {
  return <LoginForm />;
}

✅ Test het

Ga naar http://localhost:3000/login en log in met het account dat je zojuist aanmaakte. Na succesvol inloggen word je doorgestuurd naar /workouts.

Stap 6 — Maak de uitlogknop aan en voeg toe aan layout

Maak een nieuw bestand app/components/LogoutButton.js aan:

// app/components/LogoutButton.js
'use client';

import { signOut } from 'next-auth/react';

export default function LogoutButton() {
  return (
    <button onClick={() => signOut({ callbackUrl: '/login' })}>
      Uitloggen
    </button>
  );
}

Vervang daarna de inhoud van app/layout.js om de knop toe te voegen:

// app/layout.js
import './globals.css';
import Link from 'next/link';
import AuthProvider from './components/AuthProvider';
import LogoutButton from './components/LogoutButton';

export default function RootLayout({ children }) {
  return (
    <html lang="nl">
      <body>
        <AuthProvider>
          <nav>
            <Link href="/">Home</Link>
            <Link href="/workouts">Workouts</Link>
            <LogoutButton />
          </nav>
          <main>{children}</main>
        </AuthProvider>
      </body>
    </html>
  );
}

💡 callbackUrl

callbackUrl: '/login' stuurt de gebruiker na uitloggen door naar de loginpagina. Zonder dit gaat NextAuth naar de homepage.

Checklist

✅ Check of je hebt:

  • app/api/register/route.js aangemaakt
  • app/components/RegisterForm.js aangemaakt
  • app/register/page.js aangemaakt
  • app/components/LoginForm.js aangemaakt
  • app/login/page.js aangemaakt
  • app/components/LogoutButton.js aangemaakt
  • Registreren werkt — gebruiker zichtbaar in MongoDB
  • Inloggen werkt — doorgestuurd naar /workouts
  • Uitloggen werkt — doorgestuurd naar /login

Volgende Stap

Inloggen werkt! Nu beveiligen we de pagina's zodat alleen ingelogde gebruikers ze kunnen zien.

Protected Routes →

Beveilig pagina's voor niet-ingelogde gebruikers