什么authentication策略使用?

最近,我一直在阅读OAuth2,OpenID Connect等,但仍然很遗憾什么时候以及如何实现它。 我现在想使用NodeJS。

可以说我想创build一个博客服务。 该服务将公开API供客户使用。 “客户”包括pipe理员CMS。 我想这将是很好的解耦我的服务器和客户端(UI)。 我可以在不触摸服务器的情况下更改UI。 这些客户端可能会成为单页的Web应用程序。

好的第一个问题:在这个例子中,我应该使用OAuth2吗? 为什么? 仅仅因为我正在授权pipe理员应用程序访问博客?

自从SPA以来,我认为正确的策略是OAuth2 Implicit Flow?

对于每个应用程序,例如。 admin cms,我将不得不生成传递给auth服务器的AppID。 没有应用程序的秘密是正确的?

在这种情况下是否可以使用谷歌login(而不是用户名/密码)? 是否OpenID连接做到这一点?

我如何在NodeJS中实现所有这些? 我看到https://github.com/jaredhanson/oauth2orize ,但我不明白如何实现隐式stream。

我看到一个非官方的例子https://github.com/reneweb/oauth2orize_implicit_example/blob/master/app.js ,但我在想的是为什么会话需要? 我认为令牌的目标之一是服务器可以是无状态的?

我也想知道,我应该什么时候使用API​​密钥/秘密身份validation?

让我们来看看你的问题

  1. 我应该使用OAuth2吗? 为什么?

    答:那么今天旧的OpenId 2身份validation协议已被标记为过时(2014年11月),而OpenId Connect则是构build在OAuth2之上的身份层 ,所以真正的问题是如果您和您的企业知道并validation它是否重要你的用户身份(authentication部分)。 如果答案是“是”,那么去OpenId连接,否则你可以select任何一个,你觉得更舒服。

  2. 自从SPA以来,我认为正确的策略是OAuth2 Implicit Flow?

    答:不是。 您可以在使用SPA时实施任何策略,有些需要比别人更多的工作,很大程度上取决于您想要完成什么。 隐式stream是最简单的,但是由于访问令牌是直接发布的, 所以它不会对用户进行身份validation

    在隐式授权stream程中发出访问令牌时,授权服务器不会对客户端进行身份validation。 在某些情况下,可以通过用于将访问令牌传递给客户端的redirectURI来validation客户端身份。

    我不会推荐您的应用程序(或任何需要体面的安全级别1 )的stream程。

    如果你想保持简单,你应该用一个用户名和密码来使用资源所有者授权stream程,但是如果你想允许第三方应用程序使用你的服务(在我的意见是一个胜利的策略),它会比其他人更安全,因为它需要用户的明确同意。

  3. 对于每个应用程序,例如。 admin cms,我将不得不生成传递给auth服务器的AppID。 没有应用程序的秘密是正确的?

    答:是的,但无法使用基本身份validation时,可以使用client_secret向资源所有者stream中的令牌端点添加额外的安全层,但这不是其他任何stream程所要求的。 2 3

    授权服务器必须:

    • 要求对机密客户端或任何已颁发客户端证书(或其他身份validation要求)的客户端进行客户端身份validation,

    • 如果包含客户端authentication,则authentication客户端

    • 使用其现有的密码validationalgorithmvalidation资源所有者密码凭证。

    或者,授权服务器可以支持在请求主体中包括客户端证书(…)使用这两个参数在请求主体中包括客户端证书是不推荐的,并且应当限于不能直接利用HTTP基础的客户端authenticationscheme(或其他基于密码的HTTPauthenticationscheme)

  4. 在这种情况下是否可以使用谷歌login(而不是用户名/密码)? 是否OpenID连接做到这一点?

    答:可以,可以使用谷歌login,在这种情况下,您只是将身份validation和授权作业委托给Google服务器。 使用授权服务器的好处之一是可以让一次login访问其他资源,而无需为每个要访问的资源创build一个本地帐户。

  5. 我如何在NodeJS中实现所有这些?

    那么你从右脚开始。 使用oaut2horize是实现授权服务器颁发令牌的最简单方法。 我testing的所有其他库太复杂了,并且与节点和expression集成(免责声明:这只是我的观点)。 OAuthorize可以很好的与passport.js (来自同一个作者),这是一个强大的框架来执行authentication和授权超过300多个策略,如谷歌,脸谱,Github,等您可以很容易地集成谷歌使用Passport谷歌 (过时), 护照谷歌oauth和护照谷歌加 。

    我们来举个例子

    storage.js

    // An array to store our clients. You should likely store this in a // in-memory storage mechanism like Redis // you should generate one of this for any of your api consumers var clients = [ {id: 'as34sHWs34'} // can include additional info like: // client_secret or password // redirect uri from which client calls are expected to originate ]; // An array to store our tokens. Like the clients this should go in a memory storage var tokens = []; // Authorization codes storage. Those will be exchanged for tokens at the end of the flow. // Should be persisted in memory as well for fast access. var codes = []; module.exports = { clients: clients, tokens: tokens, codes: codes }; 

    oauth.js

     // Sample implementation of Authorization Code Grant var oauth2orize = require('oauth2orize'); var _ = require('lodash'); var storage = require('./storage'); // Create an authorization server var server = oauth2orize.createServer(); // multiple http request responses will be used in the authorization process // so we need to store the client_id in the session // to later restore it from storage using only the id server.serializeClient(function (client, done) { // return no error so the flow can continue and pass the client_id. return done(null, client.id); }); // here we restore from storage the client serialized in the session // to continue negotiation server.deserializeClient(function (id, done) { // return no error and pass a full client from the serialized client_id return done(null, _.find(clients, {id: id})); }); // this is the logic that will handle step A of oauth 2 flow // this function will be invoked when the client try to access the authorization endpoint server.grant(oauth2orize.grant.code(function (client, redirectURI, user, ares, done) { // you should generate this code any way you want but following the spec // http://tools.ietf.org/html/rfc6749#appendix-A.11 var generatedGrantCode = uid(16); // this is the data we store in memory to use in comparisons later in the flow var authCode = {code: generatedGrantCode, client_id: client.id, uri: redirectURI, user_id: user.id}; // store the code in memory for later retrieval codes.push(authCode); // and invoke the callback with the code to send it to the client // this is where step B of the oauth2 flow takes place. // to deny access invoke an error with done(error); // to grant access invoke with done(null, code); done(null, generatedGrantCode); })); // Step C is initiated by the user-agent(eg. the browser) // This is step D and E of the oauth2 flow // where we exchange a code for a token server.exchange(oauth2orize.exchange.code(function (client, code, redirectURI, done) { var authCode = _.find(codes, {code: code}); // if the code presented is not found return an error or false to deny access if (!authCode) { return done(false); } // if the client_id from the current request is not the same that the previous to obtain the code // return false to deny access if (client.id !== authCode.client_id) { return done(null, false); } // if the uris from step C and E are not the same deny access if (redirectURI !== authCode.uri) { return done(null, false); } // generate a new token var generatedTokenCode = uid(256); var token = {token: generatedTokenCode, user_id: authCode.user_id, client_id: authCode.client_id}; tokens.push(token); // end the flow in the server by returning a token to the client done(null, token); })); // Sample utility function to generate tokens and grant codes. // Taken from oauth2orize samples function uid(len) { function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } var buf = [] , chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' , charlen = chars.length; for (var i = 0; i < len; ++i) { buf.push(chars[getRandomInt(0, charlen - 1)]); } return buf.join(''); } module.exports = server; 

    app.js

     var express = require('express'); var passport = require('passport'); var AuthorizationError = require('oauth2orize').AuthorizationError; var login = require('connect-ensure-login'); var storage = require('./storage'); var _ = require('lodash'); app = express(); var server = require('./oauthserver'); // ... all the standard express configuration app.use(express.session({ secret: 'secret code' })); app.use(passport.initialize()); app.use(passport.session()); app.get('/oauth/authorize', login.ensureLoggedIn(), server.authorization(function(clientID, redirectURI, done) { var client = _.find(storage.clients, {id: clientID}); if (client) { return done(null, client, redirectURI); } else { return done(new AuthorizationError('Access denied')); } }), function(req, res){ res.render('dialog', { transactionID: req.oauth2.transactionID, user: req.user, client: req.oauth2.client }); }); app.post('/oauth/authorize/decision', login.ensureLoggedIn(), server.decision() ); app.post('/oauth/token', passport.authenticate(['basic', 'oauth2-client-password'], { session: false }), server.token(), server.errorHandler() ); 
  6. (…),但我在想的是为什么会议需要? 我认为令牌的目标之一是服务器可以是无状态的?

    当客户端将用户redirect到用户授权端点时,将启动授权事务。 要完成交易,用户必须authentication和批准授权请求。 因为这可能涉及多个HTTP请求/响应交换,交易存储在会话中。

    那么是的,但会话用于令牌协商过程。 稍后,您将强制授权在授权标头中发送令牌,以使用获得的令牌授权每个请求。

根据我的经验,OAuth2是保护API的标准方式。 我build议使用OpenID Connect,因为它会将authentication添加到OAuth2的基于授权的规范中。 您也可以在您的“客户”之间获得单点login。

自从SPA以来,我认为正确的策略是OAuth2 Implicit Flow?

解耦您的客户端和服务器是一个很好的概念(我通常也会这样做),但是,我build议使用授权代码stream,因为它不会将令牌暴露给浏览器。 阅读http://alexbilbie.com/2014/11/oauth-and-javascript/ 。 使用瘦服务器端代理来添加令牌到请求。 不过,我通常避免使用客户端上的任何服务器生成的代码(如java中的JSP或者rails中的erb / haml),因为它将客户端连接到服务器太多了。

对于每个应用程序,例如。 admin cms,我将不得不生成传递给auth服务器的AppID。 没有应用程序的秘密是正确的?

您将需要隐式stream的客户端ID。 如果您使用授权码stream(推荐),您将需要一个ID和秘密,但秘密将保存在瘦服务器端代理,而不是客户端唯一的应用程序(因为它不能保密案件)

在这种情况下是否可以使用谷歌login(而不是用户名/密码)? 是否OpenID连接做到这一点?

是。 Google使用openid连接

我如何在NodeJS中实现所有这些? 我看到https://github.com/jaredhanson/oauth2orize ,但我不明白如何实现隐式stream。

关于openid连接的一个好处是(如果你使用另一个提供者,如谷歌),你不必自己实现提供者,你只需要编写客户端代码(和/或利用客户端库)。 有关不同的authentication实现,请参阅http://openid.net/developers/libraries/ 。 有关nodejs,请参阅https://www.npmjs.com/package/passport-openidconnect