OAuth2.0协议小记

OAuth协议解决的是给不属于我方系统的第三方应用授权访问我方系统受保护资源(例如账号等)的问题,即它是一个授权协议。

它是基于HTTP的协议,主要有四个概念、四种角色,流程主要是资源拥有者授权、系统生成token两步。

 

1 场景

第三方应用要来访问我们系统的信息,但该应用不是我们系统的用户。

例如通过QQ账号来登录微博的场景中微博是第三方应用;

又如在云打印应用上允许应用读取你百度网盘的照片打印,这里云打印应用是第三方应用。

如果要让第三方应用访问网盘上你的照片,简单的方法是把用户名密码直接给第三方应用;这种做法的弊端是可访问你所有照片、密码泄露、没法取消授权等,因此要用其他方法以能限制第三方应用的权限。

Oauth协议要解决的就是 如何让 在资源所有者不用把用户名密码告诉第三方应用的情况下 第三方应用能访问系统中资源所有者的受保护的资源 的问题,术语曰“授权”问题。其引入了授权层来将第三方应用与资源拥有者做区分,即给第三方应用一个有别于资源所有者的角色来访问受保护的资源。具体而言,就是在资源拥有者同意授权后通过授权层来给第三方应用生成一个token,第三方应用以该token作为凭证来访问资源,该凭证的作用类似于密码,详见后文“协议交互流程”一节。

OAuth provides a method for clients to access a protected resource on behalf of a resource owner. OAuth addresses these issues by introducing an authorization layer and separating the role of the client from that of the resource owner.

当你的网站实现了OAuth协议后,很多第三方应用就可以来对接你的网站,从而可用你网站的账号去登录第三方应用、或者让第三方应用来访问你网站上的资源。实际上,也有很多基于OAuth协议的协议,比如知名的OIDC(OpenID Connect)协议就是基于OAuth来实现用户身份认证的。

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol.

 

 

2 What

Open Protocol for Authorization that allows users to share their private resources to third-partys. It is a protocol designed to let third-party applications authenticate to perform actions as a user, without getting the user's password.

OAuth 是一个基于HTTP的授权协议,目前最新版本是OAuth 2.0。它定义了一套授权机制,用来向第三方应用颁发令牌(token):数据的拥有者告诉系统,同意授权第三方应用进入系统来获取这些数据;系统从而产生一个短期的访问令牌(token),用来代替密码,供第三方应用使用,第三方应用以该token作为凭证来访问资源。

OAuth2.0 的主要内容就是 资源拥有者授权、系统生成token 两部分

令牌的作用相当于密码,但有区别:(时间上)令牌有有效期限制、(空间上)令牌有权限范围(例如限制令牌可访问哪些资源)、令牌有效性可被发放方取消。令牌的作用与密码类似,校验通过了就可以进入系统,故令牌也要保密而不能泄露,且要设置较短的有效期。

 

3 四个概念

Credentials:凭证,如用户名、密码、access token、refresh token、授权码等。Authentication、Authorization分别都是一种Credentials。

Authentication:是什么——认证信息,通常包括 PrincipalCredential(典型的是用户名和密码)。什么用——通过验证此信息来确定访问者的身份:例如确定是否是我方系统的用户、确定访问者的角色、确定是否是资源的所有者。动词 authenticate 表示进行认证的行为。

Authorization:是什么——授权信息,通常是token。什么用——通过此信息来确定是否有访问资源的权限。在工程实践上,很多不大的系统不要求请求者传授权信息,而是直接通过认证结果(如角色)来确定访问权限。动词 authorize 表示进行授权的行为。

Protected Resource:资源,如图片等各种数据。通常存储在服务器上,且是受系统保护的,访问者得提供 认证信息 或 授权信息 才能访问资源。

User-Agent:代用户 执行请求 和 接收并处理响应 者,通常是浏览器。

  

 

4 四种角色

Resource Owner(resource controller、end-user)、Resource Server(resource hoster)、Client(Third-Party application)、Authorization Server

Resource Owner:资源所有者(controller),可将资源授权给他人访问。当资源所有者指一个人时,称为 end-user(用户)。

Resource Server:资源托管者(hoster),保存资源、保护资源使得不能被随意访问。能够接收和响应 对受保护的资源的 带着access token的请求。通常就是API服务器。

Client:表示一个应用,它带着Credentials信息请求Resource Server上的资源。业务中常称为第三方应用(Third-Party application)。

Authorization Server:授权服务器,用于向Client发放access token。通常也作为Authentication Server,以对Owner或Client进行authenticate

注:

Authorization Server、Resource Server可以是同一个Server,也可不是同一个。

一个Authorization Server可以作为多个Resource Server的授权服务器。

实际上可以细分下,Client分为客户端前端、客户端后端。在OAuth2.0的几种授权机制下,有的授权机制只跟客户端前端交互、有的只跟客户端后端交互、有的与客户端前后端都有交互。

 

上述四个概念和四种角色串起来的交互过程:

Resource Owner 具有 Authentication 信息、Client 通过 Authorization Server 获取 Authorization 信息;

对于 Protected Resource,Resource Owner通过提供认证信息访问、Client通过提供授权信息访问;

Resource Server 通过验证来访者的认证信息或授权信息决定来访者是否可以访问Protected Resource;

上述各个过程彼此间的交互有页面交互、后台交互,页面交互通过 User-Agent 来完成。

 

 

5 OAuth 2.0 授权协议交互流程(Protocol Flow)

总结为三个过程(四种角色中的Client分别向另三种角色申请一次,即为三个过程):

1 A、B:Client向Resource Owner申请授信(authorization grant):可以是前者直接向后者发请求、或者由前者以Authorization Server作为中介向后者请求授权,协议中推荐用第二种;返回给Client的信息(grant)是能表示资源拥有者授权的可信凭证(crentdential),如授权码。OAuth 2.0定义了四种grant以及grant的扩展机制。

An authorization grant is a credential representing the resource owner's authorization (to access its protected resources) used by the client to obtain an access token.

2 C、D:Client向Athorization Server申请token:前者对后者发起带有Client authentication、Owner authorization grant信息的请求,后者对这两个信息进行验证,验证通过则返回access token。

3 E、F:Client带着token向Resource Server请求Protected Resource。

前两个过程是关于授权的,标准: https://tools.ietf.org/html/rfc6749;最后一个过程是关于Token的,标准:https://tools.ietf.org/html/rfc6750。其中前两个过程最为重要,我们实现OAuth 2.0协议时主要就是实现这两个过程,可以针对前两个过程实现一个OAuth公共库供各种具体业务服务引用,第三个过程则由具体业务服务实现。

  

前言

当一个服务A(如SenseStudy实验平台)集成了OAuth 2.0协议后,第三方应用就可以通过OAuth 2.0协议接入到A,使得第三方应用可以访问A上的资源。接入的过程就是标准OAuth 2.0的几种基于HTTP的授权模式的交互过程。

第三方应用需要首先在A上注册,以获得clientId、clientSecret凭证。这两个信息相当于是第三方应用的用户名、密码,第三方应用与A对接或向A申请授权时需要用到这两个信息。

 

5.1 错误响应

不管哪种授权模式,都有可能在交互过程中出错,错误信息格式的定义遵守OAuth 2.0标准,格式如下:

参数类型说明
error String 错误码。在不同模式下可能有不同的值。可能的值详见:https://tools.ietf.org/html/rfc6749#section-4.1.2.1https://tools.ietf.org/html/rfc6749#section-5.2
error_description String 错误信息描述
error_uri String 展示错误内容相关的uri。本模块未使用该字段,即该字段值始终为null

错误响应示例:

{
    "error": "invalid_client",
    "error_description": "unmatched client and redirect uri",
    "error_uri": null
}

不同授权模式(下节介绍)下出异常时的响应信息不尽相同。对于带授权页面的授权模式(前两种),redirect uri或client id无效时在授权页面提示Owner而不重定向,否则将错误信息包含在url中进行重定向;对于其他授权模式或刷新token等相关Endpoint,请求有误时也返回上述格式的错误响应。

 

5.2 四种授信(Authorization Grant)

OAuth 2.0定义了四种Authorization Grant(或称 authorization flow),详情可参阅:https://tools.ietf.org/html/rfc6749#section-4,其最终目的都是给client生成用于访问protected resource的access token。

四种授权模式中,对资源访问的保护强度依次变弱。

授权过程中应该考虑的基本点:token不能出现在url中、clientSecret不能出现在url中,等。

 

5.2.1 授权码授权(Authorization Code)

https://tools.ietf.org/html/rfc6749#section-4.1

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

过程:client以authorization server为中介向resource owner请求authorization code。具体而言:

client通过user-agent将resource owner导向authorization server;

authorization server对resource owner进行认证并获取授权信息,然后生成授权码;

authorization server附带上授权码将resource owner导回client;

client server带着授权码向authrization server请求token;

authorization server对client进行认证、校验owner的授权信息,通过则生成token返回。

 (为什么不在Owner授权通过时直接生产token并返回?token在RUL不安全。实际上第二种授权模式就是该方案,见后文)

 

包含请求授权码、请求token两个步骤:

1 请求授权码

第三方应用通过浏览器将用户导向授权页面,以让用户 进行认证、选择将哪些资源授权给第三方应用访问、选择是否同意授权;当用户做好选择并同意授权后,授权服务生成一个授权码并通过指示浏览器重定向将该授权码返回给第三方应用。

请求数据格式

参数类型是否必须说明
response_type String yes 值恒为code,表示授权码模式
scope String list yes 第三方应用期望申请的权限列表。权限列表由A定义。
client_id String yes 第三方应用的id,第三方应用在A注册时得到该值
redirect_uri String false 第三方应用接收授权码的uri,未传则采用注册时填的值
state String false 第三方应用自定义的任意状态数据,授权服务在响应请求时会将值原原本本返回

第三方在将用户导向本模块的上述Endpoint时将参数附加到Endpoint后面,示例:

 http://localhost:8081/oauth/authorize?response_type=code&scope=get_user_info,list_student&redirect_uri=http://www.test.com/api/code&client_id=4af6d97c97744c13&state=abcd 

响应数据格式

用户同意授权后,授权服务将授权码及state附加到第一步所指定的redirect_uri后面作为参数,并返回重定向指令指示浏览器重定向到该目标位置。 返回的重定向location示例:

 http://www.test.com/api/code?code=7e9a348c-xxfafdsas&state=abcd 

当出错时(如client_id等凭证信息不正确),授权服务会返回错误信息,格式见前一节所述。同样地,这些错误信息的字段会被附加到redirect_uri后面作为参数返回给第三方应用,例如: http://www.test.com/api/code?error=xxx 

 

2 请求token

第三方应用带着刚获取到的授权码向授权服务请求token,若授权服务校验通过则签发token返回给第第三方应用

请求数据格式

参数类型是否必须说明
grant_type String yes 值恒为authorization_code,表示授权码模式
code String list yes 第一步获取的授权码
client_id String yes 第三方应用的id,第三方应用在A注册时得到该值
client_secret String yes 第三方应用的secret,第三方应用在A注册时得到该值
redirect_uri String yes 与第一步中该字段的值一样

示例:

{
    "grant_type":"authorization_code",
    "code":"7e9a348c-xxfafdsas",
    "client_id":"4af6d97c97744c13",
    "client_secret":"524e62c792be4ea38adc5eaace065afc",
    "redirect_uri":"http://www.test.com"
}

  

响应数据格式

参数类型说明
access_token String 授权服务签发的access token
token_type String token类型,通常为bearer
expires_in String access token的有效时长,单位为秒
scope String list access token所具有的权限
refresh_token String 授权服务签发的refresh token

返回数据示例:

{
    "access_token": "Bearer eyJhxx1",
    "token_type": "bearer",
    "expires_in": 7200,
    "scope": [
        "get_user_info"
    ],
    "refresh_token": "Bearer eyJhxx2"
}

 

全局完整流程:

 

 

 

5.2.2 隐式授权(Implicit Grant)

https://tools.ietf.org/html/rfc6749#section-4.2

Implicit 是为纯网页应用设计的。Implicit 设计之初,由于浏览器的同源策略不允许跨站请求,因此 Authorization Code 不可行;现在由于浏览器普遍支持 CORS,且 Implicit 本身也在安全风险,故目前建议使用 PKCE(见后文)。

Implicit 不通过第三方应用程序的服务器,直接在浏览器中向授权服务申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌出现在URL中故对访问者是可见的,且客户端不需要认证。

过程:client以authorization server为中介向resource owner请求授权。具体而言:

client通过user-agent将resource owner导向authorization server。请求时带着redirect uri等信息,该redirect uri通常是个带有js脚本的页面。

authorization server对resource owner进行认证并获取授权信息,然后生成access token;

authorization server将access token加密(因token放在uri中,为了减少暴露风险,进行"加密")后附在client请求时传来的redirect uri上作为uri hash参数;

user agent请求该redirect uri,页面中的js脚本会被执行,js脚本中的逻辑是从uri中提取出access token。

redirect uri对应的页面内容示例:

GET /cb HTTP/1.1
  Host: client.example.org

  HTTP/1.1 200 OK
  Content-Type: text/html

  <script type="text/javascript">

  // First, parse the query string
  var params = {}, postBody = location.hash.substring(1),
      regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(postBody)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }

  // And send the token over to the server
  var req = new XMLHttpRequest();
  // using POST so query isn't logged
  req.open('POST', 'https://' + window.location.host +
                   '/catch_response', true);
  req.setRequestHeader('Content-Type',
                       'application/x-www-form-urlencoded');

  req.onreadystatechange = function (e) {
    if (req.readyState == 4) {
      if (req.status == 200) {
  // If the response from the POST is 200 OK, perform a redirect
        window.location = 'https://'
          + window.location.host + '/redirect_after_login'
      }
  // if the OAuth response is invalid, generate an error message
      else if (req.status == 400) {
        alert('There was an error processing the token')
      } else {
        alert('Something other than 200 was returned')
      }
    }
  };
  req.send(postBody);
View Code

 

与授权码方式类似,但是:

只需要发一次请求;

授权及申请token的过程都在浏览器执行,不需要client后端参与;

不需要client认证:请求时不需要提供client的认证信息(如clientSecret)而是依靠clientId redirectUri进行Client认证、依靠Owner username password进行Owner的认证和授权;

用于请求access token,标准规定不能签发refresh token;

token 出现在url中,比较不安全,这也是为何放在url hash中的原因(因为浏览器请求时不会将hash参数发给后端)

The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI.  These clients are typically implemented in a browser using a scripting language such as JavaScript.

The implicit grant type does not include client authentication, and relies on the presence of the resource owner and the registration of the redirection URI.

The access token may be exposed to the resource owner or other applications with access to the resource owner's user-agent.

Developers should note that some user-agents do not support the inclusion of a fragment component in the HTTP "Location" response header field.  Such clients will require using other methods for redirecting the client than a 3xx redirection response -- for example, returning an HTML page that includes a 'continue' button with an action linked to the redirection URI.

 

此模式与第一种模式很类似,只不过只有一个步骤:

第三方应用通过浏览器将用户导向授权页面,以让用户选择将哪些资源授权给第三方应用访问、以及选择是否同意授权;当用户做好选择并同意授权后,授权服务生成token并通过指示浏览器重定向将token返回给第三方应用。

此模式相当于将第一种模式的第一步和第二步合在一起,但由于会将token放在url中返回,为了降低暴露风险,会对返回url中的参数进行编码,第三方应用需要进行相应解码。此外,此种模式不会返回refresh token。

 

请求数据格式

与第一种模式第一个步骤的一样,只不过response_type字段的值恒为token

响应数据格式

会将如下字段拼接后进行Base64 URL编码,然后附加到请求时所指定的redirect_uri后面作为hash参数(或称fragment参数)。

参数类型说明
access_token String 授权服务签发的access token
token_type String token类型,通常为bearer
expires_in String access token的有效时长,单位为秒
scope String list access token所具有的权限
state String 第三方应用自定义的任意状态数据,授权服务在响应请求时会将值原原本本返回

返回示例:

 https://www.test.com/#YWNjZXNzX3Rva2VuPUJlYXJlciBleUpoeHgxJnNjb3BlPWdldF91c2VyX2luZm8mc3RhdGU9YWYmdG9rZW5fdHlwZT1iZWFyZXImZXhwaXJlc19pbj03MjAw 

值"access_token=Bearer eyJhxx1&scope=get_user_info&state=af&token_type=bearer&expires_in=7200" 中的参数会以Base64 URL编码,并被附加到请求时指定的redirect_uri后面作为uri hash参数,最终得到上述结果。

第三方应用应进行相应的解码以获取到字段数据。

 

20230306注:此模式的初衷是应用于纯在浏览器内运行而没有后台(从而无法保存secret)服务的场景,因为该模式无须保存且不会暴露client的secret信息到URL上。后来已经不推荐用 Implicit Grant,而是用 Authoriztion Code Grant with PKCE extension 替代。关于后者,见后文。

Single-page apps (or browser-based apps) run entirely in the browser after loading the source code from a web page. Since the entire source code is available to the browser, they cannot maintain the confidentiality of a client secret, so the secret is not used in this case. 

Note: Previously, it was recommended that browser-based apps use the "Implicit" flow, which returns an access token immediately in the redirect and does not have a token exchange step. In the time since the spec was originally written, the industry best practice has changed to recommend that the authorization code flow be used without the client secret. This provides more opportunities to create a secure flow, such as using the PKCE extension. Further reading: OAuth 2.0 Security Best Current Practice (ietf.org), OAuth 2.0 for Browser-Based Apps (ietf.org).

全局完整流程:

 

 

 

5.2.3 资源拥有者凭证授权(Resource Owner Password Credentials)

Client等同于Resource Owner。

在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而授权服务只有在无法用其他授权模式的情况下,才能考虑使用这种模式

此种授权模式只有一个步骤: 资源拥有者(A平台的用户)事先将自己的用户名、密码告诉第三方应用,第三方应用持该信息向A请求资源,此时对A来说,第三方应用相当于A的用户。

请求数据格式

参数类型是否必须说明
grant_type String yes 值恒为password,表示resource owner password模式
scope String list yes 第三方应用期望申请的权限列表。权限列表由A定义。
username String yes 资源拥有者的用户名
password String yes 资源拥有者的密码
client_id String yes 第三方应用的id,第三方应用在A注册时得到该值

示例:

{
    "grant_type":"password",
    "client_id":"4af6d97c97744c13",
    "username":"superadmin01",
    "password":"SenseStudy123",
    "scope":["all_resource"]
}

 

响应数据格式

与第一种授权模式中第二个步骤返回的数据格式一样。

 

5.2.4 客户端凭证授权(Client Credentials)

客户端以自己的名义而不是以用户的名义向"服务提供商"进行认证,即可认为客户端自己就是资源的拥有者。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。用于请求access token,标准规定不能签发refresh token。

此种授权模式只有一个步骤: 第三方应用带着事先在A注册的凭证信息向A请求授权,A只要校验凭证有效就向第三方应用签发token。此种授权模式下只要校验凭证有效就发放token,而无需经过资源拥有者同意,故比较危险,只应被用于授予信任的自己公司的应用!最好对应用进行审核。

请求数据格式

参数类型是否必须说明
grant_type String yes 值恒为clientcredentials,表示client credentials模式
scope String list yes 第三方应用期望申请的权限列表。权限列表由A定义。
client_id String yes 第三方应用的id,第三方应用在A注册时得到该值
client_secret String yes 第三方应用的secret,第三方应用在A注册时得到该值

示例:

{
    "grant_type":"password",
    "client_id":"4af6d97c97744c13",
    "client_secret":"524e62c792be4ea38adc5eaace065afc",
    "scope":["all_resource"]
}

响应数据格式

与第一种授权模式中第二个步骤返回的数据格式类似,只不过此种授权模式不会反回refresh token字段。

 

Authorization Code Grant with PKCE

(详情可参阅文章 oauth2-simplified

Authoriztion Code Grant with PKCE(Proof Key for Code Exchange):Authorization Code Grant 的加强,而不是替代。主要用于提高code交换时的安全性,Authorization Code Grant with PKCE 授权模式通常用于替代 Implicit Grant,适用于 browser-base app 场景。

PKCE (RFC 7636) is an extension to the Authorization Code flow to prevent CSRF and authorization code injection attacks.

PKCE is not a replacement for a client secret, and PKCE is recommended even if a client is using a client secret.

Note: Because PKCE is not a replacement for client authentication, it does not allow treating a public client as a confidential client.

PKCE was originally designed to protect the authorization code flow in mobile apps, but its ability to prevent authorization code injection makes it useful for every type of OAuth client, even web apps that use a client secret.

Authoriztion Code Grant with PKCE extension 的流程与Authorization Code Grant 的几乎一样,其解决的是:在纯浏览器(无后台服务)页面的场景下,不传clientSecret,如何确保code安全传输而不被冒用的问题。其原理很简单,引入了三个概念:

code_verifier:43-128个字符的随机串

code_challenge :url-safe_base64( sha256-hash( codeVerifier  )) 的值

code_challenge_method:hash 方法

与Authorization Code Grant 一样有两个流程,只不过额外传了上述信息:

第一步(申请code)请求时额外带上 code_challenge、code_challenge_method,服务端额外保存这两个信息

第二步(申请token)请求时不带client_secret,但带上code_verifier,服务端根据code_challenge_method计算哈希值并做base64转换看结果是否与code_challenge 一致,不一致则不认该code。

可见,这种模式是借助了上述三个信息来校验code的有效性,而原始的Authorization Code Grant 模式是靠client Id、clientSecret校验的。另外,此模式校验code有效性的原理与HTTPS中校验服务端公钥的有效性本质上也是一样的,再次体会到“万变不离其宗”。

 

5.3 Token

5.3.1 Access Token & Refresh Token

Access Token

Access tokens are credentials used to access protected resources. Tokens represent specific scopes and durations of access, granted by the resource owner, and enforced by the resource server and authorization server. The token may denote an identifier used to retrieve the authorization information or may self-contain the authorization information.

Refresh Token

Refresh tokens are credentials used to obtain new access tokens. Refresh tokens are issued to the client by the authorization server and are used to obtain a new access token when the current access token becomes invalid or expires, or to obtain additional access tokens with identical or narrower scope. OAuth 2.0 中Refresh Token是可选的,即并不一定要生成并返回给Client。

 

 

5.3.2 申请新的 access token

当第三方服务申请到的access token到期时,其可以带着refresh token向授权服务申请延长access token的有效期。 由于需要refresh token参数,因此此功能只有第一、三种授权模式下可以使用。

请求数据格式

参数类型是否必须说明
grant_type String yes 值恒为refresh_token,表示申请更新token
scope String list yes 第三方应用期望申请的权限列表。权限列表由A定义。
client_id String yes 第三方应用的id,第三方应用在A注册时得到该值
client_secret String yes 第三方应用的secret,第三方应用在A注册时得到该值
refresh_token String yes 第三方应用之前申请access token时授权服务同时签发的refresh token

示例:

{
    "grant_type":"refresh_token",
    "client_id":"4af6d97c97744c13",
    "client_secret":"524e62c792be4ea38adc5eaace065afc",
    "scope":["all_resource"],
    "refresh_token":"Bearer eyJhxx2"
}

响应数据格式

与第四种授权模式的返回相同。

 

 

5.4 几种授权模式的比较或总结

 

 

关于应用注册

不管哪种授权方式,第三方应用申请令牌之前,都必须先到授权服务器备案,说明自己的身份,然后会拿到两个凭证信息:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。实际上这就是第三方应用来我们系统的 注册过程

 

关于认证与授权

理论上,不管哪种授权模式,Client请求token时候都需要三种credential:Owner认证、Owner授权、Client认证。实际上,有些信息可以省略或间接得到,而不用显式提交。

第一种授权模式:根据Owner输入的username password进行Owner认证和授权(第一步),并生成了授权码,生成的授权码代表了Owner认证和授权的信息;根据请求时带着的clientId clientSecret进行Client认证、校验授权码相当于间接进行了Owner认证和授权校验(第二步)。

第二种授权模式:根据Owner输入的username password进行Owner认证和授权;但没传clientSecret,而是通过clientId和注册的redirectUri进行Client认证。 但这两参数都是在url中,所以很容易泄露从而被伪造请求,故安全性较低。

第三种授权模式:根据Client请求时带着的Owner的用户名密码进行Owner认证和授权;由于Client相当于是Resource Owner故不需要进行Client认证(也可认为是通过Owner认证间接进行了Client认证),即只要所传的用户名密码正确就都授权。

第四种授权模式:根据带着的Client的clientId clientSecret进行Client认证;由于Client自身就是资源的拥有者故无需进行Owner认证和授权(也可认为是通过Client认证间接进行了Owner认证和授权),即只要所传的clientid clientSecret正确就都授权。

 

关于是否有Resource Owner参与授权

1、2、3种授权模式有(1、2直接参与,3通过提供用户名密码间接参与),4则无。

 

关于Endpoint

三种Endpoint:authorization endpoint、token endpoint、redirection endpoint。前两种为授权服务器的、最后者为Client的。

 

关于回调地址重定向的跨域问题

Client需要将授权服务器设为可信的,具体而言,对于前两种方式中的重定向步骤中,Client需要设置以允许来自Authorization Server的跨域请求,否则返回302带有Location Header时会报跨域错误。当然,也可以不按标准规定进行重定向(标准中说明允许如是做,见 https://tools.ietf.org/html/rfc6749#section-1.7),如:可以自定义字段来告知前端代码进行重定向以绕过上述跨域限制。

"While the examples in this specification show the use of the HTTP 302 status code, any other method available via the user-agent to accomplish this redirection is allowed and is considered to be an implementation detail."

 

带授权页面的授权模式(1、2种)中的几个为什么

state参数的作用

用来防止CSRF:因code grant模式中,第一步请求返回重定向后client会发起第二步请求(带着code发起对authorization server的token请求)。若无state则可任意伪造对该redirect uri的请求以触发该第二步过程,有了state后就可防止了:client会比较state与自己存的不一样,从而居家发起第二步请求。

state —— An opaque value used by the client to maintain state between the request and callback.  The authorization server includes this value when redirecting the user-agent back to the client. 

You should first compare this state value to ensure it matches the one you started with. You can typically store the state value in a cookie or session, and compare it when the user comes back. This helps ensure your redirection endpoint isn't able to be tricked into attempting to exchange arbitrary authorization codes.

 

 

第三方应用请求token时,服务端有两种返回token的方式,可以根据第三方应用是否提供回调地址判断:

1、标准。服务端生成token后调用第三方应用请求token时指定的回调地址,以将token传给第三方应用。第三方应用在该地址handler内实现自己的逻辑,如将token保持在本地、调用资源服务器接口获取资源等。

2、不标准(但Github OAuth采用此):第三方也可不提供回调地址,直接在请求返回后直接拿到了token,然后实现自己的上述后续逻辑。

 

第一、第二种授权机制的第一步骤中是否需要client_secret?按理需要,但该步骤中第三方应用把信息传递给授权服务时只能通过url,此时会泄露,所以不需要;另外,第一种中,第一步骤是为了申请资源拥有者授权,故只要对拥有者认证,第二步才对Client认证。

 第一种适用于Web-Server  app(带后台的应用),第二种适用于Browser-based app(无后台应用,纯前台页面)。

 

为什么不在用户同意授权后(步骤1)就直接把token返回给第三方?

因为授权页面是在授权服务,不可能让授权服务主动去调客户端来给他授权码(代价高),故授权成功后授权服务要把信息返回给第三方应用只能通知浏览器通过重定向(总不能让授权服务去调第三方应用的接口)来实现,所以信息只能放在url,而token放url不安全,故加了一个步骤来防止token出现在url:

方式1(这就是第一种授权机制采用的):重定向+第二步骤。先返回授权码然后由第三方应用服务器带着授权码向授权服务请求token,这过程在后台运行,界面上是看不到。此方式的一个小缺点:由于包括 访问重定向到第三方服务器的url、第三方服务器在该url hanlder中完成请求token逻辑 两部分,故可能页面会稍微停顿一会。

方式2(这就是第二种授权机制采用的):重定向+加密。将token作为hash参数而不是query参数附在client请求时指定的redirect uri以减少token泄露风险(因为浏览器请求时不会将hash参数发给后端),通常还可先对token做下编码处理以免明文出现在url中。该redirect uri通常是个包含有js script的页面,浏览器访问时会执行js脚本,该脚本逻辑为从redirect uri中提取出token,然后保存在浏览器。

 

为什么不在用户同意授权后(步骤1)就直接返回资源给第三方?返回不了,原因:

当前是在授权服务的前端页面,不可能让授权服务去调第三方接口上传给第三方,这点与上面不在第一步直接返回token同理;

用重定向也不行因为不可能把资源放url上(简单的字符串可以,图片呢?)。

简而言之,资源必须是先由客户端主动发起请求才返回的。

 

为什么授权码模式中不在客户端带着授权码来请求时(步骤2)返回资源而不是返回token?技术上是行得通,但如果这样的话那授权码就相当于是“令牌”了,为了安全,此时:一方面你就得考虑授权码的有效期、刷新等问题;另一方面,即使你考虑好了这些问题,但在前一步中授权码是通过url返回给客户端的,即“令牌”出现在了url,这不安全。

 

关于是否返回refresh token

各种授权模式下是否签发refresh token:1、3是,2、4否。为何?

refresh token的意义在于让系统代替用户决策去获取access token,显然只有1、2、3种授权中有用户的参与(更多可参阅 https://stackoverflow.com/questions/29233772/why-is-a-refresh-token-not-provided-by-oauth2-servers-responding-to-a-client-cr),此外因第2种中token出现在url,为了减少token有效期,不签发refresh token。综上,只有1、3是。

 

其他

OAuth2.0与1.0是不兼容的,共性少。

 TLS is mandatory to implement and to use with OAuth 2.0.

This specification leaves a few required components partially or fully undefined (e.g., client registration, authorization server capabilities, endpoint discovery).

 

6 扩展

OAuth 2.0 的一个典型应用是 OIDC(OpenID Connect)。

OIDC是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth 2.0。它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议。我们都知道OAuth2是一个授权协议,它无法提供完善的身份认证功能,OIDC使用OAuth2的授权服务器来为第三方客户端提供用户的身份认证,并把对应的身份认证信息传递给客户端,其可以适用于各种类型的客户端(比如服务端应用,移动APP,JS应用),且完全兼容OAuth2,也就是说你搭建了一个OIDC的服务后,也可以当作一个OAuth2的服务来用。

详情参阅:https://openid.net/specs/openid-connect-core-1_0.html

 

OAuth1.0 协议,可参阅 官方标准 rfc5849快速入门指引

 

7 参考资料

https://tools.ietf.org/html/rfc6749

https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

http://www.ruanyifeng.com/blog/2019/04/oauth_design.html

http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html

 

posted @ 2020-05-13 17:40  March On  阅读(625)  评论(0编辑  收藏  举报
top last
Welcome user from
(since 2020.6.1)