如何从中间件或next.js和next-auth. js中的API的公共位置检查授权令牌及其有效性?

dxpyg8gm  于 2023-06-22  发布在  其他
关注(0)|答案(2)|浏览(93)

我正在处理一个需要实现身份验证和授权的项目。现在,我目前陷入了从一个普通的API授权,而不是为每个API编写公共代码。
例如,我正在获取一些数据,如下所示:

import prisma from '@/lib/prisma';
import { NextRequest, NextResponse } from 'next/server';
import { verifyJwt } from '../../../lib/jwt';

export async function GET(request: NextRequest) {
  const accessToken = request.headers.get("authorization")?.split(' ')[1];

  if (!accessToken || !verifyJwt(accessToken)) {
    return NextResponse.json(
      {
        message: "Unauthorized",
      },
      {
        status: 401,
      }
    );
  }

  try {
    const posts = await prisma.post.findMany();
    return NextResponse.json({ data: posts }, { status: 200 });
  } catch (error: any) {
    return NextResponse.json({ message: error.toString() }, { status: 500 });
  }
}

正如你在上面的代码中看到的,在获取一些博客文章之前,我需要检查授权。那么,我如何从一个普通的API编写下面的代码呢?

const accessToken = request.headers.get("authorization")?.split(' ')[1];

if (!accessToken || !verifyJwt(accessToken)) {
  return NextResponse.json(
    {
      message: "Unauthorized",
    },
    {
      status: 401, // token not found
    }
  );
}

next-auth.ts

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

export const authConfig: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      id: 'credentials',
      name: 'Credentials',
      credentials: {
        email: { label: "Email", type: "text", placeholder: "jsmith@email.com" },
        password: { label: "Password", type: "password" }
      },
      async authorize(credentials, req) {
        if (!credentials
          || !credentials.email
          || !credentials.password
        ) {
          return null;
        }

        const res = await fetch("http://localhost:3000/api/auth/token", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            username: credentials?.email,
            password: credentials?.password,
          }),
        });

        const user = await res.json();

        if (user && user?.data) {
          return user.data;
        } else {
          return null;
        }
      }
    }),
  ],
  session: {
    strategy: "jwt",
    maxAge: 60 * 60 * 24 * 7, // 7 days
  },
  pages: {
    signIn: '/login',
    error: '/login',
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        return { ...token, ...user };
      }
      return token;
    },

    async session({ session, token }) {
      if (token) {
        session.user = token;
      }
      return session;
    },
  },
}

jwt.ts

import jwt, { JwtPayload } from "jsonwebtoken";

const secretKey = process.env.JWT_SECRET || "";

interface SignOption {
  expiresIn?: string | number;
  issuer?: string;
}

const ACCESS_TOKEN_SIGN_OPTION = {
  expiresIn: "1h",
  issuer: 'abcsoftware',
};

const REFRESH_TOKEN_SIGN_OPTION = {
  expiresIn: "7d",
  issuer: 'abcsoftware',
};

export function signJwtAccessToken(
  payload: JwtPayload,
  options: SignOption = ACCESS_TOKEN_SIGN_OPTION
) {
  const token = jwt.sign(payload, secretKey, options);
  return token;
}

export function signJwtRefreshToken(
  payload: JwtPayload,
  options: SignOption = REFRESH_TOKEN_SIGN_OPTION
) {
  const token = jwt.sign(payload, secretKey, options);
  return token;
}

// Verify authorization
export function verifyJwt(token: string) {
  try {
    const decoded = jwt.verify(token, secretKey!);
    return decoded as JwtPayload;
  } catch (error: any) {
    console.log(error);
    return null;
  }
}

下一个版本是
13.4.6
下一个验证版本为
4.22.1
我用的是Next.js的App Router
先谢谢你了!

hsgswve4

hsgswve41#

要创建一个仔细检查授权令牌及其相关性的中间件,可以在Next.js和NextAuth.js中为API实现相互授权检查。下面是你可以修改代码的方法:
在项目目录中,可以生成一个名为apiMiddleware.ts(或任何名称)的新文件。
通过在apiMiddleware.ts中定义一个函数,使用中间件检查授权令牌。看看这个实现以供参考:

import { NextRequest, NextResponse } from 'next/server';
import { verifyJwt } from './jwt';

export function checkAuthorization(request: NextRequest, next: () => NextResponse) {
  const accessToken = request.headers.get("authorization")?.split(' ')[1];

  if (!accessToken || !verifyJwt(accessToken)) {
    return NextResponse.json(
      {
        message: "Unauthorized",
      },
      {
        status: 401,
      }
    );
  }

  return next();
}

以下是如何在API路由文件中切换内容:使用apiMiddleware.ts中的checkAuthorization函数作为API路由的中间件。检查以下代码片段:导入checkAuthorization函数并将其集成到现有代码中。

import prisma from '@/lib/prisma';
import { NextRequest, NextResponse } from 'next/server';
import { checkAuthorization } from './apiMiddleware';

export async function GET(request: NextRequest) {
  try {
    const posts = await prisma.post.findMany();
    return NextResponse.json({ data: posts }, { status: 200 });
  } catch (error: any) {
    return NextResponse.json({ message: error.toString() }, { status: 500 });
  }
}

// Apply the authorization middleware to the API route
export default function handler(request: NextRequest) {
  return checkAuthorization(request, () => GET(request));
}

根据您的项目结构,不要忘记更新apiMiddleware.ts文件中的导入路径。只需将中间件应用于每个需要授权的API路由,就可以使用此方法重用checkAuthorization中间件,而无需重复复制授权代码。
我假设jwt.ts中的verifyJwt函数正确地验证了JWT令牌,如果令牌有效,则返回解码后的有效负载。

wbgh16ku

wbgh16ku2#

对于服务器端授权,写入middleware
使用项目根目录中的文件middleware.ts(或.js)来定义Middleware。例如,与页面或应用程序处于同一级别,或者在src内部(如果适用)。
由于您使用的是next-auth,因此可以使用getToken函数
接受NextAuth.js请求(req)并返回NextAuth.js发出的JWT有效负载或原始JWT字符串。我们在cookie或Authorization头中查找JWT。

import { getToken } from "next-auth/jwt";

async function middleware(req) {
    const pathname = req.nextUrl.pathname;

    const isAuth = await getToken({ req });
    // then you add your logic

}

// specify for which routes you want to run the above middleware

export const config = {
  matchter: ["/", "/dashboard/:path*"],
};

相关问题