想象一下,我正在api .example.com 上实现一个 API 服务,该服务需要使用 OAuth2 以足够的粒度来保护其端点,以适应不同的用户类型。
- 针对 Facebook 进行身份验证的用户只能访问 https:// api .example.com/facebook
- 对Twitter进行身份验证的用户只能访问 https://api.example.com/twitter
- 针对授权服务进行本地身份验证的用户可以访问 facebook 和 twitter 端点
这意味着我们有以下角色:
- 身份验证提供程序(授权服务器、Facebook、Twitter)
- 授权提供程序(我的 OAuth2 服务器)(auth .example.com)
- 资源提供者(我的 API 服务器)(api .example.com)
- ExpressJS webapp 或 SPA ( www .example.com) 或本机应用程序
问题:
Q1应用流程应该如何实现?
我的想法是...
webapp 必须在 session 中将 accessToken 存储到api .example.com,并将 sessionid 发送到 Web 浏览器。如果没有accessToken,则必须将浏览器重定向到auth.example.com/login/selectprovider/ ,提供client_id=" www.example.com "和client_secret ,redirect_url="https://www.example.com /”。
在 https:// auth .example.com/login/selectprovider/ 上,用户选择 facebook 或 twitter 或本地,并且auth .example.com 使用 client_id 和 client_secret 将用户(再次)重定向到 /login 或 facebook 或 twitter 和redirect_url=https:// auth .example.com/ twitter或 https:// auth .example.com/ twitter以轻松区分三者。
Facebook/twitter/local 方法将对用户进行身份验证并将浏览器重定向回auth .example.com。
然后auth .example.com 将为api .example.com 生成一个(承载)accessToken 并将其与 user.id 一起存储到本地数据库表中,并具有关联的范围(“facebook”、“twitter”或“local”) ,将密钥(userId)放入浏览器会话并重定向到https://www.example.com/
现在,当用户单击 WebApp 中的链接时,www .example.com 会在api .example.com上执行 GET 或 POST ,提供 client_id="www.example.com" client_secret 和 access_token。
api .example.com 端点验证 accessToken,如果没问题,则执行 API 并将值返回给www .example.com,然后将其呈现给用户的浏览器。
Q2以上是正确的流程,还是有更好的方法?如果正确,api .example.com 如何在其端点验证 accessToken?
auth .example.com是否应该为此公开一个只能使用 client_id=" api .example.com" 和 client_secret 访问的特殊端点(例如 /userinfo)?每次调用似乎都很昂贵,但是api .example.com还能如何信任令牌的有效性(因为它已过期或用户已注销)?
什么是符合 OAuth2 标准的方式?
Q3在护照/OAuth2orize 示例中,我看到了验证身份验证的 3 种方式:
- if (req.user) {...};
- if (req.isAuthenticated()) {...};
- passport.authenticate('bearer', {session: false);
Q4 passport.authenticate('facebook') 到底是做什么的?
该信息在策略中提供。
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ facebookId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
所以这包含了client_id 和client_secret。这个什么时候用?每次调用passport.authenticate('facebook')?是否将整个重定向到 facebook 登录页面,获得授权代码授予并将其交换为 accessToken,以某种方式在内部存储 accessToken 并保留它直到注销?
这似乎不太可能,但我看不出它如何在没有这一切的情况下实际验证用户。而且每次 API 调用都太昂贵了,所以我相信它会更有效地工作。但我不知道如何和研究源代码并没有让它更清楚。
Q5 *api**.example.com 如何知道每个 API 请求中www .example.com 所包含的 Bearer accessToken 没有伪造或过期?
我认为它必须根据auth .example.com 服务对其进行检查,而该服务又必须检查 facebook/twitter 会话是否仍然有效,并在那一刻使用 refreshToken 刷新令牌?