next.js 如何发送httponly cookie客户端时使用下一个认证凭证提供程序?

oknwwptz  于 2023-06-22  发布在  其他
关注(0)|答案(3)|浏览(110)

我正在创建下一个js应用程序,使用next-auth来处理身份验证。
我有一个外部后端API,所以我使用Credentials Provider。
问题是后端发送httponly cookie,但当我向客户端发出请求时,这些cookie并没有附加到浏览器。
在/pages/API/[... auth].js中

import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import clientAxios from '../../../config/configAxios'

export default NextAuth({
    providers: [
        Providers.Credentials({
            async authorize(credentials) {
                try {
                    const login = await clientAxios.post('/api/login', {
                        username: credentials.username,
                        password: credentials.password,
                        is_master: credentials.is_master
                    })

                    const info = login.data.data.user
                    const token = {
                        accessToken: login.data.data.access_token,
                        expiresIn: login.data.data.expires_in,
                        refreshToken: login.data.data.refresh_token
                    }
                    // I can see cookies here
                    const cookies = login.headers['set-cookie']

                    return { info, token, cookies }
                } catch (error) {
                    console.log(error)
                    throw (Error(error.response.data.M))
                }
            }
        })
    ],
    callbacks: {
        async jwt(token, user, account, profile, isNewUser) {
            if (token) {
               // Here cookies are set but only in server side
               clientAxios.defaults.headers.common['Cookie'] = token.cookies
            }
            if (user) {
                token = {
                    user: user.info,
                    ...user.token,
                }
            }

            return token
        },
        async session(session, token) {
            // Add property to session, like an access_token from a provider.
            session.user = token.user
            session.accessToken = token.accessToken
            session.refreshToken = token.refreshToken

            return session
        }
    },
    session: {
        jwt: true
    }
})

我的axios配置文件

import axios from 'axios';

const clientAxios = axios.create({

    baseURL: process.env.backendURL,
    withCredentials: true,
    headers:{
        'Accept' : 'application/json',
        'Content-Type' : 'application/json'
    }

});

export default clientAxios;

页面组件

import { getSession } from "next-auth/client";
import clientAxios from "../../../config/configAxios";
import { useEffect } from "react"

export default function PageOne (props) {
    useEffect(async () => {
      // This request fails, cookies are not sent
      const response = await clientAxios.get('/api/info');
    }, [])

    return (
        <div>
           <h1>Hello World!</h1>
        </div>
    )
}

export async function getServerSideProps (context) {
    const session = await getSession(context)

    if (!session) {
        return {
            redirect: {
                destination: '/login',
                permanent: false
            }
        }
    }

    // This request works
    const response = await clientAxios.get('/api/info');
    
    return {
        props: {
            session,
            info: response.data
        }
    }
}
htzpubme

htzpubme1#

经过一段时间的研究,我发现了它。
我必须在/pages/API/auth中更改导出NextAuth的方式。
而不是

export default NextAuth({
    providers: [
       ...
    ]

})

像这样导出它,这样我们就可以访问请求和响应对象

export default (req, res) => {
    return NextAuth(req, res, options)
}

但是要在options对象中访问它们,我们可以将其设为回调

const nextAuthOptions = (req, res) => {
    return {
        providers: [
           ...
        ]
    }
}

export default (req, res) => {
    return NextAuth(req, res, nextAuthOptions(req, res))
}

要从后端向前端发送cookie,我们必须在响应中添加'Set-Cookie'头

res.setHeader('Set-Cookie', ['cookie_name=cookie_value'])

完整的代码是

import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';

const nextAuthOptions = (req, res) => {
    return {
        providers: [
           CredentialsProvider({
                async authorize(credentials) {
                   try {                      
                        const response = await axios.post('/api/login', {
                            username: credentials.username,
                            password: credentials.password
                        })

                        const cookies = response.headers['set-cookie']

                        res.setHeader('Set-Cookie', cookies)
                        
                        return response.data
                    } catch (error) {
                        console.log(error)
                        throw (Error(error.response))
                    } 
                }
           })
        ]
    }
}

export default (req, res) => {
    return NextAuth(req, res, nextAuthOptions(req, res))
}

更新- Typescript示例

为回调nextAuthOptions创建类型

import { NextApiRequest, NextApiResponse } from 'next';
import { NextAuthOptions } from 'next-auth';

type NextAuthOptionsCallback = (req: NextApiRequest, res: NextApiResponse) => NextAuthOptions

结合所有的东西

import { NextApiRequest, NextApiResponse } from 'next';
import NextAuth, { NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import axios from 'axios'

type NextAuthOptionsCallback = (req: NextApiRequest, res: NextApiResponse) => NextAuthOptions

const nextAuthOptions: NextAuthOptionsCallback = (req, res) => {
     return {
        providers: [
           CredentialsProvider({
                credentials: {
                },
                async authorize(credentials) {
                   try {                      
                        const response = await axios.post('/api/login', {
                            username: credentials.username,
                            password: credentials.password
                        })

                        const cookies = response.headers['set-cookie']

                        res.setHeader('Set-Cookie', cookies)

                        return response.data
                    } catch (error) {
                        console.log(error)
                        throw (Error(error.response))
                    } 
                }
           })
        ],
        callbacks: {
            ...
        },
        session: {
            ...
        }
    }
}

export default (req: NextApiRequest, res: NextApiResponse) => {
    return NextAuth(req, res, nextAuthOptions(req, res))
}
vngu2lb8

vngu2lb82#

为了在注销后删除nextAuth中的cookie,我使用了下面的代码块-设置cookie参数以匹配cookie过期时的参数-使用[... nextauh]. js文件中的SignOut事件

export default async function auth(req, res) {
    return await NextAuth(req, res, {
        ...    
        events: {
            async signOut({ token }) {
                res.setHeader("Set-Cookie", "cookieName=deleted;Max-Age=0;path=/;Domain=.techtenum.com;");
            },
        },
        ...
     }
}
2hh7jdfx

2hh7jdfx3#

您需要配置clientAxios,以便在返回给服务器的所有请求中包含服务器作为其响应的一部分发送的cookie。设置api.defaults.withCredentials = true;应该会得到你想要的。请参阅下面我的vue应用程序的axios配置:

import axios from "axios";

export default ({ Vue, store, router }) => {
  const api = axios.create({
    baseURL: process.env.VUE_APP_API_URL
  });
  api.defaults.withCredentials = true; ------> this line includes the cookies
  Vue.prototype.$axios = api;
  store.$axios = api;
};

相关问题