OAuth2.0 协议入门指南

本文希望以应用场景的角度出发,帮助大家快随了解OAuth协议流程,更为清楚明白的介绍在各种情况使用什么授权模式更为合适。
OAuth2 官网
原文地址
本系列相关文章:
OpenID Connect 协议入门指南
SAML2.0入门指南

1. 协议中各种角色:应用、API和用户

第三方应用:客户端
客户端,即尝试去获得用户账号信息的应用,用户需要先对此操作授权。

API: 资源服务器
即资源服务器,提供用来获得用户信息的API。

授权服务器
授权服务器用来提供接口,让用户同意或者拒接访问请求。在某些情况下,授权服务器和API资源服务器会是同一个,但是在大多数情况下,二者是独立的。

用户:资源的拥有者
资源的拥有者,当前的请求正在尝试获得他们账户的部分信息。

2. 创建App

在开始OAuth流程之前,你首选需要注册一个新应用到服务器。通常在注册的过程中,需要提供应用的基本信息,比如应用名称、网站信息、logo等等。此外,你还需要注册一个重定向URI,用以将用户通过浏览器或者移动客户端重定向回Web服务器,这个URI很重要,在后面的我们会具体讲到

2.1 重定向URI

服务器只会把用户重定向回注册过的URI以避免一些安全攻击。任何HTTP的从定向请求都必须使用TLS保护,所以要求使用HTTPS。这样的要求是为了防止token在传输的过程中被截获。对于原生APP,重定向的URI可以被注册为一个自定义的URL scheme,比如

demoapp://redirect

2.2 Client ID & Secret

当注册完毕之后,一般会返回给用户一个客户端ID(Client ID)和一个客户端密钥(Client Secret)。这个ID一般是公开的信息,用来构造登录URL,或者被包含在页面的JS代码中。这个密钥(Secret) 必须是保密的。如果一个已经部署的应用不能保护密钥,比如一个简单的JS网页,则不应该使用客户端密钥,同时理论上服务器也不应该向这类应用颁发密钥。

3. 授权:得到授权码

OAuth 2流程的第一步是获得用户的授权。对于基于浏览器应用或是移动应用,这一步通常是在服务器显示给用户的接口上完成。

OAuth 2提供了多种授权模式(grant types),根据不同的情况而使用。

  • 授权码模式:适用于Web应用、浏览器应用或是移动APP;
  • 口令模式:适用于使用用户名和口令登录的模式;
  • 应用访问模式:适用于应用访问;
  • 默认模式:之前被推荐在没有Secret情况下使用,现在被没有客户端密钥的授权码模式取代。

每一个模式的使用细节将在下面的章节中说明。

3.1 Web服务

Web服务是最常见的应用类型。用户所使用的Web App程序运行在服务器端,其源码不会公开暴露,这就代表着这类应用在授权的过程中可以使用客户端密钥(Client Secret),以避免某些攻击手段。

3.1.1 授权

创建登录链接,将用户重定向到授权服务器:

https://oauth2server.com/auth?response_type=code&
 client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
  • code: 代表服务器希望收到授权码;
  • client_id: 注册应用时颁发的ID;
  • redirect_uri: 指明当用户授权完成之后返回的地址;
  • scope: 一个或多个值,用来指明希望获得用户账户哪部分权限;
  • state:由应用生成的一个随机字符串,会在稍后的过程中去验证。

通过以上的链接,用户将会看到如下的界面


image.png

当用户点击“Allow”之后,授权服务器会把用户重定向回应用网站,并在链接中带上授权码:

https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
  • code: 服务器返回的授权码;
  • state: 返回请求授权码过程中发送的state;

当应有接收到这个请求时,要首先验证state是否就是应用刚才发送的值。在发送state之后,可以把state保存到Session以便于后续的比较。这样做的目的是防止应用接受任意伪造的授权码。

3.1.2 换取Token

然后使用授权码到资源服务器换取Token:

POST https://api.oauth2server.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_HERE&
  redirect_uri=REDIRECT_URI&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET
  • grant_type=authorization_code - 代表授权模式为验证码;
  • code=AUTH_CODE_HERE - 验证码;
  • redirect_uri=REDIRECT_URI - 重定向URI;
  • **client_id=CLIENT_ID **- 注册应用时颁发的ID;
  • client_secret=CLIENT_SECRET - 客户端密钥,因为当前请求时服务器之间传输的,并没有暴露给用户。

API服务器会返回授权码和其有效期:

{
  "access_token":"RsT5OjbzRn430zqMLgV3Ia",
  "expires_in":3600
}

或者是授权失败的提示:

{
  "error":"invalid_request"
}

安全性提示:服务器要求应用必须提前注册从定向URI

3.2 纯页面应用

纯页面应用,也称为浏览器应用,其所有运行代码都会加载到本地的浏览器上。因此其代码会暴露给使用者的浏览器,不能保护客户端密钥的安全,所以客户端密钥不能在这种情况下使用,除此之外其和Web应用没有太大区别。

以前,曾经推荐使用默认模式(implicit type),该种模式将会直接返回token,并不使用授权码来换取token。但是到了本文编写之时,业界已经开始转为推荐使用没有Scret的授权码流程,这样这样协议流程更为安全,具体内容请见引文: Redhat, Deutsche Telekom, Smart Health IT.

3.2.1 授权

创建登录链接,将用户发送至授权服务器:

https://oauth2server.com/auth?response_type=code&
client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
  • code - 指明使用授权码模式;
  • client_id - 注册时颁发的client ID;
  • redirect_uri - 重定向URI,代表当授权完成之后,返回的路径;
  • scope - 一个或多个值,代表所希望访问当前用户的那部分信息;
  • state - 由于后续步骤验证实用的随机字符串。
    之后,同样用户会显示授权页面:
    image.png

    当用户点击“Allow”,授权服务器依据重定向URI将用户返回到应用网站:
https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
  • code - 授权服务器返回的授权码;
  • state - 登录请求中发出的随机码;

应有服务器收到该请求之后,应该先核对state的值,以避免接受伪造的授权码;

3.2.2 换取Token

POST https://api.oauth2server.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_HERE&
  redirect_uri=REDIRECT_URI&
  client_id=CLIENT_ID
  • grant_type=authorization_code - 代表授权模式为验证码;
  • code=AUTH_CODE_HERE - 验证码;
  • redirect_uri=REDIRECT_URI - 重定向URI;
  • **client_id=CLIENT_ID **- 注册应用时颁发的ID;

3.3 移动APP

以前,曾经推荐使用默认模式(implicit type),该种模式将会直接返回token,并不使用授权码来换取token。但是到了本文编写之时,业界已经开始转为推荐使用没有Scret的授权码流程,这样这样协议流程更为安全,

类似于纯页面应用,移动应用不能包含客户端密钥,所以使用的是没有客户端密钥的Oauth流程。此外,对于移动APP,还有一些特别地方需要注意。

3.3.1 授权

创建一个登陆按钮,将用户跳转到服务器在手机上原生APP,或是通过手机的浏览器跳转到服务器Web页面、无论是IOS还是Android,都可以通过URL Scheme协议启动本地APP;

3.3.1.1 使用服务器的APP

如果用户安装了服务器的原生APP,比如微博APP或是微信APP,可以通过URL来直接启动APP:

fbauth2://authorize?response_type=code&client_id=CLIENT_ID
  &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
  • code - 指明使用授权码模式;
  • client_id - 注册时颁发的client ID;
  • redirect_uri - 重定向URI,代表当授权完成之后,返回的路径;
  • scope - 一个或多个值,代表所希望访问当前用户的那部分信息;
  • state - 由于后续步骤验证实用的随机字符串。

对于还要支持PKCE extension的服务器,还需要创建一个随机字符串("code verifier")保存本地,然后再URL中追加如下参数:

  • code_challenge=XXXXXXX - 过base64编码后的"code verifier"的Hash值;
  • code_challenge_method=S256 - 指明Hash算法种类,这里是sha256;

需要说明的是,这里的URL用的协议App提前向操作系统定义好的。

3.3.1.2 使用手机Web浏览器

如果服务器在手机上没有一个原生的APP,也可以通过手机浏览器上来走标准的Web认证流程。这里需要注意的是,不要使用应用内置的WebView,因为这样就不无法保证用户正在登陆网站是不是伪造的。

一般是使用移动端的原生浏览器,对于IOS 9+版本的用户,可以使用“SafariViewController”在应用中启动嵌入浏览器,这个API会显示地址栏让用户判断是否打开正确的网页,同时还提供和Safari浏览器共享cookie的功能。该API能保证应用被注入和修改,所以可以被认为是安全的。对于Android开发者,可以考虑使用Chrome Custom Tabs来提供类似的功能。

https://facebook.com/dialog/oauth?response_type=code&client_id=CLIENT_ID
 &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx

参数的意义和使用服务器原生APP的情况是一样的。比如用户登录FaceBook,就会在手机上看到如下界面:

everyday-city-auth.png

3.3.2 换取Token

当用户点击"Approve"之后,用户将会被重定向到应用,同样可以使用URL Scheme:

fb00000000://authorize#code=AUTHORIZATION_CODE&state=1234zyx

应用收到该请求之后,要先比较state的值是否满足预期,然后用授权码来换取Token。

换取Token的过程和在Web中请求类似,只不过没有加入Client Secret。如果服务器需要支持PKCE,则还需要添加更多的参数:

POST https://api.oauth2server.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_HERE&
  redirect_uri=REDIRECT_URI&
  client_id=CLIENT_ID&
  code_verifier=VERIFIER_STRING
  • grant_type=authorization_code - 标识这是授权码模式;
  • code=AUTH_CODE_HERE - 授权码;
  • redirect_uri=REDIRECT_URI -重定向URL;
  • client_id=CLIENT_ID - Client ID;
  • code_verifier=VERIFIER_STRING - 之前步骤中,生成的随机字符串

如果服务器支持PKCE,则授权服务器需要能“Code-Challenge”中识别code,也就是提供再次对code_verifier进行Hash,并且比较计算出的Hash值和之前的code_challenge是否一致,以此代替Client Secret,保证安全性。

3.4 其他授权模式

3.4.1 口令模式

这种模式直接使用用户名和口令来取回Token。很显然,这要求应用去收集用户的口令,所以只建议当前的APP是专门为服务器设计的情况下使用。比如Twitter的App使用该模式登录。

POST https://api.oauth2server.com/token
  grant_type=password&
  username=USERNAME&
  password=PASSWORD&
  client_id=CLIENT_ID
  • grant_type=password - 指明使用口令模式;
  • username=USERNAME - 用户名;
  • password=PASSWORD - 口令;
  • client_id=CLIENT_ID - client ID;

资源服务器返回的Token和其他模式是一样的。值得注意的是,因为口令模式多用于移动端和桌面应用,使用Secret并不合适

3.4.2 应用访问模式

在一些情况下,是应用去访问服务区获取资源,而不是某个用户。比如应用去获取一些服务在应用配置信息,这些信息不属于某个用户的。在这种情形下,应用也需要被授权,得到Token去访问数据,这就是应用访问模式的意义。

OAuth提供了client_credentials模式来结局这个问题:

POST https://api.oauth2server.com/token
    grant_type=client_credentials&
    client_id=CLIENT_ID&
    client_secret=CLIENT_SECRET

资源服务器返回形式和其他模式是相同的。

4. 创建已经授权的请求

最后就是使用Token获取资源,如何表面一个请求是已经呗授权?就是Http的头中加入Token:

curl -H "Authorization: Bearer RsT5OjbzRn430zqMLgV3Ia" \
https://api.oauth2server.com/1/me

另外还要确保是使用HTTPS通信,这样才能确保信息安全。

5. 与OAuth 1.0版本的差异

最后说明下两个版本的OAuth协议有什么不同点。

5.1 认证和签名

OAuth 1.0 流程中有大量密码学上的签名操作,以保证数据完整性;OAuth 2中有HTTPS来保护数据。

5.2 用户体验

和OAuth 1.0相比,OAuth 2在移动端的体验更好。

5.3 性能

和OAuth 1.0相比,OAuth 2性能更好,减少了流程的步骤。

其他参考

Learn more about creating OAuth 2.0 Servers
PKCE Extension
Recommendations for Native Apps
More information is available on OAuth.net
Some content adapted from hueniverse.com.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容