使用Passport进行社交身份验证后,如何重定向到正确的客户路由(反应,反应路由器,快递,护照) [英] How to redirect to correct client route after social auth with Passport (react, react-router, express, passport)

查看:61
本文介绍了使用Passport进行社交身份验证后,如何重定向到正确的客户路由(反应,反应路由器,快递,护照)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个React/Redux/React Router前端,Node/Express后端.我正在使用Passport(包括Facebook,Google和Github在内的各种策略)进行身份验证.

I have a React/Redux/React Router front end, Node/Express back end. I’m using Passport (various strategies including Facebook, Google and Github) for authentication.

我想发生的事情:

  1. 未经身份验证的用户尝试访问受保护的客户端路由 (类似于/posts/:postid,并重定向到/login. (反应路由器正在处理此部分)

  1. Unauthenticated user attempts to access protected client route (something like /posts/:postid, and is redirected to /login. (React Router is handling this part)

用户单击使用Facebook登录"按钮(或其他Social auth服务)

User clicks the ‘Log in with Facebook’ button (or other Social auth service)

发生了什么事

我发现使用React前端成功处理Passport社交身份验证的唯一方法是将使用Facebook登录"按钮包装在<a>标签中:

The only way I’ve found to successfully handle Passport social authentication with a React front end is to wrap the ‘Log in with Facebook’ button in an <a> tag:

<a href="http://localhost:8080/auth/facebook">Facebook Login</a>

如果我尝试将其作为API调用而不是链接进行处理,则会始终收到错误消息(此问题在此处进行了更详细的说明:

If I try to do it as an API call instead of a link I always get an error message (this issue is explained in a lot more detail here: Authentication with Passport + Facebook + Express + create-react-app + React-Router + proxy)

因此,用户单击链接,该链接将打入Express API,并成功通过Passport进行身份验证,然后Passport重定向到回调路由(http://localhost:8080/auth/facebook/callback).

So the user clicks the link, which hits the Express API, successfully authenticates with Passport, and then Passport redirects to the callback route (http://localhost:8080/auth/facebook/callback).

在回调函数中,我需要(1)将用户对象和令牌返回给客户端,并且(2)重定向到客户端路由-在重定向到/login之前,他们尝试访问的受保护路由. ,或某些默认路由,例如//dashboard.

In the callback function I need to (1) return the user object and token to the client, and (2) redirect to a client route — either the protected route they were trying to access before they got redirected to /login, or some default route like / or /dashboard.

但是由于Express中没有办法做这两项事情(我不能res.sendres.redirect,我必须选择一个),所以我一直在以一种感觉来处理它笨拙的方式: res.redirect(`${CLIENT_URL}/user/${userId}`)

But since there isn’t a way to do both of these things in Express (I can’t res.send AND res.redirect, I have to choose one), I’ve been handling it in what feels like kind of a clunky way: res.redirect(`${CLIENT_URL}/user/${userId}`)

这将在客户端上加载/user路由,然后将userId从路由参数中拉出,将其保存到Redux,然后对服务器进行ANOTHER调用以返回令牌,以将令牌保存到localStorage.

This loads the /user route on the client, and then I’m pulling the userId out of the route params, saving it to Redux, then making ANOTHER call to the server to return the token to save token to localStorage.

虽然感觉很笨拙,但一切正常,但是我无法弄清楚如何在提示登录之前如何重定向到用户尝试访问的受保护路由.

This is all working, although it feels clunky, but I can’t figure out how to redirect to the protected route the user was trying to access before being prompted to log in.

当用户尝试访问Redux时,我首先尝试保存到Redux的尝试路由,以为一旦身份验证后登陆到个人资料页面,我就可以使用该路由进行重定向.但是,由于Passport身份验证流程使用户离开现场进行3d方身份验证,然后在res.redirect上重新加载SPA,因此存储被销毁,并且重定向路径丢失了.

I first tried saving the attempted route to Redux when the user tries to access it, thinking I could use that to redirect once they land on the profile page after authentication. But since the Passport auth flow takes the user off-site for 3d-party authentication and then reloads the SPA on res.redirect, the store is destroyed and the redirect path is lost.

我最终解决的问题是将尝试的路由保存到localStorage,检查/user组件安装在前端时在localStorage中是否存在redirectUrl密钥,使用this.props.history.push(redirectUrl)重定向,然后清除localStorage中的redirectUrl键.这似乎是一个很肮脏的解决方法,必须有一种更好的方法来执行此操作.还有其他人想出如何进行这项工作的吗?

What I ended up settling on is saving the attempted route to localStorage, checking to see if there is a redirectUrl key in localStorage when the /user component mounts on the front end, redirecting with this.props.history.push(redirectUrl) and then clearing the redirectUrl key from localStorage. This seems like a really dirty workaround and there has got to be a better way to do this. Has anybody else figuree out how to make this work?

推荐答案

万一其他人都在为此苦苦挣扎,这就是我最后要解决的问题:

In case anybody else is struggling with this, this is what I ended up going with:

1.当用户尝试访问受保护的路由时,请使用React-Router重定向到/login.

1. When user tries to access protected route, redirect to /login with React-Router.

首先定义一个<PrivateRoute>组件:

// App.jsx

const PrivateRoute = ({ component: Component, loggedIn, ...rest }) => {
  return (
    <Route
      {...rest}
      render={props =>
        loggedIn === true ? (
          <Component {...rest} {...props} />
        ) : (
          <Redirect
            to={{ pathname: "/login", state: { from: props.location } }}
          />
        )
      }
    />
  );
};

然后将loggedIn属性传递到路线:

Then pass the loggedIn property to the route:

// App.jsx

<PrivateRoute
  loggedIn={this.props.appState.loggedIn}
  path="/poll/:id"
  component={ViewPoll}
/>

2.在/login组件中,将以前的路由保存到localStorage,以便稍后在身份验证后重定向到那里:

2. In /login component, save previous route to localStorage so I can later redirect back there after authentication:

// Login.jsx

  componentDidMount() {
   const { from } = this.props.location.state || { from: { pathname: "/" } };
   const pathname = from.pathname;
   window.localStorage.setItem("redirectUrl", pathname);
}

3.在SocialAuth回调中,重定向到客户端上的个人资料页面,添加userId和令牌作为路由参数

// auth.ctrl.js

exports.socialAuthCallback = (req, res) => {
  if (req.user.err) {
    res.status(401).json({
        success: false,
        message: `social auth failed: ${req.user.err}`,
        error: req.user.err
    })
  } else {
    if (req.user) {
      const user = req.user._doc;
      const userInfo = helpers.setUserInfo(user);
      const token = helpers.generateToken(userInfo);
      return res.redirect(`${CLIENT_URL}/user/${userObj._doc._id}/${token}`);
    } else {
      return res.redirect('/login');
    }
  }
};

4.在客户端的Profile组件中,拉出userId和令牌 超出路线参数,请立即使用删除它们 window.location.replaceState,并将它们保存到localStorage.然后在localStorage中检查redirectUrl.如果存在,请重定向,然后清除该值

4. In the Profile component on the client, pull the userId and token out of the route params, immediately remove them using window.location.replaceState, and save them to localStorage. Then check for a redirectUrl in localStorage. If it exists, redirect and then clear the value

// Profile.jsx

  componentWillMount() {
    let userId, token, authCallback;
    if (this.props.match.params.id) {
      userId = this.props.match.params.id;
      token = this.props.match.params.token;
      authCallback = true;

      // if logged in for first time through social auth,
      // need to save userId & token to local storage
      window.localStorage.setItem("userId", JSON.stringify(userId));
      window.localStorage.setItem("authToken", JSON.stringify(token));
      this.props.actions.setLoggedIn();
      this.props.actions.setSpinner("hide");

      // remove id & token from route params after saving to local storage
      window.history.replaceState(null, null, `${window.location.origin}/user`);
    } else {
      console.log("user id not in route params");

      // if userId is not in route params
      // look in redux store or local storage
      userId =
        this.props.profile.user._id ||
        JSON.parse(window.localStorage.getItem("userId"));
      if (window.localStorage.getItem("authToken")) {
        token = window.localStorage.getItem("authToken");
      } else {
        token = this.props.appState.authToken;
      }
    }

    // retrieve user profile & save to app state
    this.props.api.getProfile(token, userId).then(result => {
      if (result.type === "GET_PROFILE_SUCCESS") {
        this.props.actions.setLoggedIn();
        if (authCallback) {
          // if landing on profile page after social auth callback,
          // check for redirect url in local storage
          const redirect = window.localStorage.getItem("redirectUrl");
          if (redirect) {
            // redirect to originally requested page and then clear value
            // from local storage
            this.props.history.push(redirect);
            window.localStorage.setItem("redirectUrl", null);
          }
        }
      }
    });
  }

这篇博文有助于弄清问题.链接文章中的#4(推荐)解决方案要简单得多,并且可能在生产环境中可以正常工作,但是我无法在服务器和客户端具有不同基本URL的开发环境中使用它,因为将value设置为localStorage by在服务器URL呈现的页面在客户端URL的本地存储中将不存在

This blog post was helpful in figuring things out. The #4 (recommended) solution in the linked post is much simpler and would probably work fine in production, but I couldn't get it to work in development where the server and client have different base URLs, because a value set to localStorage by a page rendered at the server URL will not exist in local Storage for the client URL

这篇关于使用Passport进行社交身份验证后,如何重定向到正确的客户路由(反应,反应路由器,快递,护照)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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