Få mer kontroll över din Next.js-apps autentiseringslogik genom anpassad JWT-baserad autentiseringsimplementering.

Token-autentisering är en populär strategi som används för att skydda webb- och mobilapplikationer från obehörig åtkomst. I Next.js kan du använda autentiseringsfunktionerna som tillhandahålls av Next-auth.

Alternativt kan du välja att utveckla ett anpassat tokenbaserat autentiseringssystem med JSON Web Tokens (JWTs). Genom att göra det säkerställer du att du har mer kontroll över autentiseringslogiken; i huvudsak anpassa systemet för att exakt matcha kraven i ditt projekt.

Konfigurera ett Next.js-projekt

För att komma igång, installera Next.js genom att köra kommandot nedan på din terminal.

npx create-next-app@latest next-auth-jwt --experimental-app

Denna guide kommer att använda Next.js 13 som inkluderar appkatalogen.

Installera sedan dessa beroenden i ditt projekt med hjälp av npm, Node Package Manager.

npm install jose universal-cookie

Jose är en JavaScript-modul som tillhandahåller en uppsättning verktyg för att arbeta med JSON Web Tokens medan

instagram viewer
universal-cookie beroende ger ett enkelt sätt att arbeta med webbläsarcookies i både klient- och servermiljöer.

Du kan hitta detta projekts kod i denna GitHub-förråd.

Skapa användargränssnittet för inloggningsformuläret

Öppna src/app katalog, skapa en ny mapp och namnge den logga in. Lägg till en ny i den här mappen page.js fil och inkludera koden nedan.

"use client";
import { useRouter } from"next/navigation";

exportdefaultfunctionLoginPage() {
return (


Koden ovan skapar en funktionell komponent för inloggningssidan som återger ett enkelt inloggningsformulär i webbläsaren så att användare kan ange ett användarnamn och ett lösenord.

De använda klienten satsen i koden säkerställer att en gräns deklareras mellan endast server- och klientkod i app katalog.

I det här fallet används den för att deklarera att koden på inloggningssidan, särskilt handleSubmitfunktionen körs endast på klienten; Annars kommer Next.js att ge ett fel.

Låt oss nu definiera koden för handleSubmit fungera. Inuti den funktionella komponenten, lägg till följande kod.

const router = useRouter();

const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const username = formData.get("username");
const password = formData.get("password");
const res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify({ username, password }),
});
const { success } = await res.json();
if (success) {
router.push("/protected");
router.refresh();
} else {
alert("Login failed");
}
 };

För att hantera logiken för inloggningsautentisering, hämtar den här funktionen användaruppgifterna från inloggningsformuläret. Den skickar sedan en POST-begäran till en API-slutpunkt som förmedlar användarinformationen för verifiering.

Om inloggningsuppgifterna är giltiga, vilket indikerar att inloggningsprocessen lyckades – API: et returnerar en framgångsstatus i svaret. Hanterarfunktionen kommer sedan att använda Next.js router för att navigera användaren till en angiven URL, i det här fallet skyddad rutt.

Definiera Login API Endpoint

Inuti src/app katalog, skapa en ny mapp och namnge den api. Lägg till en ny i den här mappen login/route.js fil och inkludera koden nedan.

import { SignJWT } from"jose";
import { NextResponse } from"next/server";
import { getJwtSecretKey } from"@/libs/auth";

exportasyncfunctionPOST(request) {
const body = await request.json();
if (body.username "admin" && body.password "admin") {
const token = awaitnew SignJWT({
username: body.username,
})
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("30s")
.sign(getJwtSecretKey());
const response = NextResponse.json(
{ success: true },
{ status: 200, headers: { "content-type": "application/json" } }
);
response.cookies.set({
name: "token",
value: token,
path: "/",
});
return response;
}
return NextResponse.json({ success: false });
}

Den primära uppgiften för detta API är att verifiera inloggningsuppgifterna som skickas i POST-förfrågningarna med hjälp av skendata.

Efter framgångsrik verifiering genererar den en krypterad JWT-token associerad med de autentiserade användaruppgifterna. Slutligen skickar den ett framgångsrikt svar till klienten, inklusive token i svarscookies; annars returneras ett felstatussvar.

Implementera tokenverifieringslogik

Det första steget i tokenautentisering är att generera token efter en lyckad inloggningsprocess. Nästa steg är att implementera logiken för tokenverifiering.

I huvudsak kommer du att använda jwtVerifiera funktion som tillhandahålls av Jose modul för att verifiera JWT-tokens som skickas med efterföljande HTTP-förfrågningar.

I den src katalog, skapa en ny libs/auth.js fil och inkludera koden nedan.

import { jwtVerify } from"jose";

exportfunctiongetJwtSecretKey() {
const secret = process.env.NEXT_PUBLIC_JWT_SECRET_KEY;
if (!secret) {
thrownewError("JWT Secret key is not matched");
}
returnnew TextEncoder().encode(secret);
}

exportasyncfunctionverifyJwtToken(token) {
try {
const { payload } = await jwtVerify(token, getJwtSecretKey());
return payload;
} catch (error) {
returnnull;
}
}

Den hemliga nyckeln används för att signera och verifiera tokens. Genom att jämföra den avkodade tokensignaturen med den förväntade signaturen kan servern effektivt verifiera att den tillhandahållna tokenen är giltig och slutligen auktorisera användarnas förfrågningar.

Skapa .env fil i rotkatalogen och lägg till en unik hemlig nyckel enligt följande:

NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key

Skapa en skyddad rutt

Nu måste du skapa en rutt som endast autentiserade användare kan få tillgång till. För att göra det, skapa en ny protected/page.js fil i src/app katalog. Lägg till följande kod i den här filen.

exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}

Skapa en krok för att hantera autentiseringstillståndet

Skapa en ny mapp i src katalog och namnge den krokar. Lägg till en ny i den här mappen useAuth/index.js fil och inkludera koden nedan.

"use client" ;
import React from"react";
import Cookies from"universal-cookie";
import { verifyJwtToken } from"@/libs/auth";

exportfunctionuseAuth() {
const [auth, setAuth] = React.useState(null);

const getVerifiedtoken = async () => {
const cookies = new Cookies();
const token = cookies.get("token")?? null;
const verifiedToken = await verifyJwtToken(token);
setAuth(verifiedToken);
};
React.useEffect(() => {
getVerifiedtoken();
}, []);
return auth;
}

Denna krok hanterar autentiseringstillståndet på klientsidan. Den hämtar och verifierar giltigheten av JWT-token som finns i cookies med hjälp av verifyJwtToken funktionen och ställer sedan in den autentiserade användarinformationen till auth stat.

Genom att göra det tillåter det andra komponenter att komma åt och använda den autentiserade användarens information. Detta är viktigt för scenarier som att göra UI-uppdateringar baserat på autentiseringsstatus, göra efterföljande API-förfrågningar eller rendera annat innehåll baserat på användarroller.

I det här fallet kommer du att använda kroken för att rendera olika innehåll på Hem rutt baserat på en användares autentiseringstillstånd.

Ett alternativt tillvägagångssätt du kan överväga är hantering tillståndshantering med Redux Toolkit eller anställa en statligt ledningsverktyg som Jotai. Detta tillvägagångssätt garanterar att komponenter kan få global åtkomst till autentiseringstillståndet eller något annat definierat tillstånd.

Gå vidare och öppna app/page.js fil, ta bort boilerplate Next.js-koden och lägg till följande kod.

"use client" ;

import { useAuth } from"@/hooks/useAuth";
import Link from"next/link";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>

Public Home Page</h1>

Koden ovan använder useAuth krok för att hantera autentiseringstillståndet. Genom att göra det, återger den villkorligt en offentlig hemsida med en länk till logga in sidrutt när användaren inte är autentiserad, och visar ett stycke för en autentiserad användare.

Lägg till ett mellanprogram för att upprätthålla auktoriserad åtkomst till skyddade rutter

I den src katalog, skapa en ny middleware.js fil och lägg till koden nedan.

import { NextResponse } from"next/server";
import { verifyJwtToken } from"@/libs/auth";

const AUTH_PAGES = ["/login"];

const isAuthPages = (url) => AUTH_PAGES.some((page) => page.startsWith(url));

exportasyncfunctionmiddleware(request) {

const { url, nextUrl, cookies } = request;
const { value: token } = cookies.get("token")?? { value: null };
const hasVerifiedToken = token && (await verifyJwtToken(token));
const isAuthPageRequested = isAuthPages(nextUrl.pathname);

if (isAuthPageRequested) {
if (!hasVerifiedToken) {
const response = NextResponse.next();
response.cookies.delete("token");
return response;
}
const response = NextResponse.redirect(new URL(`/`, url));
return response;
}

if (!hasVerifiedToken) {
const searchParams = new URLSearchParams(nextUrl.searchParams);
searchParams.set("next", nextUrl.pathname);
const response = NextResponse.redirect(
new URL(`/login?${searchParams}`, url)
);
response.cookies.delete("token");
return response;
}

return NextResponse.next();

}
exportconst config = { matcher: ["/login", "/protected/:path*"] };

Denna middleware-kod fungerar som en vakt. Den kontrollerar att när användare vill komma åt skyddade sidor, är de autentiserade och auktoriserade att komma åt rutterna, förutom att omdirigera obehöriga användare till inloggningssidan.

Säkra Next.js-applikationer

Token-autentisering är en effektiv säkerhetsmekanism. Det är dock inte den enda tillgängliga strategin för att skydda dina applikationer från obehörig åtkomst.

För att stärka applikationer mot det dynamiska cybersäkerhetslandskapet är det viktigt att anta en omfattande säkerhet tillvägagångssätt som på ett holistiskt sätt tar itu med potentiella kryphål och sårbarheter i säkerheten för att garantera grundlig skydd.