本文以一种简化的格式描述OAuth 2.0 ,以帮助开发人员和服务提供者实现该协议。
The OAuth 2 spec(规范) 读起来让人有点困惑,我写这篇文章是为了帮助用简化的格式来描述术语。核心规范将许多决策留给实现者,通常是基于实现的安全权衡。而不是描述所有需要成功实现OAuth 2的可能的决策,这篇文章做出了适合大多数实现的决策。
注意:这篇文章是根据OAuth 2.0的当前最佳实践,从2012年的版本得到了更新的。原版本可以在这里找到。here.
Table of Contents (目录)
- Roles: Applications, APIs and Users
- Creating an App
-
Authorization: Obtaining an access token 授权,获取一个令牌
- Web Server Apps
- Single-Page Apps 单页面应用程序
- Mobile Apps
- Others
- Making Authenticated Requests 制作身份验证的请求
- Differences from OAuth 1.0 区别OAuth1.0
- Authentication and Signatures 身份验证和签名
- User Experience and Alternative Authorization Flows 用户体验和可选授权流
- Performance at Scale 大规模集群的性能
- Resources
Roles (角色)
The Third-Party Application: "Client" (第三方应用程序:“客户端”)
客户端是试图访问用户账户的应用程序。它需要得到用户的许可才能做到这一功能。
The API: "Resource Server" (API:"资源服务器")
资源服务器是用于访问用户信息的API服务器。
The Authorization Server(授权服务器)
这是提供用户批准或拒绝请求的接口服务器。在较小的规模中,这可能是与API资源服务器相同的服务器,但更大规模的部署通常会将其作为单独的组件构建。
The User: "Resource Owner"(用户:“资源所有者”)
资源所有者是能够访问他们部分账户信息的人。
Creating an App(创建一个应用程序)
在开始OAuth流程之前,你必须先注册一个新的应用程序。在注册一个新的应用程序的时候,你通常会注册一些基本信息,比如应用程序名称,网站,标识等等。此外,您必须注册一个重定向URL,以便将用户重定向到web服务器,基于浏览器或移动应用程序。
Redirect URIs (重定向的URL)
该服务只会将用户重定向到一个已注册的URL,这有助于防止一些攻击。任何HTTP重定向URL都必须使用TLS(安全传输层协议)安全保护,因此服务器将只重定向到以"https"开头的URL。这样可以防止令牌(tokens)在授权过程中被拦截,本地应用程序可以注册一个针对它的重定向URL解决方案。它看起来像:demoapp://redirect.
Client ID and Secret (客户端ID 和 密钥)
在注册了你的应用之后,你将收到一个客户ID和一个客户的密钥。客户ID被认为是公共信息,用于构建登录URL,或者包含在网页上的JavaScript源代码中。客户的密钥必须保密,如果一个被部署的应用不能保守密钥,比如单页javascript应用或者原生应用,然后,这个密钥就不会被使用了,最理想的情况是,服务不应该首先对这些类型的应用发布密钥。
Authorization (授权)
OAuth 2的第一步是获得用户的授权,对于基于浏览器或移动的应用程序来说,这通常是通过向用户显示服务器提供的接口来实现的。
OAuth 2为不同的用例提供了几种“授权类型”。授权类型定义是:
-
Authorization Code for apps running on a web server, browser-based and mobile apps
(授权码:对于在web服务器上运行的应用程序,基于浏览器和移动用友程序) -
Password for logging in with a username and password
(密码:使用用户名和密码登录的密码) -
Client credentials for application access
(应用程序访问的客户凭据) -
Implicit was previously recommended for clients without a secret, but has been superceded by using the Authorization Code grant with no secret.
(隐式之前推荐给没有密钥的客户,但是通过使用授权代码授权,没有任何密钥。已经被取代)
下面将详细描述每个用例:
Web Server Apps(网络服务器应用程序)
在处理OAuth服务器时,Web服务器应用程序是您遇到的最常见的应用程序类型。Web应用程序是用服务端语言写的,并在服务器上运行,而应用程序的源代码在公共场合是不可用的。这意味着应用程序可以在与授权服务器通信时使用它的客户端机密,这可以帮助避免一些攻击向量。
Authorization
创建一个“登录”链接,将用户发送到:
https://oauth2server.com/auth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
- code - Indicates that your server expects to receive an authorization code (表明您的服务器期望收到授权代码)
- client_id - The client ID you received when you first created the application (第一次创建应用程序时接收到的客户ID)
- redirect_uri - Indicates the URI to return the user to after authorization is complete (表示在授权完成之后将URL返回给用户)
- scope - One or more scope values indicating which parts of the user's account you wish to access (一个或者多个范围值:指示您希望访问的用户账户的哪些部分)
- state - A random string generated by your application, which you'll verify later (由应用程序生成的随机字符串,稍后将验证。)
用户看到授权提示符:
授权提示符如果用户单击“允许”,该服务将使用一个auth(授权)码将用户重定向到您的站点。
https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
-
code - The server returns the authorization code in the query string
(服务器返回查询字符串中的授权代码) -
state - The server returns the same state value that you passed
(服务器返回您通过的相同的状态值)
您应该首先比较这个状态值,以确保它与您开始时的值相匹配。通常可以将状态值存储在cookie或者会话中。然后在用户返回时进行比较。这将确保您的重定向端点不能被欺骗来尝试交换任意的授权代码。
Token Exchange(令牌交换)
您的服务器交换用于访问令牌的身份验证码(授权码):
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 - The grant type for this flow is authorization_code
(该流程的授权类型是authorization_code ) -
code=AUTH_CODE_HERE - This is the code you received in the query string
(这是在查询字符串中接收到的代码) -
redirect_uri=REDIRECT_URI - Must be identical to the redirect URI provided in the original link
(必须与原始链接中提供的重定向URL相同) -
client_id=CLIENT_ID - The client ID you received when you first created the application
(你收到你第一次创建应用程序时的客户端ID) -
client_secret=CLIENT_SECRET - Since this request is made from server-side code, the secret is included
(由于这个请求时由服务端代码完成的,所以包含了这个密钥)
服务器以访问令牌和过期时间来应答。
{
"access_token":"RsT5OjbzRn430zqMLgV3Ia",
"expires_in":3600
}
或者如果出现了错误:
{
"error":"invalid_request"
}
安全性:注意,服务必须要求应用程序预先注册它们的重定向URL。
Single-Page Apps(单页面应用程序)
单页面应用程序(或基于浏览器的应用程序)在从网页加载源代码后完全在浏览器运行。因为整个源代码都可以在浏览器中使用,他们不能保持客户密钥的机密性。所以这个密钥是不被使用的。该流正好与上面的授权码流完全相同。但在最后一步,在不使用客户端密钥的情况下,将授权代码交换访问令牌。
注意:以前,推荐使用基于浏览器的应用程序使用“隐式”流“它立即返回一个访问令牌,并且没有令牌交换步骤。在最初编写规范的时候,行业最佳实践已经改变,建议在不使用客户端密钥的情况下使用授权代码流。这位创建安全流提供了更多的机会,例如使用状态参数。引用:Redhat, Deutsche Telekom,Smart Health IT.
Authorization (授权)
创建一个”登录“链接,将用户发送到:
https://oauth2server.com/auth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
-
code - Indicates that your server expects to receive an authorization code
(表明您的服务器期望收到授权码) -
client_id - The client ID you received when you first created the application
(第一次创建应用程序时接收到的客户端ID) -
redirect_uri - Indicates the URI to return the user to after authorization is complete
(表示在授权完成之后将URL返回给用户.) -
scope - One or more scope values indicating which parts of the user's account you wish to access
(一个或多个范围值指定该用户账户的哪些部分你想要访问的) -
state - A random string generated by your application, which you'll verify later
(由应用程序生成的随机字符串,稍后将验证。)
用户看到授权提示符:
授权提示符如果用户单击”允许“,该服务将使用一个auth码将用户重定向到您的站点。
https://oauth2client.com/cb?code=AUTH_CODE_HERE&state=1234zyx
-
code - The server returns the authorization code in the query string
(服务器返回查询字符串中的授权代码) -
state - The server returns the same state value that you passed
(服务器返回您通过的相同的状态值)
您应该首先比较这个状态值,以确保它与开始时的值相匹配。通常可以将状态值存储在cookie中,然后在用户返回时进行比较。这将确保您的重定向端点不能被欺骗来尝试交换任意的授权代码。
Token Exchange(令牌交换)
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 - The grant type for this flow is authorization_code
(该流的授权类型是授权码) -
code=AUTH_CODE_HERE - This is the code you received in the query string
(这是在查询字符串中接收到的代码) -
redirect_uri=REDIRECT_URI - Must be identical to the redirect URI provided in the original link
(必须与原始链接中提供的重定向URL相同) -
client_id=CLIENT_ID - The client ID you received when you first created the application
(第一次创建应用程序时接收到的客户端ID)
Mobile Apps(移动应用程序)
注意:在此之前,有人建议移动和原生应用使用隐性授权,在最初编写规范的时候,业界最佳实践已经改变了,建议使用授权代码流,对本地应用程序没有任何密钥。对于原生应用来说,还有一些 additional recommendations(额外的建议)值得一看
基于浏览器的应用程序,移动应用程序也不能维护其客户密钥的机密性。正因为如此,移动应用程序还必须使用不需要客户端密钥的OAuth流。还有一些额外的关注,即移动应用应该牢记于心,以确保OAuth流程的安全性。
Authorization(授权)
创建一个”Log in“按钮,将用户发送到手机的本地应用程序,或者是为服务的移动网页,在iphone上,应用程序可以注册一个自定义的URL协议例如: "facebook://",因此,每当访问该协议的URL时,本地Facebook应用程序就会启动。在Android上,应用程序可以注册URL匹配模式,如果访问了匹配模式的URL,就会启动原生应用。
Using the Service's Native App (使用该服务的本地应用程序)
如果用户安装了原生的Facebook应用。将它们直接链接到以下网址:
fbauth2://authorize?response_type=code&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
-
response_type=code - indicates that your server expects to receive an authorization code
(表明您的服务器期望收到授权代码) -
client_id=CLIENT_ID - The client ID you received when you first created the application
(第一次创建应用程序时接收到的客户ID) -
redirect_uri=REDIRECT_URI - Indicates the URI to return the user to after authorization is complete, such as fb00000000://authorize
(表示在授权完成之后将URL返回用户,例如:fb00000000://authorize) -
scope=email - One or more scope values indicating which parts of the user's account you wish to access
(一个或多个范围值,指示您希望访问的用户账户的哪些部分) -
state=1234zyx - A random string generated by your application, which you'll verify later
(由应用程序生成的随机字符串,稍后将验证)
对于支持 PKCE extension的服务器。(如果您正在构建一个服务器,那么您应该支持PKCE扩展),你应该包括以下参数:首先,创建一个”代码验证器“,它是应用程序在本地存储的一个随机字符串。
-
code_challenge=XXXXXXX - This is a base64-encoded version of the sha256 hash of the code verifier string
(这是代码验证字符串sha256散列的一个base64编码版本。) -
code_challenge_method=S256 - Indicates the hashing method used to compute the challenge, in this case, sha256.
(表示用于计算挑战的散列方法,在本例中是sha256)
注意:您的重定向URL可能看起来像 ”fb00000000://authorize“ 是您的应用程序在操作系统上注册的自定义URL方案。
Using a Web Browser(使用Web浏览器)
如果服务没有本地应用程序,您可以启动一个移动浏览器到标准的web授权URL,注意,在您自己的应用程序中永远不应该使用嵌入式web视图,因为这使用户无法保证他们实际上是在服务网站中输入密码而不是钓鱼网站。你应该启动原生的移动浏览器,或者使用新的IOS"SafariViewController"在你的应用程序中启动嵌入式浏览器。这个API是在ios9中添加的,它提供了一个机制在应用程序中启动一个浏览器,它显示了地址栏这样用户就可以确认他们在正确的网站上,还可以和真正的Safari浏览器共享cookie。它还可以防止应用程序的使用从检查和修改浏览器的内容。所以可以被认为是安全的。
https://facebook.com/dialog/oauth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
同样,如果服务支持PKCE,那么这些参数应该包括在上面描述的。
-
response_type=code - indicates that your server expects to receive an authorization code
(表明您的服务器期望收到授权 代码) -
client_id=CLIENT_ID - The client ID you received when you first created the application
(第一次创建应用程序时接收到的客户端ID) -
redirect_uri=REDIRECT_URI - Indicates the URI to return the user to after authorization is complete, such as fb00000000://authorize
(表示在授权完成之后将用户返回给用户的URL,例如 fb00000000://authorize) -
scope=email - One or more scope values indicating which parts of the user's account you wish to access
(一个或者多个范围值,指示您希望访问的用户账户的哪些部分。) -
state=1234zyx - A random string generated by your application, which you'll verify later
(由应用程序生成的随机字符串,稍后将验证。)
用户将看到授权提示:
授权提示Token Exchange (令牌交换)
点击“批准”后,用户将被重定向到您的应用程序伴随着一个URL,像下面这样:
fb00000000://authorize#code=AUTHORIZATION_CODE&state=1234zyx
您的移动应用程序首先应该验证状态对于初始请求中使用的状态,然后可以交换访问令牌的授权代码。
令牌交换看起来与在web服务器应用程序中交换代码一样,除非这个密钥没有被发送,如果服务器支持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 - The grant type for this flow is authorization_code
(该流的授权类型是授权码) -
code=AUTH_CODE_HERE - This is the code you received in the query string
(这是在查询字符串中接收到的代码) -
redirect_uri=REDIRECT_URI - Must be identical to the redirect URI provided in the original link
(必须与原始链接中提供的重定向URL相同) -
client_id=CLIENT_ID - The client ID you received when you first created the application
(第一次创建应用程序时接收到的客户端ID。) -
code_verifier=VERIFIER_STRING - The plaintext string that you previously hashed to create the code_challenge
(您之前所创建的散列纯文本字符串用来创建设备代码)
授权服务器将验证此请求并返回访问令牌。如果服务器支持PKCE,然后授权服务器就会识别,这段代码是通过设备代码生成的,并会对所提供的明问文本哈希,以及确认哈希版本与最初授权请求中发送的散列字符串相对应。这确保了使用不支持密钥的客户端使用授权代码流的安全性。
Other Grant Types(其他授权类型)
Password(密码)
OAuth 2还提供了一个“密码”授权类型,可用于直接为访问令牌交换用户名和密码。因为这显然需要应用程序来收集用户的密码,它只能由服务本身创建的应用程序使用,例如,原生Twitter应用可以使用这种授权类型登录移动端或桌面应用程序。
使用密码授予类型,简单地提出一个POST请求,如下:
POST https://api.oauth2server.com/token
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
-
grant_type=password - The grant type for this flow is password
(该流的授权类型是password) -
username=USERNAME - The user's username as collected by the application
(应用程序收集的用户的用户名。) -
password=PASSWORD - The user's password as collected by the application
(应用程序收集的用户的密码。) -
client_id=CLIENT_ID - The client ID you received when you first created the application
(第一次创建应用程序时接收到的客户端ID.)
服务器以与其他授权类型相同的格式返回一个访问令牌。
注意:这里不包含客户端密钥,因为密码授予的大多数用例都是移动或桌面应用程序,而这些应用程序的秘密无法得到保护
Application access(应用访问)
在某些情况下,应用程序可能需要一个访问令牌代表自己,而不是用户。例如,该服务可能为应用程序提供一种方式来更新他们自己的信息,比如他们的网站URL或者图标,或者他们可能希望获得关于该应用的用户的统计信息。在这种情况下,应用程序需要一种方法来为自己的账户获取访问令牌,在任何特定用户的上下文中。OAuth 为这个目的提供了client_credentials授权类型。
要使用client_credentials授权类型,请发出如下的POST请求:
POST https://api.oauth2server.com/token
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
响应将包括与其他授权类型相同的访问令牌。
Making Authenticated Requests(制作身份验证的请求)
所有的授权类型的最终结果是获得一个访问令牌。
现在你有了一个访问令牌,您可以向API发出请求。您可以使用URL来快速地创建一个API请求。
curl -H "Authorization: Bearer RsT5OjbzRn430zqMLgV3Ia"
https://api.oauth2server.com/1/me
就是这样!确保您总是通过HTTPS发送请求,并且永远不要忽略无效的证书。HTTPS是唯一保护请求不被拦截或修改的东西。
Differences from OAuth 1.0 (区别OAuth 1.0)
OAuth 1.0 主要是基于现有的专有协议。如Flickr的“FlickrAuth”和Google的“AuthSub”。结果是基于实际的实现经验的最佳解决方案。然而,在使用了多年的协议之后,社区已经学会了在OAuth 1.0 被证明是有限或令人困惑的三个主要领域重新思考和改进该协议。
Authentication and Signatures (身份验证和签名)
大多数开发人员对OAuth 1.0的困惑和烦恼是由于使用客户机ID和密钥来签名请求的加密要求。失去了容易复制和粘贴旋度的能力,使得快速启动变得更加困难。
OAuth 2认识到了这一困难,并在浏览器,客户端和API之间的所有通信中都需要HTTPS来替换签名。
User Experience and Alternative Authorization Flows(用户体验和可选授权流)
OAuth 包含两个主要部分,获得一个访问令牌,并使用访问令牌来发出请求。OAuth 1.0 最适合桌面浏览器,但不能为本地桌面和移动应用提供良好的用户体验,也不能提供诸如游戏或电视控制台之类的替代设备。
OAuth2支持本地应用程序更好的用户体验,并且支持扩展协议以提供与未来设备需求的兼容性。
Performance at Scale(大规模集群的性能)
当较大的提供者开始使用OAuth 1.0时,社区很快意思到协议的伸缩性不佳。许多步骤都需要状态管理和临时凭证,这需要共享存储,并且很难跨数据中心同步。OAuth 1.0还要求API服务器访问应用程序的ID和密钥,这常常破坏大多数大型提供者的体系结构,其中授权服务器和API服务器是完全分离的。
OAuth 2支持分离获取用户授权和处理API调用的角色。需要这种可伸缩性的较大的提供者可以自由地实现它,而较小的提供者可以在它们希望的情况下使用相同的服务器。
Resources(资源)
- 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.
Previous versions of this post:
网友评论