passport.js ,在弹出窗口中进行身份验证后,将其关闭并重定向父窗口

Posted

技术标签:

【中文标题】passport.js ,在弹出窗口中进行身份验证后,将其关闭并重定向父窗口【英文标题】:passport.js , after authentication in popup window, close it and redirect the parent window 【发布时间】:2015-04-08 04:00:12 【问题描述】:

在我的 node.js express 应用程序中,我使用 passport.js 集成了 Facebook 身份验证。我可以在弹出窗口中打开 Facebook 登录页面,然后登录。在这个阶段,重定向发生在弹出窗口中。但我需要关闭弹出窗口并在父窗口中进行重定向(即父窗口将重定向)。我将在此处粘贴完整代码虽然我认为主要问题在于模板文件(.ejs)。

我在 SO 中的类似问题中找不到任何解决方案。

// Passport session setup.
passport.serializeUser(function(user, done) 
  done(null, user);
);

passport.deserializeUser(function(obj, done) 
  done(null, obj);
);


// Use the FacebookStrategy within Passport.
passport.use(new FacebookStrategy(
    clientID: config.facebook_api_key,
    clientSecret:config.facebook_api_secret ,
    callbackURL: config.callback_url
  ,

  function(accessToken, refreshToken, profile, done) 

    console.log(" id = "+profile.id);

    process.nextTick(function () 
      //Check whether the User exists or not using profile.id
      //Further DB code.
      profile.user_id="00000444000555";
      return done(null, profile);
    );

  
));

app.set('views', __dirname + '/views');

app.set('view engine', 'ejs');
//app.set('view engine', 'jade');
app.use(cookieParser());
app.use(bodyParser.urlencoded( extended: false ));
app.use(session( secret: 'keyboard cat', key: 'sid'));
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname + '/public'));

//Router code
app.get('/', function(req, res)
  res.render('index',  user: req.user );
);

app.get('/account', ensureAuthenticated, function(req, res)

    console.log(req.user);
  res.render('account',  user: req.user );
);

//Passport Router
app.get('/auth/facebook', passport.authenticate('facebook'));
app.get('/auth/facebook/callback',

passport.authenticate('facebook', 
       successRedirect : '/',
       failureRedirect: '/login'
  ),

   function(req, res) 
    res.redirect('/');

  );

app.get('/logout', function(req, res)
  req.logout();
   res.redirect('/');
);

function ensureAuthenticated(req, res, next) 
     if (req.isAuthenticated())  return next(); 
  res.redirect('/login')

app.listen(8000);

模板文件是:

<script type="text/javascript">
    if (window.location.hash && window.location.hash == '#_=_') 
        if (window.history && history.pushState) 
            window.history.pushState("", document.title, window.location.pathname);
         else 
            // Prevent scrolling by storing the page's current scroll offset
            var scroll = 
                top: document.body.scrollTop,
                left: document.body.scrollLeft
            ;
            window.location.hash = '';
            // Restore the scroll offset, should be flicker free
            document.body.scrollTop = scroll.top;
            document.body.scrollLeft = scroll.left;
        
    
</script>

<% if (!user)  %>

  <div style="width:500px;height:180px;">
    <h2 style="font-size:40px;">Welcome! Please log in.</h2>
    <a href="javascript:void(0)" onclick=" window.open('/auth/facebook','',' scrollbars=yes,menubar=no,width=500, resizable=yes,toolbar=no,location=no,status=no')"><img src="fb-login.jpg"  ></a>
    </div>

<%  else  %>

<script type="text/javascript">

    window.parent.location.href ="/";
    window.close();
</script>
    <h2>Hello, <%= user.displayName %>.</h2>
<%  %>

【问题讨论】:

也许这有帮助? webdeveasy.com/single-page-application-authentication不是同一个栈,不过你可以借鉴他们的想法 @kane 非常准确,但他们使用 angular 将 msg 发送回父窗口...我现在想知道如何在没有 angular 的情况下做到这一点...似乎打开了一个套接字和向自己发送消息可以工作,但看起来有点矫枉过正 【参考方案1】:

最简单的解决方案是这样的:

res.send('<script>window.close()</script>');

【讨论】:

哈哈哈好吧我很高兴你滚动到最底部¯\_(ツ)_/¯ 哦,简洁优雅。【参考方案2】:

我们自己也为此苦苦挣扎,最终编写了一个简单的 javascript 插件来帮助我们解决这个问题https://github.com/enhancv/popup-tools

在客户端使用它会这样:

<button id="button">Facebook Login</button>
<script>
document.getElementById("button").onclick = function () 
    popupTools.popup('/popup-url', "Facebook Connect", , function (err, user) 
        if (err) 
            alert(err.message);
         else 
            // save the returned user in localStorage/cookie
        
    )
</script>

在服务器上:

passport.use(new FacebookStrategy(
    clientID: process.env.FACEBOOK_ID,
    clientSecret: process.env.FACEBOOK_SECRET,
    callbackURL: '/calback-url',
, function (accessToken, refreshToken, profile, done) 
    // process facebook profile into user
));

app.post('/popup-url', passport.authenticate('facebook'))
app.get('/callback-url', 
    passport.authenticate('facebook'),
    function (req, res) 
        res.end(popupTools.popupResponse(req.user))
    
);

希望人们会发现这很有用。

【讨论】:

超级!无忧无虑。只是一个快速的问题。它适用于所有浏览器吗? 如果我想在弹出窗口中显示 fb 和 google 等选项并发送请求怎么办。我试过了,但响应返回错误【参考方案3】:

我上面的评论可能没有完整的答案,所以这是我在我的网站上所做的完整解决方案。

在我的节点后端,我创建了一个路由,该路由基本上会在成功认证后路由到一个名为 after-auth.html 的特殊页面

app.get('/auth/twitter/callback',
  auth.passport.authenticate('twitter',  failureRedirect: '/failedLogin.html' ),
  function(req, res) 
    res.redirect('/after-auth.html');
);

我的 after-auth.html 页面仅包含 javascript,可将焦点放回父页面并像这样自行关闭

<script type="text/javascript">
    if (window.opener) 
        window.opener.focus();

      if(window.opener.loginCallBack) 
        window.opener.loginCallBack();
      
    
    window.close();
</script>  

您可能还注意到我调用了一个名为 loginCallBack 的函数。我将其用作挂钩,以便父浏览器知道在成功验证后更新其模型/视图。例如,在我的 loginCallBack 中,我检查用户是否已登录并删除所有“登录”按钮并将其替换为用户的个人资料图片 例如

  var loginCallBack = function() 
    loginCntr.checkLogin();
  

【讨论】:

是的,我昨天在发表评论后才发现,我可以从模态窗口访问父级,现在我正在实现与您在此处描述的相同的事情,但据我所知,有一个由于某些 IE 策略,IE 有时会告诉 windows.opener 未定义,但 IE 支持 Chrome 已弃用的 showModalDialog 但可以替代 IE 的 window.open()。并感谢您的回答)

以上是关于passport.js ,在弹出窗口中进行身份验证后,将其关闭并重定向父窗口的主要内容,如果未能解决你的问题,请参考以下文章

如何在弹出窗口中通过 OAuth 2.0 向 Google 进行身份验证?

在弹出窗口中打开 Google SignIn 用户身份验证,需要使用 OAuth 2.0 在同一选项卡本身中打开

Sails.js + Passport.js 通过 websockets 进行身份验证

使用 Passport.js、express-session 和 express-mysql-session 进行用户身份验证

在带有 React.js 前端的 Node.js 上使用 Passport.js 进行身份验证

使用 Everyauth/Passport.js 向 Twitter 进行身份验证,同时询问用户名/电子邮件/密码