This episode covers security and authentication in Nuxt: using @sidebase/nuxt-auth for login and sessions, secure cookie management, protecting routes with middleware, and Role-Based Access Control and secure data fetching.

Once an application has data and forms, security is no longer optional. Episode 12 covers authentication and security in Nuxt: how users sign in, how sessions are stored securely, how routes are protected, and how access rights differ between roles.
We'll cover two approaches: the complete @sidebase/nuxt-auth, and building a lighter custom authentication. Both are valid — the choice depends on your project's needs. What's non-negotiable: secure cookies, protective middleware, and authorization checked on the server.
@sidebase/nuxt-auth wraps Auth.js so login, sessions, and callbacks are available quickly:
npm install @sidebase/nuxt-authexport default defineNuxtConfig({
modules: ["@sidebase/nuxt-auth"],
auth: {
provider: {
type: "local",
endpoints: {
signIn: { path: "/api/auth/login", method: "post" },
signOut: { path: "/api/auth/logout", method: "post" },
},
},
},
})The configuration above points login to an API endpoint you provide. This module provides useSession in components as well as middleware endpoints to protect server routes.
import { signIn, signOut } from "#auth"
async function login() {
await signIn({ username: form.username, password: form.password })
}
async function keluar() {
await signOut()
}signIn and signOut from #auth handle the entire session flow. After a successful login, useSession() in other components immediately reflects the user status.
If the project needs full control — for example, integration with an internal SSO system — custom authentication makes more sense. The principles stay the same: verify credentials on the server, create a session token, store it in a secure cookie.
export default defineEventHandler(async (event) => {
const { username, password } = await readBody(event)
if (!verifikasiKredensial(username, password)) {
throw createError({ statusCode: 401, message: "Kredensial salah" })
}
const token = buatTokenSesi(username)
setCookie(event, "session", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 60 * 60 * 24,
})
return { ok: true }
})setCookie(event, "session", token, {...}) stores the token in a cookie. The httpOnly attribute prevents access via JavaScript, secure ensures it's only sent over HTTPS, and sameSite prevents CSRF attacks.
Three mandatory attributes for session cookies:
const token = getCookie(event, "session")
if (!token) {
throw createError({ statusCode: 401, message: "Belum login" })
}
async function logout(event) {
deleteCookie(event, "session")
}getCookie(event, "session") reads the cookie on the server, and deleteCookie removes it on logout. Validating the token on every sensitive request is a must.
Don't rely on components alone for security. Every sensitive server route must check the session:
export default defineEventHandler((event) => {
if (!getCookie(event, "session")) {
throw createError({ statusCode: 401, message: "Akses ditolak" })
}
return { data: "hanya untuk yang login" }
})Combine the middleware from episode 4 with the session cookie:
export default defineRouteMiddleware(() => {
if (!useCookie("session").value) {
return navigateTo("/login")
}
})useCookie("session") reads the token on the client. Pages that register this middleware automatically redirect logged-out users to the login page.
Role-Based Access Control differentiates access rights based on the user's role:
export default defineEventHandler((event) => {
const pengguna = getPenggunaDariSesi(event)
if (pengguna.peran !== "admin") {
throw createError({
statusCode: 403,
message: "Butuh hak admin",
})
}
return listSemuaPesanan()
})Role checks must always happen on the server, not just by hiding buttons in the UI. Status 403 is returned when a user is logged in but lacks permission.
Secure data fetching means: a server route only returns data the user is actually allowed to see. Filter by identity from the session, don't return the entire database.
Episode 12 layers your application with security: authentication with @sidebase/nuxt-auth or custom auth, sessions stored in httpOnly and secure cookies, page routes protected by middleware, and Role-Based Access Control always rechecked on the server.
Key takeaways:
@sidebase/nuxt-auth provides quick login, session, and logout.In the next episode, episode 13, we will discuss API integration and data protection — consuming external APIs from Nuxt, protecting requests with auth headers, handling secrets safely, and rate limiting and error handling for resilient integrations.