英文:
How can I make sure a cookie is set before using it?
问题
我目前正在使用 Next.js 和 NestJS 开发 JWT 授权代码流。
这是我从前端发送到后端的 POST 请求:
const response = await fetch(
'http://localhost:4000/auth/42/callback?code=' + code, { // code 是从 OAuth 服务器接收到的
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
}
);
一旦后端接收到请求,它将生成 JWT 并使用 response
将其发送回前端:
@Post('42/callback')
async Callback(@Query('code') code: string, @Res({passthrough: true}) res) {
if (!code) {
res.status(400).send('缺少 code');
return;
}
try {
const response = await this.authService.exchangeCodeForToken(code);
const jwt = await this.authService.handleCallback(response);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.cookie('jwt', jwt, {
sameSite: 'strict'
});
res.status(200).send();
} catch (error) {
console.error(error);
res.status(500).send('内部服务器错误');
}
}
浏览器成功设置了 jwt
cookie,但我无法确保在使用之前它已被设置。
似乎在响应和设置 cookie 之间存在一小段延迟:
import jwtDecode from 'jwt-decode';
import { useCookies } from 'react-cookie';
const [ cookies ] = useCookies();
const jwt = cookies.jwt;
const jwtPayload: any = jwtDecode(jwt); // 这里 jwt 是未定义的
英文:
I'm currently working on a JWT authorization code flow using Next.js and NestJS.
This is the POST request I'm sending from the front to the back.
const response = await fetch(
'http://localhost:4000/auth/42/callback?code=' + code, { // code is received from the OAuth server
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
}
);
Once the backend receives the request, it will generate the JWT and send it back to the front using response
.
@Post('42/callback')
async Callback(@Query('code') code: string, @Res({passthrough: true}) res) {
if (!code) {
res.status(400).send('Missing code');
return;
}
try {
const response = await this.authService.exchangeCodeForToken(code);
const jwt = await this.authService.handleCallback(response);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.cookie('jwt', jwt, {
sameSite: 'strict'
});
res.status(200).send();
} catch (error) {
console.error(error);
res.status(500).send('Internal server error');
}
}
The browser successfully sets the jwt
cookie, however I'm unable to find a way to make sure it is set before using it.
It seems that there is a small latency between the response and setting the cookie.
import jwtDecode from 'jwt-decode';
import { useCookies } from 'react-cookie';
const [ cookies ] = useCookies();
const jwt = cookies.jwt;
const jwtPayload: any = jwtDecode(jwt); // Here jwt is undefined
I've looked at the browser Network Profiler but I'm not able to see any latency.
答案1
得分: 0
为了绕过延迟并确保只在设置后访问 cookie 值,您可以使用 useEffect 钩子。类似于这样:
import { useEffect } from 'react'
import jwtDecode from 'jwt-decode'
import { useCookies } from 'react-cookie'
function YourComponent() {
const [cookies, setCookie] = useCookies()
const jwt = cookies.jwt
useEffect(() => {
if (jwt) {
const jwtPayload = jwtDecode(jwt);
// 访问 jwtPayload 或在此处执行其他与 JWT 相关的操作
}
}, [jwt])
// 组件的其余代码
return <div>您的组件内容</div>
}
export default YourComponent;
或者,如果您喜欢的话,您还可以创建一个自定义钩子,类似于这样:
import { useEffect, useState } from 'react'
import jwtDecode from 'jwt-decode'
import { useCookies } from 'react-cookie'
function useJWT() {
const [cookies] = useCookies()
const [jwt, setJWT] = useState(null)
const [jwtPayload, setJWTPayload] = useState(null)
useEffect(() => {
if (cookies.jwt) {
setJWT(cookies.jwt)
}
}, [cookies.jwt])
useEffect(() => {
if (jwt) {
const decodedPayload = jwtDecode(jwt)
setJWTPayload(decodedPayload);
} else {
setJWTPayload(null)
}
}, [jwt])
return jwtPayload
}
export default useJWT
请注意,这些代码示例中的注释和标签也已经被翻译。
英文:
To bypass the delay and to ensure that you access the cookie value only after it has been set, you can use the useEffect hook. Something like this..
import { useEffect } from 'react'
import jwtDecode from 'jwt-decode'
import { useCookies } from 'react-cookie'
function YourComponent() {
const [cookies, setCookie] = useCookies()
const jwt = cookies.jwt
useEffect(() => {
if (jwt) {
const jwtPayload: any = jwtDecode(jwt);
// Access the jwtPayload or perform any other actions with the JWT here
}
}, [jwt])
// Rest of your component code
return <div>Your component content</div>
}
export default YourComponent;
Alternatively you can also create a custom hook if you prefer. something like this...
import { useEffect, useState } from 'react'
import jwtDecode from 'jwt-decode'
import { useCookies } from 'react-cookie'
function useJWT() {
const [cookies] = useCookies()
const [jwt, setJWT] = useState(null)
const [jwtPayload, setJWTPayload] = useState(null)
useEffect(() => {
if (cookies.jwt) {
setJWT(cookies.jwt)
}
}, [cookies.jwt])
useEffect(() => {
if (jwt) {
const decodedPayload = jwtDecode(jwt)
setJWTPayload(decodedPayload);
} else {
setJWTPayload(null)
}
}, [jwt])
return jwtPayload
}
export default useJWT
答案2
得分: 0
我终于找到了解决方法。
显然,在某些情况下,react-cookie
不可靠(请参见这个问题或这个问题)。当添加新的 Cookie 时,useCookies
钩子不会更新。在此问题得到修复之前,最佳的解决方法是使用universal-cookie。
这是它的样子:
import Cookies from 'universal-cookie';
import jwtDecode from 'jwt-decode';
const cookies = new Cookies();
const jwt = cookies.get('jwt');
const decodedPayload = jwtDecode(jwt);
英文:
I've finally found a solution to my problem.
Apparently, react-cookie
is not reliable in some situations (see this issue or this one). The useCookies
hook is not updating when a new cookie is added. While this issue isn't fixed the best workaround is to use universal-cookie instead.
Here is what it will looks like:
import Cookies from 'universal-cookie';
import jwtDecode from 'jwt-decode';
const cookies = new Cookies();
const jwt = cookies.get('jwt');
const decodedPayload = jwtDecode(jwt);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论