2

我目前正在构建一个博客示例应用程序,使用 NextJS、ApolloClient 和 MongoDB + MongoRealm。NextJS 骨架是在框架的官方页面教程之后构建的。目前,新用户可以通过访问在“页面/注册”路由的注册表单进行注册。输入他们的凭据后,他们将被重定向到主页。然后,新登录的用户必须访问另一个页面(与“页面/登录”根相关联的页面),该页面包含登录表单,该表单负责他们的电子邮件/密码身份验证。此外,我还设置了 Realm 以向用户的电子邮件地址发送确认电子邮件。该电子邮件包含指向我的 NextJs 应用程序的自定义页面的链接,该页面将处理他们的确认(用户还必须在请求登录后得到确认)

应以此建立工作流程。但是,我想在他/她刚刚登录后自动登录用户(这样他们在创建帐户时就不需要登录并访问登录页面)。

我遇到的问题是我处理用户确认的 React 组件无权访问用户实例的电子邮件和密码。我需要一种登录用户的方法,而无需访问他/她的凭据。

下面,我将尝试准确解释为什么首先会发生这种访问限制。尽管整个 '_app.js' 包含在一些自定义提供程序中,但我会尽量保持简单,因此我将仅介绍本主题所需的内容。

我的 signup.js 文件如下所示:

import { useForm } from "react-hook-form";
// Used 'useForm' hook to simplify data extraction from the //input form
import { useAuth } from "members";

const SignUpForm = () => {
  const router = useRouter();
  const { handleSubmit, register } = useForm();
  const { signup } = useAuth();

  const signUpAndRedirect = (form) => {
  signup(form.email, form.password);
  router.push("/");
  // after signing up, redirect client back to home
  };

  return (
{/*My form's 'email' and 'password' fields are only accessible in the SignUpForm component*/}
    <div>
      <form onSubmit={handleSubmit(signUpAndRedirect)}>
        ...
       ...
      </form>
    </div>
  );
};

export default SignUpForm;

我的 login.js 文件是根据相同的概念构建的,唯一的区别是 'signUpAndRedirect' 被替换为 'authenticateAndRedirect':

const authenticateAndRedirect = (form) => {
    login(form.email, form.password);
    router.push("/");
  };

这是我的 confirm.js 文件,它负责从确认 URL 中提取令牌和 tokenId。该组件通常仅在客户端收到电子邮件并单击确认链接(基本上具有 /confirm 形式,其中每个令牌是一个字符串并由 Realm 添加到 URL 中)时才会呈现。

import Link from "next/link";
import { useEffect } from "react";
import { useRouter } from "next/router";
import { useAuth } from "members";

const Confirm = () => {
  const router = useRouter();
  const { confirm, login } = useAuth();
  useEffect(() => {
    const token = router.query.token;
    const tokenId = router.query.tokenId;

    if (token && tokenId) {
      confirm(token, tokenId);
      login(email, password); // !!! I don't have access to these
    }
  }, [router]);
//used useEffect() to assure the confirmation only happens once, after the component was rendered.

  return (
    <div>
      <h2>
        Thank you for confirming your email. Your profile was      successfully
        activated.
      </h2>
      <Link href="/">
        <a>Go back to home</a>
      </Link>
    </div>
  );
};

export default Confirm;

最后,快速浏览一下我可以通过我的定制提供商访问的注册、登录和确认方法。我非常肯定它们可以正常工作:

const client = () => {
  const { app, credentials } = useRealm();
    const [currentUser, setCurrentUser] = useState(app.currentUser || false);
  const [isAuthenticated, setIsAuthenticated] = useState(user ? true : false);

  
  // Login and logout using email/password.

  const login = async (email, password) => {
    try {
      const userCredentials = await credentials(email, password);
      await app.logIn(userCredentials);

      setCurrentUser(app.currentUser);
      setIsAuthenticated(true);
    } catch (e) {
      throw e;
    }
  };

  const logout = async () => {
    try {
      setUser(null);

      // Sign out from Realm and Auth0.
      await app.currentUser?.logOut();
      // Update the user object.
      setCurrentUser(app.currentUser);
      setIsAuthenticated(false);
      setUser(false);
    } catch (e) {
      throw e;
    }
  };

  const signup = async (email, password) => {
    try {
      await app.emailPasswordAuth.registerUser(email, password);
      // await app.emailPasswordAuth.resendConfirmation(email);
    } catch (e) {
      throw e;
    }
  };

  const confirm = async (token, tokenId) => {
    try {
      await app.emailPasswordAuth.confirmUser(token, tokenId);
    } catch (e) {
      throw e;
    }
  };

  return {
    currentUser,
    login,
    logout,
    signup,
    confirm,
  };
};

export default client;

currentUser 基本上代表 Realm.app.currentUser 并将由我的提供者提供给 _app。

所以,问题是我的 Confirm 组件无权访问电子邮件和密码字段。我尝试使用 useContext 钩子在兄弟组件之间传递数据,但很快就放弃了这种方法,因为我不想在整个 NextJS 页面中传递敏感数据(我应该使用密码的唯一地方是在MongoDB POST 请求,因为它被 Realm Web 加密)。

有什么办法可以解决这个问题吗?也许是完全不同的方法?

非常感谢您!任何帮助将不胜感激!

4

1 回答 1

1

如果您禁用用户电子邮件确认,您可能会在注册完成后调用登录功能:

registerAndLogin(email, password)
 .then(() => 
      loginAndRedirect(email, password)
      .then(() => router.push('/')
      .catch(err => throw err)
  )
 .catch(err => throw err)

我用你的帖子解决了我遇到的一个错误,顺便谢谢你。希望我的答案有效,我没有时间测试。

于 2021-04-08T16:10:38.197 回答