将 React 中的 Auth0 重定向延迟到收到用户凭据之后

Delay Auth0 redirect in React until after user credentials have been received

我正在开发一个使用 auth0.js 进行用户身份验证的 React 应用程序。

当前进程是:

  1. 用户单击主页上的 Login 按钮,将他们重定向到登录页面,在那里他们输入登录详细信息并提交表单。
  2. 提交表单后,用户将使用 Auth0 登录(或正确处理错误)并重定向回主页。
  3. 随着应用程序刷新,自定义 handleAuthentication() 函数被调用,它解析 URL 散列片段并设置用户会话,这涉及将访问令牌、到期时间和用户信息存储在localStorage 在刷新时提供持久性。
  4. 返回主页后,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 错误大约有一半出现。

经过一些研究,我想出了几个可能的解决方案。

  1. 使用超时延迟主页呈现,直到 localStorage 中存在用户信息,同时显示加载页面。但我读到其他 SO 答案,你不应该延迟 React 渲染,因为这会导致其他问题。所以解决方案#2:
  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])