使用 passport.js 和 express.js (node.js) 制作安全的 oauth API

Posted

技术标签:

【中文标题】使用 passport.js 和 express.js (node.js) 制作安全的 oauth API【英文标题】:Make a secure oauth API with passport.js and express.js (node.js) 【发布时间】:2012-12-05 17:15:06 【问题描述】:

我想到了一个具体的问题,除非我没有以正确的方式做事。

我有一个包含 2 个面的应用程序,一个客户端(html 站点)和一个 API(使用 express + mongodb 构建)。 API 需要被安全访问。他们都在不同的域上,现在假设我的网站在 domain.com 上,我的 API 在 api.com:3000 上。

当我使用他们的数据创建用户时,我添加了护照以从 Github 获取访问令牌,因此我基本上可以“使用 Github 登录”。

我目前的流程是:

1) 客户端(站点),在 API 上打开一个弹出窗口

window.open("http://api.com:3000/oauth")

2)快递服务器,启动护照进程:

app.get('/oauth', passport.authenticate('github'), function(req, res) 

);

对于策略,我使用了this code。

3) 我将回调重定向到关闭弹出窗口的 url(带有 window.close() 的 javascript):

app.get('/oauth/callback', passport.authenticate('github',  failureRedirect: '/' ), function(req, res) 
    res.redirect('/auth_close');
);

此时,一切都很好,我现在已登录 API。我的问题是如何将数据返回给客户端,因为客户端对 accessToken、用户或用户 ID 一无所知。

所以,从客户端(打开弹出窗口的网站),我尝试了不同的方法:

从弹出窗口中获取值:由于重定向不起作用,我忘记了弹出窗口的 javascript 信息

调用我的API获取会话中的“当前用户”,如http://api.com:3000/current 还有一个要求,并不理想,但这一项工作。但是,我还是有问题。

如果我从浏览器访问此 /current url,它会返回用户,因为浏览器会在标头请求中发送快速会话 cookie:

Cookie:connect.sid=s%3AmDrM5MRA34UuNZqiL%2BV7TMv6.Paw6O%2BtnxQRo0vYNKrher026CIAnNsHn4OJdptb8KqE

问题是我需要从 jquery 或类似的请求中发出这个请求,这就是它失败的地方,因为没有发送会话。所以用户没有返回:

app.get('/current', function() 
    // PROBLEM HERE, req.user is undefined with ajax calls because of the session!
);

我找到了一种让它工作的方法,但我不满意,因为我会遇到跨浏览器的 CORS 问题,这是为了表达:

res.header("Access-Control-Allow-Credentials", "true");

并在 jquery ajax 调用中添加 withCredentials 字段,如 here 所述。

要在本机 XHR 对象上设置的 fieldName-fieldValue 对的映射。例如,如果需要,您可以使用它为跨域请求设置 withCredentials 为 true。

$.ajax(
   url: a_cross_domain_url,
   xhrFields: 
      withCredentials: true
   
);

我对此也不满意,因为我丢失了标题上的通配符:Access-Control-Allow-Origin,我需要指定一个域,如 here 所述。

所以,我不确定我应该在这里采取的方法,我唯一需要的是客户端从护照 oauth 过程中获取 accessToken 或用户 ID。这个想法是在每次调用 API 时使用该令牌来验证调用。

有什么线索吗?

【问题讨论】:

你找到什么了吗?我也有同样的问题:( 您可以看到我的this 对此的回答。它对我有用。 【参考方案1】:

我有同样的问题。我在登录页面上使用了本地策略,然后检查用户是否在其他请求的会话中。

正如您所说,解决方案是使用 CORS 以便使用 XMLHTTPRequest 在 cookie 中传递会话 ID。

我决定在其他请求上使用访问令牌,而不是使用尚未在所有浏览器上工作的 CORS。我使用的工作流程如下:

POST /login
用户名和密码在正文中传递。 使用本地策略进行身份验证。 响应返回用户对象,包括 access_token

GET /endpoint/abc123?access_token=abcdefg

从登录响应中获取的令牌 使用承载策略进行身份验证(在 passport-http-bearer 中)

Express 现在不需要会话。

我希望这个替代方案有所帮助。

【讨论】:

我也带着令牌去了。但是,我使用了带有授权标头的承载身份验证策略。然后使用 Passport HTTP Bearer 使用 Express:github.com/jaredhanson/passport-http-bearer 关于令牌生成,拥有一个永不过期的令牌是否安全?我必须使令牌过期并生成新令牌吗? 我想到的令牌就像我想到的密码 - 有些网站会自动使它们过期(例如 OAuth),而其他网站则不会(例如 Amazon Web Services)。如果您将令牌视为密码 - 永远不要通过不安全的 HTTP 传递它,在您的数据库中加密它们,允许用户手动重新生成等,那么您可能不需要自动过期。不同的人对安全性和可用性有不同的门槛。 如何保证token不能被拦截和重放?如果您不能这样做,则必须相当快地使令牌过期。【参考方案2】:

在快速应用中配置任何内容之前,使用以下(完全相同)设置跨域响应的标头:

app.use(function(req, res, next) 
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) 
     res.send(200);
  else 
     next();
 
);

【讨论】:

你能逐行解释sintax的含义吗?如果我不使用它怎么办?

以上是关于使用 passport.js 和 express.js (node.js) 制作安全的 oauth API的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Passport.js 和 Express 4 访问 req.user

使用 passport.js 和 express.js (node.js) 制作安全的 oauth API

Express Sequelize 和 Passport.js 身份验证策略不起作用

Express + Passport.js:req.user 未定义

使用 Passport.js 的 Vue.js 客户端和 Express.js 服务器的完整 Oauth2 身份验证流程

logout() 在使用 passport.js、express.js、AngularJs 时不起作用