Как сделать авторизацию в API Next.js с использованием middleware?
Делаю авторизацию в приложении Next.js TypeScript. В куки хранится информация об аккаунте текущего пользователя, в том числе sessionId. В файле api.ts есть запросы на определенный адрес, которые нужно перехватывать в middleware и добавлять к заголовку запроса сессию в формате 'X-Auth-Token': account.sessionId. Если ответ на запрос возвращается с кодом 403, то нужно вернуть пользователя на страницу авторизации.
Файл middleware.ts
import AccountService from '@/lib/account-service';
import { getCookies } from 'next-client-cookies/server';
import { NextResponse } from 'next/server'
import type { NextRequest} from 'next/server'
export async function middleware(request: NextRequest) {
const cookies = getCookies();
const account = new AccountService(cookies).get();
if (!account && !request.nextUrl.pathname.startsWith('/login')) {
return NextResponse.rewrite(new URL('/login', request.url));
}
if (account && request.nextUrl.pathname.startsWith('/login')) {
return NextResponse.redirect(new URL('/', request.url))
}
/*
if (account ) {
const modifiedRequest = {
headers: new Headers(request.headers),
};
modifiedRequest.headers.set('X-Auth-Token', account.sessionid);
return NextResponse.next({ request: modifiedRequest });
}*/
}
export const config = {
matcher: [
'/((?!api|_next|favicon.ico).*)',
],
}
файл api.ts
import { faker } from "@faker-js/faker";
import { _arrayUnique } from "chart.js/helpers";
export default class Api {
public static SRV = new Api(process.env.API_ENDPOINT || '');
public static CLN = new Api(process.env.NEXT_PUBLIC_API_ENDPOINT || '');
public static SAE = new Api(process.env.A_API_ENDPOINT || '');
public static CAE = new Api(process.env.NEXT_PUBLIC_A_API_ENDPOINT || '');
private _endpoint: string;
private constructor(endpoint: string) {
this._endpoint = endpoint;
}
public async fetchWidgetData(uuid: string, key: string): Promise<{ [key: string]: any }>
{
// MOCKUP DATA
const options = {
responsive: true,
plugins: {
legend: {
position: 'top' as const,
},
title: {
display: true,
text: 'Chart.js Line Chart',
},
},
};
const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
const data = {
labels,
datasets: [
{
label: 'Dataset 1',
data: labels.map(() => faker.number.int({ min: -1000, max: 1000 })),
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
},
{
label: 'Dataset 2',
data: labels.map(() => faker.number.int({ min: -1000, max: 1000 })),
borderColor: 'rgb(53, 162, 235)',
backgroundColor: 'rgba(53, 162, 235, 0.5)',
},
],
};
return { options, data };
}
public async fetchConsumersData(uuid: string, key: string): Promise<{ [key: string]: any }> {
try{
const queryParams = {
convertTimestamp: 'true',
joinCampaignNames: 'true',
};
const url = `${Api.SAE._endpoint}/?${new URLSearchParams(queryParams).toString()}`;
console.log(url);
const data = await Api._run<IConsumersProps>(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
//'X-Auth-Token': sessionId,
},
}
);
return data ;
}
catch (error) {
console.error('Error during sign-in:', error);
throw error;
}
}
public async fetchInsight(uuid: string): Promise<IInsight> {
// MOCKUP DATA
return {
title: 'Global Insights',
uuid,
eventTypes: [
{ key: 'all-events', label: 'All events' },
{ key: 'purchase', label: 'Purchase' },
{ key: 'content', label: 'Content' },
{ key: 'misc', label: 'Misc' }
],
layout: [
{
name: '', widgets: [
{ key: 'event-type' }
]
},
{
name: 'Overview', widgets: [
{ key: 'campaign-details' },
{ key: 'performance-trend' },
]
},
{
name: 'Consumer Journey', widgets: [
{ key: 'user-profiles' },
]
},
]
};
}
public async signIn(email: string, password: string): Promise<IAccount> {
try {
const url = `${Api.CAE._endpoint}/signin/remote`;
const response = await Api._run<any>(
url,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
'login': email,
'password': password,
}),
}
);
if (response.result === 'AUTHENTICATED') {
const account: IAccount = {
id: response.advertiserData.guid,
email: email,
sessionid: response.advertiserData.sessionID,
role: response.advertiserData.role,
};
return account;
} else {
throw new Error('Invalid login or password');
}
} catch (error) {
console.error('Error during sign-in:', error);
throw error;
}
}
private static async _run<Type>(url: RequestInfo, params?: RequestInit) {
const request = new Request(url, params);
const resp = await fetch(request);
if(resp.ok){
const json = await resp.json();
return json as Type;
}
else {
if(resp.status === 403) {
throw new Error(`HTTP error! Status: ${resp.status}`);
}
else {
throw new Error(`HTTP error! Status: ${resp.status}`);
}
}
}
}
Я совсем недавно начала погружаться в Next.js. Подскажите, пожалуйста, как это можно реализовать.