OAuth2.0与微信小程序初探

OAuth2.0 不是一个Authentication Protocol, 而是一个Authorization framework,用来授权第三方应用获取用户数据

简单理解就是我们登录大部分网站时,除了最简单的账户密码登录外,可以选择的通过qq登录or微信登录等快捷登录方式。

参与方

  • Resource Owner 拥有数据或资源并授予访问权的用户,既用户本身
  • Client 请求访问用户资源的应用程序,既用户选择进行Oauth的客户端
  • Authorization Server 验证用户身份并在获得用户同意后发放Token的服务器
  • Resource Server 托管客户要访问的受保护资源的服务器

作用

OAuth在Client与Resource Server之间,设置了一个授权层(authorization layer)。Client不能直接登录Resource Server,只能登录授权层,以此将用户与客户端区分开来。Client登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。

Client登录授权层以后,Resource Server根据令牌的权限范围和有效期,向Client开放用户储存的资料。

流程

from RFC 6749

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端相应授权。

(C)客户端使用获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,发放令牌到客户端。

(E)客户端使用令牌,向资源服务器申请获取对应的资源。

(F)资源服务器确认令牌无误,同意向客户端开放相应的受保护的资源。

下图同理,图源OAuth2.0 详解 - 知乎 (zhihu.com)

授权

在步骤B中存在一个授权动作,这个动作有几个实现方案,如****授权码模式、简化模式、密码模式以及客户端模式,****以下只对授权码模式分别做一下具体介绍

授权码模式

首先是最常见的授权码模式

流程可参考下图,图源有图解有案例,我终于把OAuth2.0搞清楚了 - 知乎 (zhihu.com)

更具体的还是得看看 RFC 6749

大概步骤如下:

(A)用户访问客户端,后者重定向将前者到认证服务器。

(B)用户选择是否给予客户端授权。

(C)如果用户选择给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

A步骤中,客户端申请认证的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

e.g.

1
2
3
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

C步骤中,服务器回应客户端的URI,包含以下参数:

  • code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

e.g.

1
2
3
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
  • code:表示上一步获得的授权码,必选项。
  • redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
  • client_id:表示客户端ID,必选项。
1
2
3
4
5
6
7
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

E步骤中,认证服务器发送的HTTP回复,包含以下参数:

  • access_token:表示访问令牌,必选项。
  • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache

     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }

关于access token和refresh token

  • Access token: 即客户端用来请求Resource Server(API). Access tokens通常是short-lived短暂的。access token是short-lived, 因此没有必要对它做revoke, 只需要等待access token过期即可。
  • Refresh token: 当access token过期之后refresh token可以用来获取新的access token。refresh token是long-lived。refresh token可以被revoke。

OpenID Connect

OpenID Connect 是在OAuth2.0 协议基础上增加了身份验证层 (identity layer)。OAuth 2.0 定义了通过access token去获取请求资源的机制,但是没有定义提供用户身份信息的标准方法。OpenID Connect作为OAuth2.0的扩展,实现了Authentication的流程。OpenID Connect根据用户的 id_token 来验证用户,并获取用户的基本信息。

图源OAuth2.0 详解 - 知乎 (zhihu.com)

OpenID Connect流程主要涉及如下几个步骤:

  1. 发现获取OIDC metadata
  2. 执行OAuth流程,获取id_tokenaccess_token。例如:在 Authorization code模式下即为通过code来换取id_tokenaccess_token
  3. 获取JWT签名(signature key)并且可选的动态的注册客户端应用
  4. 基于日期签名来本地验证JWT id_token,或者将id_token发给后端backend进行验证
  5. 根据id_token通过UserInfo Endpoint获取用户信息,根据access_token获取用户其他资源信息

更详细的流程如下:

微信小程序

到了微信小程序可以非常明显的发现,微信小程序的登录逻辑也就是类似Oauth这么一套流程首先wx.login获取的code类似于Oauth授权码模式获得的授权码,然后通过此授权码开发者服务器再可以向微信的接口服务换取到在微信小程序登录流程中的token(X也就是session_keyopenid这些敏感信息,通过session_key可以知道用户的各类信息如手机号等等。

具体流程如下图所示