Laravel 12 & Next.js Authentication with Sanctum

Laravel 12 & Next.js Authentication with Sanctum

Laravel 12 & Next.js Authentication with Sanctum – Guide

This tutorial will show you how to connect Laravel 12 (as the backend API) with Next.js 14+ (as the frontend) using Laravel Sanctum for authentication. We’ll build a basic full-stack authentication flow from register to logout.

Prerequisites

ToolRequirement
PHP8.1 or higher
ComposerInstalled
Node.js18.x or higher
npmInstalled
MySQLAny version
Git (optional)Version control

Project Structure

fullstack-app/ │ ├── backend/ → Laravel 12 API └── frontend/ → Next.js 14+ App

Step 1: Create the Laravel 12 Backend

1.1 Create Laravel Project

composer create-project laravel/laravel laravel-backend

1.2 Configure Database

Edit the .env file

DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=nextjs_auth DB_USERNAME=root DB_PASSWORD=

1.3 Run Migrations

php artisan migrate

Step 2: Install Sanctum

composer require laravel/sanctum

Step 3: Enable API Tokens

In app/Models/User.php, add

use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; }

Step 4: Configure CORS

Update config/cors.php

return [ 'paths' => ['api/*', 'sanctum/csrf-cookie'], 'allowed_methods' => ['*'], 'allowed_origins' => ['http://localhost:3000'], 'allowed_headers' => ['*'], 'supports_credentials' => true, ];

Step 5: (Optional) Generate API Boilerplate

php artisan install:api

Step 6: Add Auth Routes

Edit routes/api.php

use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use App\Models\User; Route::post('/register', function (Request $request) { $request->validate([ 'name' => 'required', 'email' => 'required|email|unique:users', 'password' => 'required|min:6', ]); User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), ]); return response()->json(['message' => 'Registered successfully']); }); Route::post('/login', function (Request $request) { if (!Auth::attempt($request->only('email', 'password'))) { return response()->json(['message' => 'Invalid credentials'], 401); } $user = Auth::user(); $token = $user->createToken('auth_token')->plainTextToken; return response()->json([ 'message' => 'Login successful', 'access_token' => $token, 'token_type' => 'Bearer', ]); }); Route::middleware('auth:sanctum')->get('/user', fn(Request $request) => $request->user()); Route::middleware('auth:sanctum')->post('/logout', function (Request $request) { $request->user()->currentAccessToken()->delete(); return response()->json(['message' => 'Logged out successfully']); });

Step 7: Create the Next.js Frontend

7.1 Create App

npx create-next-app@latest next-frontend

7.2 Install Axios

npm install axios

Step 8: Axios Configuration

Create lib/axios.js

import axios from 'axios'; const axiosInstance = axios.create({ baseURL: 'http://localhost:8000', withCredentials: true, }); export default axiosInstance;

Step 9: Register Page

Create pages/register.js

import { useState } from 'react'; import Link from 'next/link'; import axios from '../lib/axios'; export default function Register() { const [form, setForm] = useState({ name: '', email: '', password: '' }); const handleRegister = async (e) => { e.preventDefault(); try { await axios.post('/api/register', form); alert('Registered successfully'); window.location.href = '/login'; // redirect after registration } catch (error) { alert(error.response?.data?.message || 'Registration failed'); } }; return ( <div> <h2>Register</h2> <form onSubmit={handleRegister}> <input placeholder="Name" onChange={e => setForm({...form, name: e.target.value})} /> <input placeholder="Email" onChange={e => setForm({...form, email: e.target.value})} /> <input placeholder="Password" type="password" onChange={e => setForm({...form, password: e.target.value})} /> <button type="submit">Register</button> </form> <p>Already have an account? <Link href="/login">Login here</Link></p> </div> ); }

Step 10: Login Page

Create pages/login.js

import { useState } from 'react'; import { useRouter } from 'next/router'; import Link from 'next/link'; import axios from '../lib/axios'; export default function Login() { const [form, setForm] = useState({ email: '', password: '' }); const router = useRouter(); const handleLogin = async (e) => { e.preventDefault(); try { const res = await axios.post('/api/login', form); const { access_token } = res.data; localStorage.setItem('token', access_token); alert('Logged in successfully'); router.push('/profile'); } catch (error) { alert('Login failed'); } }; return ( <div> <h2>Login</h2> <form onSubmit={handleLogin}> <input placeholder="Email" onChange={e => setForm({...form, email: e.target.value})} /> <input placeholder="Password" type="password" onChange={e => setForm({...form, password: e.target.value})} /> <button type="submit">Login</button> </form> <p>Don’t have an account? <Link href="/register">Register here</Link></p> </div> ); }

Step 11: Profile Page

Create pages/profile.js

import { useEffect, useState } from 'react'; import axios from 'axios'; import { useRouter } from 'next/router'; export default function Profile() { const [user, setUser] = useState(null); const router = useRouter(); useEffect(() => { const token = localStorage.getItem('token'); if (token) { axios.get('http://localhost:8000/api/user', { headers: { Authorization: `Bearer ${token}` } }) .then(res => setUser(res.data)) .catch(() => alert('Not logged in or token invalid')); } }, []); const handleLogout = async () => { const token = localStorage.getItem('token'); try { await axios.post('http://localhost:8000/api/logout', {}, { headers: { Authorization: `Bearer ${token}` }, withCredentials: true, }); localStorage.removeItem('token'); router.push('/login'); } catch { alert('Logout failed'); } }; if (!user) return <p>Loading...</p>; return ( <div> <h2>Welcome, {user.name}</h2> <p>Email: {user.email}</p> <button onClick={handleLogout}>Logout</button> </div> ); }

Step 12: Run Both Servers

Laravel (Backend)

cd laravel-backend php artisan serve # http://localhost:8000

Next.js (Frontend)

cd next-frontend npm run dev # http://localhost:3000

Test It

  1. Visit /register and create an account.

  2. Go to /login and log in.

  3. View your profile at /profile.

  4. Click Logout to clear the token and return to the login screen.

What's Next?

  • Protect frontend routes using localStorage.getItem('token').

  • Add middleware in Next.js for guarding pages.

  • Display a dynamic navbar (login/register/logout) based on authentication state.

Souy Soeng

Souy Soeng

Hi there 👋, I’m Soeng Souy (StarCode Kh)
-------------------------------------------
🌱 I’m currently creating a sample Laravel and React Vue Livewire
👯 I’m looking to collaborate on open-source PHP & JavaScript projects
💬 Ask me about Laravel, MySQL, or Flutter
⚡ Fun fact: I love turning ☕️ into code!

Post a Comment

CAN FEEDBACK
close