本文希望以应用场景的角度出发,帮助大家快随了解OAuth协议流程,更为清楚明白的介绍在各种情况使用什么授权模式更为合适。
OAuth2 官网
原文地址
本系列相关文章:
OpenID Connect 协议入门指南
SAML2.0入门指南
第三方应用:客户端
客户端,即尝试去获得用户账号信息的应用,用户需要先对此操作授权。
API: 资源服务器
即资源服务器,提供用来获得用户信息的API。
授权服务器
授权服务器用来提供接口,让用户同意或者拒接访问请求。在某些情况下,授权服务器和API资源服务器会是同一个,但是在大多数情况下,二者是独立的。
用户:资源的拥有者
资源的拥有者,当前的请求正在尝试获得他们账户的部分信息。
在开始OAuth流程之前,你首选需要注册一个新应用到服务器。通常在注册的过程中,需要提供应用的基本信息,比如应用名称、网站信息、logo等等。此外,你还需要注册一个重定向URI,用以将用户通过浏览器或者移动客户端重定向回Web服务器,这个URI很重要,在后面的我们会具体讲到。
服务器只会把用户重定向回注册过的URI以避免一些安全攻击。任何HTTP的从定向请求都必须使用TLS保护,所以要求使用HTTPS。这样的要求是为了防止token在传输的过程中被截获。对于原生APP,重定向的URI可以被注册为一个自定义的URL scheme,比如
demoapp://redirect
当注册完毕之后,一般会返回给用户一个客户端ID(Client ID)和一个客户端密钥(Client Secret)。这个ID一般是公开的信息,用来构造登录URL,或者被包含在页面的JS代码中。这个密钥(Secret) 必须是保密的。如果一个已经部署的应用不能保护密钥,比如一个简单的JS网页,则不应该使用客户端密钥,同时理论上服务器也不应该向这类应用颁发密钥。
OAuth 2流程的第一步是获得用户的授权。对于基于浏览器应用或是移动应用,这一步通常是在服务器显示给用户的接口上完成。
OAuth 2提供了多种授权模式(grant types),根据不同的情况而使用。
每一个模式的使用细节将在下面的章节中说明。
Web服务是最常见的应用类型。用户所使用的Web App程序运行在服务器端,其源码不会公开暴露,这就代表着这类应用在授权的过程中可以使用客户端密钥(Client Secret),以避免某些攻击手段。
创建登录链接,将用户重定向到授权服务器:
https://oauth2server.com/auth?response_type=code&
client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
通过以上的链接,用户将会看到如下的界面
image.png
当用户点击“Allow”之后,授权服务器会把用户重定向回应用网站,并在链接中带上授权码:
https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
当应有接收到这个请求时,要首先验证state是否就是应用刚才发送的值。在发送state之后,可以把state保存到Session以便于后续的比较。这样做的目的是防止应用接受任意伪造的授权码。
然后使用授权码到资源服务器换取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
API服务器会返回授权码和其有效期:
{
"access_token":"RsT5OjbzRn430zqMLgV3Ia",
"expires_in":3600
}
或者是授权失败的提示:
{
"error":"invalid_request"
}
安全性提示:服务器要求应用必须提前注册从定向URI
纯页面应用,也称为浏览器应用,其所有运行代码都会加载到本地的浏览器上。因此其代码会暴露给使用者的浏览器,不能保护客户端密钥的安全,所以客户端密钥不能在这种情况下使用,除此之外其和Web应用没有太大区别。
以前,曾经推荐使用默认模式(implicit type),该种模式将会直接返回token,并不使用授权码来换取token。但是到了本文编写之时,业界已经开始转为推荐使用没有Scret的授权码流程,这样这样协议流程更为安全,具体内容请见引文: Redhat, Deutsche Telekom, Smart Health IT.
创建登录链接,将用户发送至授权服务器:
https://oauth2server.com/auth?response_type=code&
client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
image.png
https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
应有服务器收到该请求之后,应该先核对state的值,以避免接受伪造的授权码;
POST https://api.oauth2server.com/token
grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=REDIRECT_URI&
client_id=CLIENT_ID
以前,曾经推荐使用默认模式(implicit type),该种模式将会直接返回token,并不使用授权码来换取token。但是到了本文编写之时,业界已经开始转为推荐使用没有Scret的授权码流程,这样这样协议流程更为安全,
类似于纯页面应用,移动应用不能包含客户端密钥,所以使用的是没有客户端密钥的Oauth流程。此外,对于移动APP,还有一些特别地方需要注意。
创建一个登陆按钮,将用户跳转到服务器在手机上原生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
对于还要支持PKCE extension的服务器,还需要创建一个随机字符串("code verifier")保存本地,然后再URL中追加如下参数:
需要说明的是,这里的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
当用户点击"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
如果服务器支持PKCE,则授权服务器需要能“Code-Challenge”中识别code,也就是提供再次对code_verifier进行Hash,并且比较计算出的Hash值和之前的code_challenge是否一致,以此代替Client Secret,保证安全性。
这种模式直接使用用户名和口令来取回Token。很显然,这要求应用去收集用户的口令,所以只建议当前的APP是专门为服务器设计的情况下使用。比如Twitter的App使用该模式登录。
POST https://api.oauth2server.com/token
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
资源服务器返回的Token和其他模式是一样的。值得注意的是,因为口令模式多用于移动端和桌面应用,使用Secret并不合适。
在一些情况下,是应用去访问服务区获取资源,而不是某个用户。比如应用去获取一些服务在应用配置信息,这些信息不属于某个用户的。在这种情形下,应用也需要被授权,得到Token去访问数据,这就是应用访问模式的意义。
OAuth提供了client_credentials模式来结局这个问题:
POST https://api.oauth2server.com/token
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
资源服务器返回形式和其他模式是相同的。
最后就是使用Token获取资源,如何表面一个请求是已经呗授权?就是Http的头中加入Token:
curl -H "Authorization: Bearer RsT5OjbzRn430zqMLgV3Ia" \
https://api.oauth2server.com/1/me
另外还要确保是使用HTTPS通信,这样才能确保信息安全。
最后说明下两个版本的OAuth协议有什么不同点。
OAuth 1.0 流程中有大量密码学上的签名操作,以保证数据完整性;OAuth 2中有HTTPS来保护数据。
和OAuth 1.0相比,OAuth 2在移动端的体验更好。
和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.
作者:登高且赋
链接:https://www.jianshu.com/p/6392420faf99
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。