惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
Jina AI
Jina AI
Stack Overflow Blog
Stack Overflow Blog
量子位
博客园_首页
Vercel News
Vercel News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Forbes - Security
Forbes - Security
IT之家
IT之家
N
News and Events Feed by Topic
S
Security Affairs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Webroot Blog
Webroot Blog
Recorded Future
Recorded Future
L
LangChain Blog
Y
Y Combinator Blog
AI
AI
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google Online Security Blog
Google Online Security Blog
V2EX - 技术
V2EX - 技术
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
PCI Perspectives
PCI Perspectives
I
Intezer
T
Tenable Blog
G
Google Developers Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Troy Hunt's Blog
L
LINUX DO - 最新话题
云风的 BLOG
云风的 BLOG
C
CXSECURITY Database RSS Feed - CXSecurity.com
有赞技术团队
有赞技术团队
O
OpenAI News
P
Proofpoint News Feed
TaoSecurity Blog
TaoSecurity Blog
C
Check Point Blog
Last Week in AI
Last Week in AI
S
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
Blog — PlanetScale
Blog — PlanetScale

博客园 - 莫耶

学习RxJS:Cycle.js 学习RxJS: 导入 [Node.js] Node.js项目的持续集成 [Node.js] 使用node-forge保障Javascript应用的传输安全 [Node.js] 对称加密、公钥加密和RSA [Node.js] DSL in action [Node.js] 使用TypeScript编写Node项目 [Node.js] Node.js中的流 [Node.js] 基于Socket.IO 的私聊 [Node.js] 闭包和高阶函数 [Node.js] Promise,Q及Async [Node.js] Express的测试覆盖率 [Node.js] BDD和Mocha框架 [Node.js] 也说this [Node.js] ECMAScript 6中的生成器及koa小析 [Node.js] 使用File API 异步上传文件 [Node.js] Node + Redis 实现分布式Session方案 [Node.js] Cluster,把多核用起来 Python开源框架Scrapy安装及使用
[Node.js] OAuth 2 和 passport框架
莫耶 · 2014-10-01 · via 博客园 - 莫耶

原文地址:http://www.moye.me/?p=592

OAuth是什么

OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth 2.0  OAuth 2

OAuth的版本有v1.0, v1.0a 和 v2.0。OAuth 2.0 的出现主要是解决1.0+中的几个问题,提升开发简易度和应用安全性:

  • 更好的支持非浏览器APP(移动和桌面客户端,可以省略v1.0的交换token过程,增强用户体验)
  • 给访问令牌(Access-Token)添加了续期概念,对令牌泄露多了一层防御
  • 不再强制客户端使用Token Secret,有令牌就够了
  • 引入Bearer 验证机制

OAuth 2 是目前被广泛支持的版本,但不向下兼容1.0。

术语

简单说,OAuth 2涉及到三方

  1. Client APP:要访问用户的程序
  2. Resource Owner :用户,由他来给APP授权
  3. Authorization Server:也称为 API Provider(Google/Facebook/Twitter…)
    1. client_id 和 client_secret:Server颁发给 APP 的身份凭证
    2. Access Token:Server颁发的令牌,访问资源API需要带着它
    3. Scope:APP 向Server 提供需要调用的API种类
    4. Redirect URL:APP 向 Server提供的回调地址

OAuth 2 交互模型

OAuth 2的 交互模型比较灵活,主要的交互模型分为如下几种:

  • 服务端Authorization code授权:被 Web服务端编程广泛采用,也是本文重点介绍的模型
  • 客户端隐式授权:Web客户端编程(JavaScript APP)使用这种方式交互,在用户授权后直接取到令牌
  • Resource Owner登录授权:需要用户输入用户名和密码来交换令牌
  • 客户端凭证:这种模型下,APP 可能就是Owner,代表自己进行API调用

准备工作

首先,开发人员需要去API Provider处,为APP 创建应用信息,申请权限,以Google为例:

  1. 在 Google Developers Console 创建项目
  2. 在 APIs & Auth -> APIs 中,选择打开需要调用的API
  3. 在 APIs & Auth -> Credentials 中,填写 Redirect URIS(OAuth 2 回调的地址),创建Client ID。成功后,会得到需要的 CLIENT ID 和 CLIENT SECRET,保存好不要泄露 :)
  4. 在 APIs & Auth -> Consent screen 中,填写 Email/ Product Name/ Homepage/ Logo等 APP元信息,这些是给用户看的,在交互过程中会出现在确认授权页

服务端交互流程

OAuth 2 的服务端交互流程,是一个对用户透明的三方过程:

Server-side Web Application flow: Step-by-step

如果用Node.js 实现前述的 Google API OAuth 2访问,编程模型大概如此:

  1. 判断是否已有 access_token,过期了吗?如果不存在或过期,一步步来:
  2. 将用户页面跳向到https://accounts.google.com/o/oauth2/auth(附上一系列Query参数:response_type/ client_id/ redirect_uri/ scope,视需要追加参数:access_type/ approval_prompt/ state…)
  3. 为 redirect_uri 提供 HTTP GET 方法的处理,以响应回调
  4. 第3步的回调会收到一个code参数,用它向 https://accounts.google.com/o/oauth2/token 发起一个 POST请求,这次需要提供的参数:code / client_id/ client_secret/ redirect_uri/ grant_type
  5. 为 第4步 的redirect_uri 提供HTTP GET 方法的处理,以响应回调
  6. 第5步的回调会收到一个 JSON对象,里面有access_token 和它的过期时间expires_in,将它存下来
  7. 访问API,附上得到的 access_token

引入Passport

显然,自己实现OAuth 流程将需要写不少东西,且每增加一个API Provider,这个过程需要再来一次。这行里有句黑话:写得越多,错得就越多 :)  这时候,应该找个合适的框架

Passport 框架就是为解决类似问题而生的:它以中间件的形式为Node 程序提供身份认证,框架本身将一般形式的认证过程(Basic & Digest/ OAuth/ Open ID)、回调及错误处理进行了封装,而将具体的认证实现抽象为Strategy(策略),与框架本身并无关系,只要是符合Passport 的Strategy都能以插件的形式加入项目被Passport使用。比如基于Google的OAuth 2认证,我们可以用 passport-google-oauth,基于Facebook的OAuth 2认证我们可以用passport-facebook,当然也可以用别的或者自己写。 这种基于策略的抽象大大简化了编程模型,所以Passport 有自信称 " Simple, unobtrusive authentication for Node.js",诚不欺我。

Passport 实现 Google 用户登录

Google 作为具体的 API Provider,用Passport 对其进行OAuth 2访问,需要一个 Strategy 来提供它的交互流程实现,本例中使用 passport-google-oauth

在package.json中确定引用,并用 npm install 安装模块:

"dependencies": {
        //... more libraries
        "passport": "*",
        "passport-google-oauth": "*",
    }

 将从 Google Developers Console 获得的client_id 和 client_secret 存到配置文件中:

{
    //...more configuration
    "GOOGLE_CLIENT_ID" : "xxxx.apps.googleusercontent.com",
    "GOOGLE_CLIENT_SECRET" : "wdsfas3_-safdsafasf",
    "GOOGLE_RETURN_URL" : "http://www.xxx.com/auth/google/return",
}

上篇提到的配置读取器configUtils 将能自动读取到它,结合这些实现Google OAuth 2认证(authUtils.js:

var passport = require('passport')
    , GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
 
var config = require('../configUtils');
 
passport.use(new GoogleStrategy({
        authorizationURL: 'https://accounts.google.com/o/oauth2/auth',
        tokenURL: 'https://accounts.google.com/o/oauth2/token',
        clientID: config.getConfigs().GOOGLE_CLIENT_ID,
        clientSecret: config.getConfigs().GOOGLE_CLIENT_SECRET,
        callbackURL: config.getConfigs().GOOGLE_RETURN_URL
    },
    function (accessToken, refreshToken, profile, done) {
        var userInfo = {
            'type': 'google',
            'userid': profile.id,
            'name': profile.displayName,
            'email': profile.emails[0].value,
            'avatar': profile._json.picture
        };
        return done(null, userInfo);
    }
 
    passport.serializeUser(function (user, done) {
       done(null, user);
    });
 
    passport.deserializeUser(function (obj, done) {
      done(null, obj);
    });
 
));

代码中的 function (accessToken, refreshToken, profile, done) 就是用户授权后的回调,Passport 会将获取到的用户信息包装成profile,里面的转换代码可以自由发挥,最后记得调用done,并将用户信息返回。done 就是我们定义在路由里的回调,稍后会提到。

serializeUser 和 deserializeUser 是转换用的序列化/ 反序列化。

结合Express

在  authUtils.js 中导出给 Express 用的路由:

module.exports = function (app) {
    app.use(passport.initialize());
 
    app.get('/auth/google',
        passport.authenticate('google', {
            scope: 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'
        }));
 
    app.get('/auth/google/return',
        passport.authenticate('google', { failureRedirect: '/login' }),
        function (req, res) {
            if (req.user) {
                //...some user login handling code
            }
            res.redirect('/');
        });
};

 第1个 get 路由是我们可以在UI 上引用的地址,点击它开始OAuth 2 流程,即 Passport 的入口;第2个 get路由是Passport 的出口,即上节提到被 done 方法回调的地址,此时整个 OAuth 2 流程完成,程序得到了 user 信息(Passport将它注入到了 req上,但仅此一次可用,阅后即焚)。在认证过程发生任何错误,将跳转到  failureRedirect 指定的地址。Passport为我们做了诸如 code 交换 token的一系列琐碎事情。

app.js 中调用,So easy:

var authUtils = require('../authUtils');
authUtils(app);

小结

OAuth 2 协议为用户资源的授权提供了一个安全的、开放而又简易的标准,但即便如此,在Node.js 的 OAuth 2交互实现中,仍需 为Owner/App/Server的三方交互流程编写很多代码。使用Passport框架,将大大简化这一编程模型,使我们可以将更多精力投入到业务实现上。

本文提供的仅是一个基于Passport框架的OAuth 2方案思路,代码部分也仅是个骨架,望抛砖引玉。

更多文章请移步我的blog新地址: http://www.moye.me/