将 React 中的 Auth0 重定向延迟到收到用户凭据之后
Delay Auth0 redirect in React until after user credentials have been received
我正在开发一个使用 auth0.js 进行用户身份验证的 React 应用程序。
当前进程是:
- 用户单击主页上的
Login
按钮,将他们重定向到登录页面,在那里他们输入登录详细信息并提交表单。
- 提交表单后,用户将使用 Auth0 登录(或正确处理错误)并重定向回主页。
- 随着应用程序刷新,自定义
handleAuthentication()
函数被调用,它解析 URL 散列片段并设置用户会话,这涉及将访问令牌、到期时间和用户信息存储在localStorage
在刷新时提供持久性。
- 返回主页后,
Login
按钮应替换为用户个人资料图片的头像和用户个人资料页面的 link。
然而,虽然这个过程有效,但它并不总是第一次有效。有时我需要刷新页面,因为找不到用户信息。我发现原因是对 auth0client.client.userInfo()
的调用是异步的,因此在检索要存储在 localStorage
中的用户信息时会稍有延迟。因此,主页很可能会在 Auth0 发送响应之前呈现。当我第二次刷新页面时,用户信息已被检索并存储,因此主页可以正确呈现。
以下是上述过程中的部分相关代码:
auth.login()
login(realm, username, password, handleIncorrect) {
// Attempt to log the user in
this.auth0Client.login(
{
realm: realm,
username: username,
password: password,
},
(err, authResult) => {
// handle result/error
}
);
}
auth.handleAuthentication()
handleAuthentication() {
// Attempt to authenticate the user in the app
this.auth0Client.parseHash({}, (err, authResult) => {
if (err) {
return;
} else if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
}
});
}
auth.setSession()
setSession(authResult) {
// Use the results from handleAuthentication() to set up the user's browser session
let expiresAt = authResult.expiresIn * 1000 + new Date().getTime();
this.accessToken = authResult.accessToken;
this.idToken = authResult.idToken;
this.expiresAt = expiresAt;
localStorage.setItem("access_token", authResult.accessToken);
localStorage.setItem("id_token", authResult.idToken);
localStorage.setItem("expires_at", expiresAt);
this.auth0Client.client.userInfo(authResult.accessToken, (err, user) => {
if (err) {
console.log("setSession: Couldn't get user info");
return;
} else {
localStorage.setItem("user", JSON.stringify(user));
}
});
}
auth.getUserInfo()
getUserInfo() {
// Return user info
const user = localStorage.getItem("user");
if (!user) {
console.log("getUserInfo: No user info to retrieve");
return;
}
return JSON.parse(user);
}
App.js
export default function App() {
auth.handleAuthentication();
return (
<BrowserRouter>
<Suspense fallback={<p>Loading...</p>}>
<Layout>
<Switch>
// Routes to pages
</Switch>
</Layout>
</Suspense>
</BrowserRouter>
auth
是我创建的自定义 class 以使用 localStorage
扩展默认的 Auth0 功能。
App.js
显示 handleAuthentication()
(以及 setSession()
)在呈现新路由(本例中为主页)之前被调用。但是 setSession()
通常需要比页面呈现时间更长的时间来完成。因此,当主页加载并调用 auth.getUserInfo()
时,用户信息最初可能会从 localStorage
中丢失,因此 user is undefined
错误大约有一半出现。
经过一些研究,我想出了几个可能的解决方案。
- 使用超时延迟主页呈现,直到
localStorage
中存在用户信息,同时显示加载页面。但我读到其他 SO 答案,你不应该延迟 React 渲染,因为这会导致其他问题。所以解决方案#2:
- 最初在主页上显示一个占位符头像图像,直到用户信息在
localStorage
中可用。然后,使用用户信息更新状态以重新渲染并显示用户的实际头像图像。
这两种方法都是 good/correct 方法吗?如果没有,还有什么其他方法可以用来处理这个问题?
我会这样做:
使用状态跟踪响应何时到达
const [arrived, setArrived] = useState(false)
并在 setSession()
项添加到 localStorage
后在 setSession()
中更改它:
this.auth0Client.client.userInfo(authResult.accessToken, (err, user) => {
if (err) {
console.log("setSession: Couldn't get user info");
return;
} else {
localStorage.setItem("user", JSON.stringify(user));
// over here
setArrived(true);
}
});
和 运行 getUserInfo()
在 useEffect()
内:
useEffect(() => {
// run logic here to get the user from localStorage
// I would also keep this information inside state
}, [arrived])
我正在开发一个使用 auth0.js 进行用户身份验证的 React 应用程序。
当前进程是:
- 用户单击主页上的
Login
按钮,将他们重定向到登录页面,在那里他们输入登录详细信息并提交表单。 - 提交表单后,用户将使用 Auth0 登录(或正确处理错误)并重定向回主页。
- 随着应用程序刷新,自定义
handleAuthentication()
函数被调用,它解析 URL 散列片段并设置用户会话,这涉及将访问令牌、到期时间和用户信息存储在localStorage
在刷新时提供持久性。 - 返回主页后,
Login
按钮应替换为用户个人资料图片的头像和用户个人资料页面的 link。
然而,虽然这个过程有效,但它并不总是第一次有效。有时我需要刷新页面,因为找不到用户信息。我发现原因是对 auth0client.client.userInfo()
的调用是异步的,因此在检索要存储在 localStorage
中的用户信息时会稍有延迟。因此,主页很可能会在 Auth0 发送响应之前呈现。当我第二次刷新页面时,用户信息已被检索并存储,因此主页可以正确呈现。
以下是上述过程中的部分相关代码:
auth.login()
login(realm, username, password, handleIncorrect) {
// Attempt to log the user in
this.auth0Client.login(
{
realm: realm,
username: username,
password: password,
},
(err, authResult) => {
// handle result/error
}
);
}
auth.handleAuthentication()
handleAuthentication() {
// Attempt to authenticate the user in the app
this.auth0Client.parseHash({}, (err, authResult) => {
if (err) {
return;
} else if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
}
});
}
auth.setSession()
setSession(authResult) {
// Use the results from handleAuthentication() to set up the user's browser session
let expiresAt = authResult.expiresIn * 1000 + new Date().getTime();
this.accessToken = authResult.accessToken;
this.idToken = authResult.idToken;
this.expiresAt = expiresAt;
localStorage.setItem("access_token", authResult.accessToken);
localStorage.setItem("id_token", authResult.idToken);
localStorage.setItem("expires_at", expiresAt);
this.auth0Client.client.userInfo(authResult.accessToken, (err, user) => {
if (err) {
console.log("setSession: Couldn't get user info");
return;
} else {
localStorage.setItem("user", JSON.stringify(user));
}
});
}
auth.getUserInfo()
getUserInfo() {
// Return user info
const user = localStorage.getItem("user");
if (!user) {
console.log("getUserInfo: No user info to retrieve");
return;
}
return JSON.parse(user);
}
App.js
export default function App() {
auth.handleAuthentication();
return (
<BrowserRouter>
<Suspense fallback={<p>Loading...</p>}>
<Layout>
<Switch>
// Routes to pages
</Switch>
</Layout>
</Suspense>
</BrowserRouter>
auth
是我创建的自定义 class 以使用 localStorage
扩展默认的 Auth0 功能。
App.js
显示 handleAuthentication()
(以及 setSession()
)在呈现新路由(本例中为主页)之前被调用。但是 setSession()
通常需要比页面呈现时间更长的时间来完成。因此,当主页加载并调用 auth.getUserInfo()
时,用户信息最初可能会从 localStorage
中丢失,因此 user is undefined
错误大约有一半出现。
经过一些研究,我想出了几个可能的解决方案。
- 使用超时延迟主页呈现,直到
localStorage
中存在用户信息,同时显示加载页面。但我读到其他 SO 答案,你不应该延迟 React 渲染,因为这会导致其他问题。所以解决方案#2: - 最初在主页上显示一个占位符头像图像,直到用户信息在
localStorage
中可用。然后,使用用户信息更新状态以重新渲染并显示用户的实际头像图像。
这两种方法都是 good/correct 方法吗?如果没有,还有什么其他方法可以用来处理这个问题?
我会这样做:
使用状态跟踪响应何时到达
const [arrived, setArrived] = useState(false)
并在 setSession()
项添加到 localStorage
后在 setSession()
中更改它:
this.auth0Client.client.userInfo(authResult.accessToken, (err, user) => {
if (err) {
console.log("setSession: Couldn't get user info");
return;
} else {
localStorage.setItem("user", JSON.stringify(user));
// over here
setArrived(true);
}
});
和 运行 getUserInfo()
在 useEffect()
内:
useEffect(() => {
// run logic here to get the user from localStorage
// I would also keep this information inside state
}, [arrived])