Впровадження автентифікації на основі JWT за допомогою Next.js v14 та NextAuth v4

В цьому блозі ми розглянемо, як реалізувати аутентифікацію на основі JWT за допомогою Next.js v14 і NextAuth v4. Ми створимо власну сторінку входу та використаємо постачальника облікових даних для обробки процесу аутентифікації.

Налаштування Next.js та NextAuth

Для початку потрібно встановити Next.js і NextAuth за допомогою npm або yarn:

pnpm add next-auth

Створення власної сторінки входу

Далі потрібно створити власну сторінку входу.
Ми створимо новий файл app/login/page.tsx для цього:

pic

'use client'  
import React, { useState } from 'react';  
import Image from "next/image";  
import Link from "next/link";  
import { Button } from "@/components/ui/button";  
import { Input } from "@/components/ui/input";  
import { Label } from "@/components/ui/label";  
import { Logo } from "@/components/svg/logo";  
import { Icons } from "@/components/ui/icons";  
import { signIn, useSession } from "next-auth/react";  
import { useRouter } from 'next/navigation';  

const Login: React.FC = () => {  
 const [username, setUsername] = useState("");  
 const [password, setPassword] = useState("");  
 const [errorMessage, setErrorMessage] = useState(null);  
 const [pending, setPending] = useState(false);  
 const { status } = useSession();  
 const router = useRouter();  
 const formSubmitted = async (event) => {  
 event.preventDefault();  
 setPending(true);  

 try {  
 const res = await signIn('credentials', {  
 redirect: false,  
 username,  
 password,  
 });  
 if (res?.error ) {  
 console.log('res error :::: ',res)  
 setErrorMessage(res.error);  
 setPending(false);  
 } else {  
 setPending(false);  
 // Обробка успішного входу (наприклад, редирект чи зберігання даних користувача)  
 router.push("/dashboard");  

 }  
 } catch (error) {  
 console.error("Login error:", error);  
 setErrorMessage("Сталася помилка при вході");  
 setPending(false);  
 }  
 };  


 return (  
 \
    \
    \
    \
    \
Inatale Admin Login\    \

 Введіть ваш офіційний логін для доступу до акаунту  
 \  
 \  
 \  
    \ setPassword(e.target.value)}    /\>    \    \    \
    {errorMessage && (    \<\>    \    \
{errorMessage}\  
 \  
 )}  
 \  
 \  
 \
    Forgot    \    Go to Website    \    \    \    \    );   }      function LoginButton({ pending }) {    return (    \    Log in \    \    );   }      export default Login; ```  

## Налаштування NextAuth

У цьому розділі ми налаштуємо NextAuth для обробки аутентифікації в нашому додатку.
Це включає створення конфігураційного файлу за адресою `app/api/auth/[...nextauth]/route.ts`. Цей файл визначатиме, як NextAuth має обробляти провайдери аутентифікації та сесії.

1. **Імпортування необхідних модулів**: Ми починаємо з імпорту `NextAuth` та необхідних провайдерів, таких як `CredentialsProvider`. `NextAuth` є основною бібліотекою для обробки аутентифікації, а провайдери, такі як `CredentialsProvider`, дозволяють нам керувати кастомною логікою аутентифікації, використовуючи облікові дані (логін та пароль).
2. **Функція для перетворення об'єкта в дані форми**: Додається утилітна функція `toFormData`, яка перетворює об'єкт JavaScript в URL-кодовані дані форми. Це корисно для відправки облікових даних для входу в форматі, який може бути оброблений сервером.
3. **Визначення параметрів аутентифікації**:

- **Провайдери**: Масив `providers` містить `CredentialsProvider`, який налаштовується на прийом логіну та паролю.
Функція `authorize` в межах цього провайдера є ключовою, оскільки вона обробляє сам процес входу. Вона відправляє облікові дані на наш бекенд API та обробляє відповідь.
- **Сторінки**: Властивість `pages` вказує на кастомні сторінки для різних станів аутентифікації. Тут вона вказує `/login` як сторінку для входу.
- **Стратегія сесії**: Властивість `session` налаштовується на використання JWT (JSON Web Tokens) для керування сесіями. Це безстанова методика, яка ідеально підходить для масштабованих додатків.
- **Зворотні виклики (Callbacks)**: Розділ `callbacks` визначає функції для маніпулювання об'єктами JWT та сесії. Наприклад, зворотний виклик `jwt` додає дані користувача до токена, що забезпечує наявність усієї необхідної інформації користувача в токені для подальших запитів.

Налаштувавши `route.ts` таким чином, ми створюємо надійну систему аутентифікації, яка може обробляти кастомні облікові дані та керувати сесіями користувачів безпечно, використовуючи JWT.
Ця конфігурація дозволяє нашому додатку мати гнучкий та безпечний механізм аутентифікації.

![pic](https://drive.javascript.org.ua/766914239a1_WBKLEbeEcVMTO_p1EkSB2w_png)

_Мій API для бекенду, який обробляє вхід (Postman)_

import NextAuth from "next-auth";
import GitHubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
import { NextAuthOptions } from "next-auth";

// Функція для перетворення об'єкта в URL-кодовані дані форми
function toFormData(obj) {
const formBody = [];
for (const property in obj) {
const encodedKey = encodeURIComponent(property);
const encodedValue = encodeURIComponent(obj[property]);
formBody.push(${encodedKey}=${encodedValue});
}
return formBody.join("&");
}

export const authOptions : NextAuthOptions = {

providers: [
// GitHubProvider({
// clientId: process.env.GITHUB_ID ?? "",
// clientSecret: process.env.GITHUB_SECRET ?? "",
// }),
CredentialsProvider({
name: 'Credentials',
credentials: {
username: { label: "Username", type: "text", placeholder: "jsmith", value: "administrator" },
password: { label: "Password", type: "password", value: "admin" },
},

async authorize(credentials, req) {
// Включіть приховані значення тут
const data = {
username: credentials.username,
password: credentials.password,
};
const formData = toFormData(data);
try {
const res = await fetch("http://development.localhost:8000/api/method/lms_api.api.login", {
method: 'POST',
body: formData,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
});

const resData = await res.json();
if (res.ok && resData && resData.data) {
return resData.data;
} else {
console.error('Authorization failed:', resData);
return null;
}
} catch (error) {
console.error('Authorization error:', error);
return null;
}
}
})
],
pages: {
signIn: '/login'
},
session: { strategy: "jwt" },
callbacks: {
async jwt({token, user}){
return {...token, ...user}
},
async session ({ session, token, user }) {
session.user = token as any ;
return session;
}
}

};

export const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
```

Створення Middleware

Далі, нам потрібно створити файл middleware middleware.ts у кореневій папці для обробки аутентифікації:

export { default } from "next-auth/middleware";  

export const config = {  
 matcher: ["/dashboard"],  
};
  • Об'єкт config використовується для вказівки маршрутів, до яких має застосовуватись middleware.
  • Властивість matcher містить масив шляхів.
    У цьому випадку він налаштований на ["/dashboard"], що означає, що middleware буде виконуватися лише для запитів до маршруту /dashboard.
  • Це гарантує, що кожен запит до /dashboard пройде через перевірки аутентифікації, реалізовані за допомогою NextAuth middleware, захищаючи цей маршрут і забезпечуючи доступ до нього тільки для аутентифікованих користувачів.

Використовуючи цю конфігурацію middleware, ви централізуєте та спрощуєте процес аутентифікації, що дозволяє легко захищати певні маршрути у вашому додатку Next.js.

Створення інтерфейсу сесії

Також потрібно створити файл next-auth.d.ts, щоб визначити інтерфейс сесії:

import "next-auth";  

declare module "next-auth"_on: number;  
 exp:number;  
 iat:number;  
 jti:string;  
 }  

 interface Session extends DefaultSession {  
 user: User;  
 expires\_in: string;  
 error: string;  
 }  
}

Налаштування файлу tsconfig.json

{  
 "compilerOptions": {  
 // ...
*\*/*.tsx", ".next/types/\*\*/\*.ts","types/\*\*/\*.ts", "\*\*/\*.ts", "\*\*/\*.tsx"],  
 // ...  
}

Створення провайдера сесії в Next.js з NextAuth

У цьому розділі ми створимо компонент Provider для управління сесіями користувачів за допомогою SessionProvider від NextAuth.
Цей компонент обгортатиме всю програму або її окремі частини, забезпечуючи доступність даних сесії в будь-якому місці програми.

Мета:

  • Управління сесіями користувачів: Обгортаючи компоненти за допомогою SessionProvider, ви забезпечуєте доступність даних сесії та стану автентифікації для всіх вкладених компонентів.
  • Рендеринг на стороні клієнта: Це налаштування необхідне для компонентів, які залежать від хук і рендерингу на стороні клієнта.
"use client";  
import { SessionProvider } from "next-auth/react";  
import React, { ReactNode } from "react";  

interface Props {  
 children: ReactNode;  
}  

function Provider({ children }: Props) {  
 return {children};  
}  

export default Provider;

Щоб використати цей компонент Provider, додайте його на верхньому рівні вашого додатка, наприклад, у app/Provider.tsx.
Це забезпечує доступ усіх частин вашого додатку до контексту сесії, який керується NextAuth.

У кореневому макеті ми забезпечуємо ефективне керування сесіями користувачів, обгортаючи додаток компонентом Provider від NextAuth.
Компонент Provider забезпечує доступ до даних сесії та стану автентифікації для всіх компонентів додатка.

import type { Metadata } from "next";  
import { Inter } from "next/font/google";  
import "./globals.css";  
import { ThemeProvider } from "@/components/theme/theme-provider";  
import { ThemeWrapper } from "@/components/theme/theme-wrapper";  
import Provider from "./Provider";  

const inter = Inter({ subsets: ["latin"] });  

export const metadata: Metadata = {  
 title: "Inatale Admin",  
 description: "Generated by create next app",  
};  

export default function RootLayout({  
 children,  
}: Readonly\<{  
 children: React.ReactNode;  
}\>) {  
 return (  
 \  
 \  
 \  
 \  
 \  
 {children}  
 \  
 \  

 \  
 \  

 \  
 );  
}

Перегляд статусу користувача

Щоб переглянути статус користувача після входу, ми можемо використати хук useSession з next-auth/react:

import { useSession, signIn, signOut } from "next-auth/react";  

export default function LoginStatusButton() {  
 const { data: session } = useSession()  
 console.log(session)  
 if (session) {  
 return (  
 \<\>  
 Signed in as {session?.user?.name} \  
 \ signOut()}\>Sign out\  
 \  
 )  
 }  
 return (  
 \<\>  
 Not signed in \  
 \ signIn()}\>Sign in\  
 \  
 )  
}

Висновок

У цьому блозі ми реалізували автентифікацію на основі JWT, використовуючи Next.js v14 і NextAuth v4.
Ми створили власну сторінку входу та використали постачальника облікових даних (credential provider) для обробки процесу автентифікації. Також ми налаштували проміжне програмне забезпечення (middleware) та інтерфейс сесії для керування автентифікацією.

Перекладено з: Implementing JWT-Based Authentication with Next.js v14 and NextAuth v4

Leave a Reply

Your email address will not be published. Required fields are marked *