Есть ли способ передать мою константу middleware.ts, которая проверяет, вошел ли пользователь в другой API, логику домена или page.tsx? (также открыт для новых предложений)

Я пытаюсь создать функцию аутентификации для своего веб-приложения, следуя этому онлайн-руководству.

Я создал middleware.ts для своего веб-приложения и увидел, что там есть const isLoggedIn, поэтому подумал, что могу использовать это логическое значение на всех остальных страницах для постоянной аутентификации, если пользователь вошел в систему.

поэтому я абстрагировал это вот так

export const checkIsLoggedIn = (req: any): boolean => {
    return !!req.auth;
};

поскольку это промежуточное программное обеспечение, которое представляет собой действие сервера, а все мои компоненты — это действия клиента, я решил создать API, чтобы checkisloggedin передавался из middleware.ts -> check-auth.ts (api) -> main-nav .tsx (компонент, который я хочу использовать, чтобы проверить, вошел ли пользователь в систему).

но когда я console.info, чтобы проверить, работает ли мой API, консоль элемента проверки моего браузера вернула эту ошибку

[main-nav-tsx][checkAuthStatus]: Error fetching auth status 
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
window.console.error    @   app-index.js:33

Видимо, это означало, что мой маршрут API не найден.

Проблема: я не уверен, как еще я могу передать логическое значение checkisloggedin своим компонентам, поскольку каждый метод, который я пытался проверить, вошел ли пользователь в систему, не работает. все мои каталоги тоже казались правильными.

Ожидаемый результат: мне просто нужен способ проверить, вошел ли пользователь в систему, и при необходимости отобразить кнопку выхода.

Я также открыт для новых решений, связанных с сеансами и токенами, но понятия не имею, как их использовать.

Это мои коды. Поскольку NextJS уделяет особое внимание файловым каталогам, я прокомментировал, где хранится каждый файл, в самом верху.

// src/middleware.ts

// import { auth } from "./auth"
import authConfig from "./auth.config"
import NextAuth from "next-auth"
import {
    DEFAULT_LOGIN_REDIRECT,
    apiAuthPrefix,
    authRoutes,
    publicRoutes,
} from "@/routes"
import { NextResponse } from "next/server";

const { auth } = NextAuth(authConfig);

export const checkIsLoggedIn = (req: any): boolean => {
    return !!req.auth;
};

export default auth((req) => {
    const { nextUrl } = req;
    const isLoggedIn = !!req.auth;
    console.info('[middleware-ts][checkisloggedin]: ', checkIsLoggedIn(req));
    console.info('[middleware-ts][isloggedin]: ', isLoggedIn);
    const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
    const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
    const isAuthRoute = authRoutes.includes(nextUrl.pathname);

    if (isApiAuthRoute) {
        return NextResponse.next();
    }

    if (isAuthRoute) {
        if (isLoggedIn) {
            return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl))
        }
        return NextResponse.next();
    }

    if (!isLoggedIn && !isPublicRoute) {
        return NextResponse.redirect(new URL("/auth/login", nextUrl));
    }
    return NextResponse.next();
})

export const config = {
    // matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
    // matcher: ["/auth/login", "/auth/register"],
    matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
} 
// src/components/main-nav.tsx

"use client"

import * as React from "react"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { signOut } from "@/auth"

export function MainNav() {
    const pathname = usePathname()

    const [isLoggedIn, setIsLoggedIn] = React.useState(false);

    React.useEffect(() => {
        const checkAuthStatus = async () => {
            console.info('[main-nav-tsx][checkAuthStatus]: Checking auth status...');
            try {
                const response = await fetch('/app/api/authentication/check-auth');
                if (response.ok) {
                    const data = await response.json();
                    console.info('[main-nav-tsx][checkAuthStatus]: Auth status response:', data);
                    setIsLoggedIn(data.isLoggedIn);
                } else {
                    console.error('[main-nav-tsx][checkAuthStatus]: Error fetching auth status', response.status);
                }
            } catch (error) {
                console.error('[main-nav-tsx][checkAuthStatus]: Error fetching auth status', error);
            }
        }

        checkAuthStatus();
    }, []);

    /**
     * handleSignOut is a server side action to check for log in 
     * main-nav is a client side action 
     * even though the method is post, we cannot use server action for checking if logged in is true or false
     * hence a POST api is needed
     */
    const handleSignOut = async () => {
        const response = await fetch('/app/api/authentication/signout', {
            method: 'POST',
        });
        if (response.ok) {
            // Handle successful sign-out, e.g., redirect or update state
            setIsLoggedIn(false);
        } else {
            // Handle error
            console.error('Error signing out');
        }
    };

    return (
        <div className = "mr-4 hidden md:flex">
            <Link href = "/" className = "mr-6 flex items-center space-x-2">
                <Icons.logo className = "h-6 w-6" />
                <span className = "hidden font-bold sm:inline-block">
                    {siteConfig.name}
                </span>
            </Link>
            <nav className = "flex items-center gap-4 text-sm lg:gap-6">
                {docsConfig.mainNav?.map(item => (
                    item.href && (
                        <Link
                            key = {item.href}
                            href = {item.href}
                            className = {cn(
                                "transition-colors hover:text-foreground/80",
                                pathname === item.href ? "text-foreground" : "text-foreground/60"
                            )}
                        >
                            {item.title}
                        </Link>
                    )
                ))}

                {/* change this button else where */}
                {isLoggedIn && (
                    <button onClick = {handleSignOut}>Sign Out</button>
                )}

            </nav>
        </div>
    )
}
// src/app/api/authentication/check-auth.ts

import { NextApiRequest, NextApiResponse } from 'next'
import { checkIsLoggedIn } from '../../../middleware'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
    const isLoggedIn = checkIsLoggedIn(req);
    // console.info("[check-auth-ts]: ", isLoggedIn)
    res.status(200).json({ isLoggedIn });
}

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Получение данных сеанса

В предоставленном вами руководстве для аутентификации используются сторонние библиотеки, в частности следующая аутентификация. Итак, если вы хотите получить доступ к данным сеанса, все, что вам нужно, это просто использовать крючок useSession. Он предоставляет поле статуса, которое может иметь одно из следующих значений: "loading" | "authenticated" | "unauthenticated".

Передача данных из промежуточного программного обеспечения на страницы

Однако вы также спрашиваете, как передавать данные из промежуточного программного обеспечения на страницы, что немного сложнее. Что касается текущей версии (14.2.4), Next.js использует Edge runtime для промежуточного программного обеспечения и маршрутов API, при этом по умолчанию используется Node для рендеринга. Поэтому я сомневаюсь, что это вообще возможно. Что ж, вы можете подписаться на Edge Runtime , но Next.js по-прежнему не предоставляет никакого прямого способа доступа req к объекту, если вы используете App Router. Обсуждения смотрите здесь.

Что касается возможных решений передачи данных из промежуточного программного обеспечения, могу предложить несколько решений:

  1. Переместите логику промежуточного программного обеспечения в компонент template.tsx или layout.tsx. Там вы можете получить доступ к заголовкам и файлам cookie и получить из них необходимые данные. Затем вы можете поместить его в удобное вам решение для управления состоянием (Redux, React Context и т. д.). Что-то вроде этого:

    export const Layout = ({ children }: LayoutProps) => {
      const userData = getUserData();
      return (
        <StateProvider userData = {userData}>
          {children}
        </StateProvider>
      )
    }
    

Таким образом, при необходимости будет удобнее передавать больше пользовательских данных — информацию о пользователе, роли и т. д. Кроме того, это хорошее место для выполнения сетевого запроса, если полученные из него данные актуальны для всего приложения.

  1. Если это не вариант для вас из-за некоторых ограничений, вы можете сделать довольно хакерское решение, но, тем не менее, работающее. Вы можете создать собственный заголовок, установить его в промежуточном программном обеспечении и получить его со своей страницы.

     // middleware.ts
     const myMiddleware = (req: Request, res: Response, next: NextFunction) => {
       const userIsAuthenticated = checkAuthentication(req);
       req.headers['x-user-authenticated'] = userIsAuthenticated;
       return next();
     }; 
    
    
     // page.ts
     const MyPage = () => {
       const userIsAuthenticated = headers().get('x-user-authenticated');
       // rest of the logic
     }; 
    

Однако я бы воздержался от этого решения, потому что обычно в контексте аутентификации вам нужно больше, чем одно поле. Да, передача каждого отдельного поля в отдельный заголовок затруднительна.

Краткое содержание

  1. Если вы используете библиотеку next-auth (или что-то подобное), она предоставляет API для получения данных сеанса на страницах — например, крючок useSession.
  2. Не существует простого способа передачи данных из следующего промежуточного программного обеспечения. Вы можете переместить логику в компонент макета или шаблона для страниц приложения или использовать собственные заголовки, если это невозможно.

о, так никогда не было способа «естественно» передавать значения из промежуточного программного обеспечения на мои страницы? я никогда этого не знал. Спасибо.

Wer Wer 24.06.2024 20:36

К сожалению нет. Я считаю, что в более старых версиях объект req использовался совместно разными контекстами (пользовательский _document и страница), возможно, даже промежуточным программным обеспечением. В любом случае, для вашего случая аутентификации библиотеки next-auth должно быть достаточно.

Vladyslav Shlianin 25.06.2024 09:30

Другие вопросы по теме