authentication - 这是将 Next.js + NextAuth 与 Django Rest Framework API 连接的正确且安全的方式吗?

标签 authentication django-rest-framework next.js next-auth

我一直在使用 Django Rest Framework 开发带有自定义后端的 Next.js 应用程序,主要关注社交平台(Google、Github 等)的身份验证。这是我想要使用的流程:

  • 让 NextAuth 为社会认证做繁重的工作。例如,当用户想要使用他/她的 Google 帐户登录时,它会返回一个访问 token 和一个 id token 。
  • 将谷歌返回的 id token 和访问 token 放入 NextAuth session 对象中。
  • 在前端,使用 session 对象中的这两个 token 向 DRF 后端发出 POST 请求,后端基本上接受访问 token 和 id token ,并返回访问 token 和刷新 token 。注意。 DRF 后端有 dj-rest-authdjango-allauth设置以处理社交身份验证。
  • DRF 后端以 HTTPOnly cookie 的形式发回 token 。所以,下次我想向 DRF API 发出请求时,cookie 应该随请求一起传递。

  • 这是正确和安全的,还是我在用脚射击?
    我的上下文代码:index.tsx
    import React, { useEffect } from "react";
    import { signIn, signOut, useSession } from "next-auth/client";
    import { Typography, Button, Box } from "@material-ui/core";
    import { makeUrl, BASE_URL, SOCIAL_LOGIN_ENDPOINT } from "../urls";
    import axios from "axios";
    axios.defaults.withCredentials = true;
    
    function auth() {
      const [session, loading] = useSession();
    
      useEffect(() => {
        const getTokenFromServer = async () => {
          // TODO: handle error when the access token expires
          const response = await axios.post(
            // DRF backend endpoint, api/social/google/ for example
            // this returns accessToken and refresh_token in the form of HTTPOnly cookies
            makeUrl(BASE_URL, SOCIAL_LOGIN_ENDPOINT, session.provider),
            {
              access_token: session.accessToken,
              id_token: session.idToken,
            },
          );
        };
    
        if (session) {
          getTokenFromServer();
        }
      }, [session]);
    
      return (
        <React.Fragment>
          <Box
            display="flex"
            justifyContent="center"
            alignItems="center"
            m={5}
            p={5}
            flexDirection="column"
          >
            {!loading && !session && (
              <React.Fragment>
                <Typography variant="button">Not logged in</Typography>
                <Button
                  variant="outlined"
                  color="secondary"
                  onClick={() => signIn()}
                >
                  Login
                </Button>
              </React.Fragment>
            )}
            {!loading && session && (
              <React.Fragment>
                <Typography>Logged in as {session.user.email}</Typography>
                <pre>{JSON.stringify(session, null, 2)}</pre>
                <Button
                  variant="outlined"
                  color="primary"
                  onClick={() => signOut()}
                >
                  Sign Out
                </Button>
              </React.Fragment>
            )}
          </Box>
        </React.Fragment>
      );
    }
    
    export default auth;
    
    api/auth/[...nextauth].ts
    import NextAuth from "next-auth";
    import { InitOptions } from "next-auth";
    import Providers from "next-auth/providers";
    import { NextApiRequest, NextApiResponse } from "next";
    import axios from "axios";
    
    import { BASE_URL, SOCIAL_LOGIN_ENDPOINT, makeUrl } from "../../../urls";
    import { AuthenticatedUser, CustomSessionObject } from "../../../types";
    import { GenericObject } from "next-auth/_utils";
    
    const settings: InitOptions = {
      providers: [
        Providers.Google({
          clientId: process.env.GOOGLE_CLIENT_ID,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET,
          authorizationUrl:
            "https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code",
        }),
      ],
    
      secret: process.env.NEXT_AUTH_SECRET,
    
      session: {
        maxAge: 6 * 60 * 60, // 6 hours
      },
    
      callbacks: {
        async signIn(user: AuthenticatedUser, account, profile) {
          if (account.provider === "google") {
            const { accessToken, idToken, provider } = account;
            user.accessToken = accessToken;
            user.idToken = idToken;
            user.provider = provider;
            return true;
          }
    
          return false;
        },
    
        async session(session: CustomSessionObject, user: AuthenticatedUser) {
          session.accessToken = user.accessToken;
          session.idToken = user.idToken;
          session.provider = user.provider;
          return session;
        },
    
        async jwt(token, user: AuthenticatedUser, account, profile, isNewUser) {
          if (user) {
            token.accessToken = user.accessToken;
            token.idToken = user.idToken;
            token.provider = user.provider;
          }
    
          return token;
        },
      },
    };
    
    export default (req: NextApiRequest, res: NextApiResponse) => {
      return NextAuth(req, res, settings);
    };
    
    
    session 对象是否足够安全以存储 token 这一事实让我感到压力很大。我还想实现一种机制,在访问 token 过期时使用刷新 token 刷新访问 token 。

    最佳答案

    随着时间的推移,我最终解决了这个问题。我写了一篇由两部分组成的文章,概述了我是如何解决它的,可以找到 herehere

    关于authentication - 这是将 Next.js + NextAuth 与 Django Rest Framework API 连接的正确且安全的方式吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66288809/

    相关文章:

    python - 如何配置 Apache Airflow 以使用 MS SQL Server Windows 身份验证

    next.js - 在 next.js api 中使用订阅的 Apollo Server 路由 : websockets trouble

    javascript - 如何使 Next.js "see"成为 react-dom 包?

    python - 如何重写 django Rest 框架 ModelViewSet 的创建操作来创建批量记录?

    python - Django REST 框架 : type object X has no attribute 'get_extra_actions'

    python - Wagtail API - 如何公开片段

    javascript - Next.js 正在执行两次完整的 F5 刷新操作以在静态页面上显示新内容 `revalidate: 1` ?我期待一个

    c# - 未经授权时重定向

    session - 在 JSF 中设置/获取 session 属性

    javascript - Angular 和 Typescript - 从管理员登录中停用其他用户时出现问题