使用下一个身份验证凭据提供程序时如何发送 httponly cookie 客户端? [英] How to send httponly cookies client side when using next-auth credentials provider?

查看:12
本文介绍了使用下一个身份验证凭据提供程序时如何发送 httponly cookie 客户端?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个下一个 js 应用程序,使用 next-auth 来处理身份验证.

我有一个外部后端 api,所以我正在使用凭据提供程序.

问题是后端发送 httponly cookie,但是当我在客户端发出请求时,这些cookie没有附加到浏览器.

在/pages/api/[...auth].js 中

import NextAuth from 'next-auth';从 'next-auth/providers' 导入提供者;从'../../../config/configAxios'导入clientAxios导出默认 NextAuth({提供者:[Providers.Credentials({异步授权(凭据){尝试 {const login = await clientAxios.post('/api/login', {用户名:凭据.用户名,密码:凭据.密码,is_master:凭证.is_master})const info = login.data.data.user常量令牌 = {accessToken: login.data.data.access_token,expiresIn: login.data.data.expires_in,refreshToken:login.data.data.refresh_token}//我可以在这里看到 cookieconst cookies = login.headers['set-cookie']返回 { 信息、令牌、cookie }} 捕捉(错误){控制台日志(错误)抛出(错误(error.response.data.M))}}})],回调:{async jwt(token, user, account, profile, isNewUser) {如果(令牌){//这里设置了cookie,但只在服务器端clientAxios.defaults.headers.common['Cookie'] = token.cookies}如果(用户){令牌 = {用户:用户信息,...用户令牌,}}返回令牌},异步会话(会话,令牌){//向会话添加属性,例如来自提供者的 access_token.session.user = token.usersession.accessToken = token.accessTokensession.refreshToken = token.refreshToken返回会话}},会议: {jwt: 真}})

我的 axios 配置文件

import axios from 'axios';常量 clientAxios = axios.create({baseURL:process.env.backendURL,withCredentials:真,标题:{'接受' : '应用程序/json',内容类型":应用程序/json"}});导出默认客户端Axios;

一个页面组件

import { getSession } from "next-auth/client";从../../../config/configAxios"导入clientAxios;从反应"导入 { useEffect }导出默认功能 PageOne (props) {使用效果(异步()=>;{//这个请求失败,cookies没有发送常量响应 = 等待 clientAxios.get('/api/info');}, [])返回 (

<h1>你好世界!</h1></div>)}导出异步函数 getServerSideProps (context) {常量会话 = 等待 getSession(上下文)如果(!会话){返回 {重定向:{目的地:'/登录',永久:假}}}//这个请求有效常量响应 = 等待 clientAxios.get('/api/info');返回 {道具: {会议,信息:response.data}}}

解决方案

经过一段时间的研究,我想通了.

我必须在导出 NextAuth 的方式中更改/pages/api/auth.

代替

导出默认 NextAuth({提供者:[...]})

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

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

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

const nextAuthOptions = (req, res) =>{返回 {提供者:[...]}}导出默认值 (req, res) =>{返回 NextAuth(req, res, nextAuthOptions(req, res))}

要将 cookie 从后端发送回前端,我们必须在响应中添加一个Set-Cookie"标头

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

完整的代码是

import NextAuth from 'next-auth';从 'next-auth/providers/credentials' 导入 CredentialsProvider;const nextAuthOptions = (req, res) =>{返回 {提供者:[凭据提供程序({异步授权(凭据){尝试 {const response = await axios.post('/api/login', {用户名:凭据.用户名,密码:凭据.密码})const cookies = response.headers['set-cookie']res.setHeader('Set-Cookie', cookies)返回响应数据} 捕捉(错误){控制台日志(错误)抛出(错误(error.response))}}})]}}导出默认值 (req, res) =>{返回 NextAuth(req, res, nextAuthOptions(req, res))}

I'm creating a next js application, using next-auth to handle authentication.

I have an external backend api, so I'm using Credentials Provider.

The problem is that the backend sends httponly cookies, but those are not being attached to the browser when i make a request client side.

In /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
    }
})

my axios config file

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;

a page component

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
        }
    }
}

解决方案

After time of researching I have figured it out.

I had to make a change in /pages/api/auth in the way I'm exporting NextAuth.

Instead of

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

})

Export it like this, so we can have access to request and response object

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

But to access them in the options object, we can make it a callback

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

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

To send a cookie back to the frontend from the backed we must add a 'Set-Cookie' header in the respond

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

The complete code would be

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))
}

这篇关于使用下一个身份验证凭据提供程序时如何发送 httponly cookie 客户端?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆