我正在编写一个应用程序,前端在emberjs中,后端/服务器端在nodejs服务器中。我已经配置了emberjs,以便用户可以登录/注册第三方Oauth(google,twitter,Facebook)。我有一个后端在express nodejs服务器中编写,该服务器托管RESTful API。
我没有连接到emberjs的数据库,而且我认为我也不应该这样做,因为它是严格的客户端代码。我计划使用JWT在客户端和服务器端之间进行通信。当用户使用他们的oauth cred登录时,我会从提供者那里得到一个JSON对象,其中包含uid、name、login、access_token和其他详细信息。
我正在为如何处理用户注册选择策略而挣扎,因为它是OAuth,所以没有注册过程,所以流程是如果用户不在我的数据库中,我不支持电子邮件/密码身份验证。当用户第一次通过OAuth提供商登录时,流程是什么?emberjs是否应该在每次登录时将所有详细信息发送到后端,以便后端可以将新用户添加到数据库?
我的JWT主体应该包含什么?我在考虑uid和提供者提供的访问令牌。我在这里可以想到的一个问题是提供者特定的访问令牌可以更改。用户可以从提供者的站点撤销令牌,并重新注册emberjs。
我愿意在任何其他javascript客户端框架中编写前端,如果这样做更容易的话。
3条答案
按热度按时间sigwle7e1#
如果我们讨论的不仅仅是工作,而且是安全的无状态身份验证,您将需要考虑使用
access
和refresh
令牌的适当策略。1.访问令牌是提供对受保护资源的访问的令牌。*
Expiration
* 此处可能在大约1小时内安装(取决于您的考虑)。1.Refresh token是一个特殊的token,应该用来产生额外的
access token
,以防它过期或使用者工作阶段已更新。显然,您需要让它的寿命更长(与access token
相比),并尽可能地安全。*Expiration
* 可能会在约10天或更长的时间内安装(也取决于您的考量)。**仅供参考:**由于
refresh tokens
是长期存在的,为了确保它们的安全性,您可能希望将它们存储在数据库中(刷新令牌请求很少执行)。这样,假设,即使您的刷新令牌被黑客攻击,有人重新生成了access/refresh
令牌,当然您将失去权限,但您仍然可以登录到系统,因为您知道login/pass(以防稍后使用)或通过任何社交网络登录。在何处存储这些令牌?
基本上有2个常见的地方:
1.HTML5 Web存储(本地存储/会话存储)
很好,但同时也有足够的风险。存储可以通过同一个域上的javascript代码访问。这意味着如果你有XSS,你的令牌可能会被黑客攻击。所以选择这种方法你必须小心编码/转义所有不可信的数据。即使你这样做了,我敢肯定,您使用一些第三方客户端模块,并没有保证他们中的任何一个有一些恶意代码。
此外,
Web Storage
在传输过程中不强制执行任何安全标准,因此您需要确保JWT是通过HTTPS
发送的,而不是通过HTTP
发送的。1.小甜饼
使用特定的
HttpOnly
选项,cookie无法通过javascript访问,并且不受XSS的影响。您也可以设置Secure
cookie标志,以保证cookie仅通过HTTPS发送。但是,cookie容易受到其他类型的攻击:跨站请求伪造(CSRF)。在这种情况下,可以通过使用某种同步令牌模式来防止CSRF
。在AngularJS
中的Security Considerations部分中有很好的实现。一个你可能想追随的article。
为了说明它的一般工作原理:
关于JWT本身的几句话:
为了让大家明白,Auth 0的人真的很酷JWT Debugger。有2种(有时是3种)常见的声明类型:
public
、private
(和保留的)。JWT
主体的一个例子(有效负载,可以是任何你想要的):有关
JWT
结构的更多信息,您可以找到here。1tu0hz3e2#
回答您提出的两个具体问题:
当用户第一次登录OAuth提供商时,会有什么样的流程?emberjs是否应该在每次登录时将所有详细信息发送到后端,以便后端可以向数据库添加新用户?
每当用户通过oauth注册或登录并且您的客户端收到新的访问令牌时,我会“上插”(更新或插入)到用户表中(或集合)沿着您从oauth提供程序API中检索到的关于用户的任何新的或更新的信息。我建议将其直接存储在每个用户记录中以确保访问令牌和关联的配置文件信息原子地更改。一般来说,我通常会将其组合到某种中间件中,当出现新令牌时,该中间件会自动执行这些步骤。
我的JWT主体应该包含什么?我在考虑uid和提供者提供的访问令牌。我在这里可以想到的一个问题是提供者特定的访问令牌可以更改。用户可以从提供者的站点撤销令牌,并重新注册emberjs。
JWT主体通常由 *users claimes * 组成。我个人认为将提供者访问令牌存储在JWT令牌的主体中没有什么好处,因为它对您的客户机应用程序没有什么好处(除非您要从客户机对其API执行大量直接API调用,我更喜欢在服务器端执行这些调用并向我的应用程序客户机发回一组符合我自己的接口的规范化声明)。您不必从客户端应用程序中解决多个提供商之间存在的各种差异。例如,将在API中命名不同的Twitter和Facebook特定字段合并为存储在用户配置文件表中的公共字段,然后将本地配置文件字段作为声明嵌入到JWT主体中,以便由客户机应用程序解释。这样做还有一个额外的好处,即您将不会在未加密的JWT标记中持久保存将来可能泄漏的任何数据。
无论您是否将oauth提供程序提供的访问标记存储在JWT标记主体中,您都需要在每次概要文件数据更改时授予一个新的JWT标记(如果没有发生概要文件更新,并且以前的标记仍然有效,则您可以加入一种机制,以绕过发出新的JWT标记)。
除了在JWT标记主体中存储为声明的配置文件字段之外,我还将始终定义标准的JWT标记主体字段:
gfttwv5a3#
对于任何OAuth工作流,您都应该使用passportjs库。您还应该阅读完整的文档。它很容易理解,但我犯了一个错误,第一次没有阅读完整的东西,并挣扎。它包含OAuth身份验证与超过300个提供商和发行令牌。
不过,如果您想手动执行或希望获得基本的理解,我将使用以下流程:
1.前端有一个登录页面,列出了使用Google/Facebook等进行登录的情况,其中实现了OAuth。
1.成功的OAuth将生成uid、登录名、访问令牌等(JSON对象)
1.在Node.js应用程序中将JSON对象POST到
/login/
路由中。(是的,不管是新用户还是现有用户,都发送整个响应。在这里发送额外的数据比发送两个请求要好)1.后端应用程序读取
uid
和access_token
。通过以下方式确保access_token
有效(https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken)或使用访问令牌从提供程序请求用户数据。(如果访问令牌无效,则此操作将失败,因为OAuth访问令牌是基于每个应用/开发者生成的)现在,搜索后端数据库。1.如果数据库中存在
uid
,则在DB中更新用户的access_token和expiresIn(access_token允许您从Facebook获取该特定用户的更多信息,并且它通常提供几个小时的访问权限)。1.否则,您将创建一个包含uid、login等信息的新用户。
1.更新access_token或创建新用户后,发送包含
uid
的JWT标记。(用secret对jwt进行编码,这样可以确保它是由您发送的,并且没有被篡改。 checkout https://github.com/auth0/express-jwt)1.在前端,用户从
/login
收到jwt后,通过sessionStorage.setItem('jwt', token);
将其保存到sessionStorage
1.在前端上,还添加以下内容:
if ($window.sessionStorage.token) { xhr.setRequestHeader("Authorization", $window.sessionStorage.token); }
这将确保如果存在JWT令牌,则它将随每个请求一起发送。
1.在Node.js app.js文件上,添加
app.use(jwt({ secret: 'shhhhhhared-secret'}).unless({path: ['/login']}));
个这将验证jwt路径中的任何内容,确保用户已登录,否则不允许访问并重定向到登录页面。这里的例外情况是
/login
,因为在这里您为新用户或未经身份验证的用户提供了JWT。您可以在Github URL上找到更多关于如何获取令牌以及您当前正在处理哪个用户的请求的信息。