理解OAuth2.0

Posted by BY annie dong on March 17, 2017

很有以前就专门看过相关内容,今天打算整理一下……


OAuth2

OAuth就是一个对于授权的开放标准
OAuth2是现在网络上比较流行的授权框架,它可以使第三方应用程序或客户端获得对HTTP服务上(例如 Google,GitHub )用户帐户信息的有限访问权限。 OAuth2通过将用户身份验证委派给托管用户帐户的服务以及授权客户端访问用户帐户进行工作。综上,OAuth 2 可以为 Web 应用 和桌面应用以及移动应用提供授权流程。

OAuth2 角色

OAuth 2 标准中定义了以下几种角色:

  • 资源所有者(Resource Owner)
  • 资源服务器(Resource Server)
  • 授权服务器(Authorization Server)
  • 客户端(Client)

OAuth2 的授权流程

avatar

  1. Authrization Request
    客户端向用户请求对资源服务器的authorization grant
  2. Authorization Grant(Get)
    如果用户授权该次请求,客户端将收到一个authorization grant
  3. Authorization Grant(Post)
    客户端向授权服务器发送它自己的客户端身份标识和上一步中的authorization grant,请求访问令牌。
  4. Access Token(Get)
    如果客户端身份被认证,并且authorization grant也被验证通过,授权服务器将为客户端派发access token。授权阶段至此全部结束。
  5. Access Token(Post && Validate)
    客户端向资源服务器发送access token用于验证并请求资源信息。
  6. Protected Resource(Get)
    如果access token验证通过,资源服务器将向客户端返回资源信息。

上面六个步骤之中,第二步Authorization Grant是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。

客户端应用注册

在应用 OAuth 2 之前,你必须在授权方服务中注册你的应用。如 Google Identity Platform 或者 Github OAuth Setting,诸如此类 OAuth 实现平台中一般都要求开发者提供如下所示的授权设置项:

  • 应用名称
  • 应用网站
  • 重定向URI或回调URL
    重定向URI是授权方服务在用户授权(或拒绝)应用程序之后重定向供用户访问的地址,因此也是用于处理授权码或访问令牌的应用程序的一部分。
一旦你的应用注册成功,授权方服务将以client id和client secret的形式为应用发布client credentials(客户端凭证)。client id是公开透明的字符串,授权方服务使用该字符串来标识应用程序,并且还用于构建呈现给用户的授权 url 。当应用请求访问用户的帐户时,client secret用于验证应用身份,并且必须在客户端和服务之间保持私有性。

授权许可(Authorization Grant)的四种模式

OAuth 2.0定义了四种授权方式:

  • 授权码模式(authorization code)– 结合普通服务器端应用使用
  • 简化模式(implicit)– 结合移动应用或 Web App 使用
  • 密码模式(resource owner password credentials)– 适用于受信任客户端应用,例如同个组织的内部或外部应用
  • 客户端模式(client credentials)– 适用于客户端调用主服务API型应用(比如百度API Store)

授权码模式(authorization code)

功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。
avatar

  • 步骤A User Authorization Request
    首先,客户端构造了一个用于请求authorization code的URL并引导User-agent跳转访问。
https://authorization-server.com/auth
 ?response_type=code
 &client_id=29352915982374239857
 &redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback
 &scope=create+delete
 &state=xcoiv98y2kd22vusuye3kch
response_type=code 
此参数和参数值用于提示授权服务器当前客户端正在进行Authorization Code授权流程。
client_id 
客户端身份标识。
redirect_uri 
标识授权服务器接收客户端请求后返回给User-agent的跳转访问地址。
scope 
指定客户端请求的访问级别。
state 
由客户端生成的随机字符串,步骤2中用户进行授权客户端的请求时也会携带此字符串用于比较,这是为了防止CSRF攻击。  
  • 步骤B User Authorizes Applcation
    当用户点击上文中的示例链接时,用户必须已经在授权服务中进行登录(否则将会跳转到登录界面,不过 OAuth 2 并不关心认证过程),然后授权服务会提示用户授权或拒绝应用程序访问其帐户。以下是授权应用程序的示例:
    avatar

  • 步骤C Authorization Code Grant
    如果用户确认授权,授权服务器将重定向User-agent至之前客户端提供的指向客户端的redirect_uri地址,并附带code和state参数(由之前客户端提供),于是客户端便能直接读取到authorization code值。

https://example-client.com/redirect
 ?code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3
 &state=xcoiv98y2kd22vusuye3kch
state值将与客户端在请求中最初设置的值相同。客户端将检查重定向中的状态值是否与最初设置的状态值相匹配。这可以防止CSRF和其他相关攻击。
    
code是授权服务器生成的authorization code值。code相对较短,通常持续1到10分钟,具体取决于授权服务器设置。
  • 步骤D Access Token Request
    现在客户端已经拥有了服务器派发的authorization code,接下来便可以使用authorization code和其他参数向服务器请求access token(POST方式)。其他相关参数如下:
grant_type=authorization_code - 这告诉服务器当前客户端正在使用Authorization Code授权流程。
code - 应用程序包含它在重定向中给出的授权码。
redirect_uri - 与请求authorization code时使用的redirect_uri相同。某些资源(API)不需要此参数。
client_id - 客户端标识。
client_secret - 应用程序的客户端密钥。这确保了获取access token的请求只能从客户端发出,而不能从可能截获authorization code的攻击者发出。
  • 步骤E Access Token Grant
    服务器将会验证第4步中的请求参数,当验证通过后(校验authorization code是否过期,client id和client secret是否匹配等),服务器将向客户端返回access token。
{
  "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
  "token_type":"bearer",
  "expires_in":3600,
  "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
  "scope":"create delete"
}

至此,授权流程全部结束。直到access token 过期或失效之前,客户端可以通过资源服务器API访问用户的帐户,并具备scope中给定的操作权限。

简化模式(implicit)

Implicit授权流程和Authorization Code基于重定向跳转的授权流程十分相似,但它适用于移动应用和 Web App,这些应用与普通服务器端应用相比有个特点,即client secret不能有效保存和信任。

相比Authorization Code授权流程,Implicit去除了请求和获得authorization code的过程,而用户点击授权后,授权服务器也会直接把access token放在redirect_uri中发送给User-agent(浏览器)。 同时第1步构造请求用户授权 url 中的response_type参数值也由 code 更改为 token 或 id_token 。
avatar

  • 步骤A User Authorization Request
    客户端构造的URL如下所示:
https://{yourOktaDomain}.com/oauth2/default/v1/authorize?client_id=0oabv6kx4qq6
h1U5l0h7&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%3
A8080&state=state-296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601&nonce=foo'
response_type的response_type参数值为 token 或 id_token 。其他请求参数与Authorization Code授权流程相比没有并什么变化。
  • 步骤B User Authorizes Application(略)参考上面

  • 步骤C Redirect URI With Access Token In Fragment
    假设用户授予访问权限,授权服务器将User-agent(浏览器) 重定向回客户端使用之前提供的redirect_uri。并在 uri 的 #fragment 部分添加access_token键值对。如下所示:

HTTP/1.1 302 Found //重定向
Location: http://localhost:8080/#access_token=eyJhb[...]erw&token_type=Bearer&expires_in=3600&scope=openid&state=state-296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601

token_type - 当且仅当response_type设置为 token 时返回,值恒为 Bearer。
注意在Implicit流程中,access_token值放在了 URI 的 #fragment 部分,而不是作为 ?query 参数。
  • 步骤D User-agent Follows the Redirect URI
    User-agent(浏览器)遵循重定向指令,请求redirect_uri标识的客户端地址,并在本地保留 uri 的 #fragment 部分的access_token信息。

  • 步骤E Application Sends Access Token Extraction Script
    客户端生成一个包含 token 解构脚本的 Html 页面,这个页面被发送给User-agent(浏览器),执行脚本解构完整的redirect_uri并提取其中的access_token(access token信息在第4步中已经被User-agent保存)。

  • 步骤F Access Token Passed to Application
    User-agent(浏览器)向客户端发送解构提取的access token。
    至此,授权流程全部结束。直到access token 过期或失效之前,客户端可以通过资源服务器API访问用户的帐户,并具备scope中给定的操作权限。

密码模式(resource owner password credentials)

密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
avatar

  • 步骤A Resource Owner Password Credentials From User Input
    用户向客户端提供用户名与密码作为授权凭据。

  • 步骤B Resource Owner Password Credentials From Client To Server
    客户端向授权服务器发送用户输入的授权凭据以请求 access token。客户端必须已经在服务器端进行注册。

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
grant_type - 必选项,值恒为 password
  • 步骤C Access Token Passed to Application
    授权服务器对客户端进行认证并检验用户凭据的合法性,如果检验通过,将向客户端派发 access token
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

客户端模式(client credentials)

在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。
客户端可以直接使用它的client credentials或其他有效认证信息向授权服务器发起获取access token的请求。 avatar

  • 步骤A
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
grant_type - 必选项,值恒为 client_credentials
  • 步骤B
    {
         "access_token":"2YotnFZFEjr1zCsicMWpAA",
         "token_type":"example",
         "expires_in":3600,
         "example_parameter":"example_value"
    }
    

参考资料

OAuth2 深入介绍