What is oauth2
Introduction
Oauth2 设计的目的是为了可以让用户不用提供用户名密码给第三方,也能让第三方可以访问用户的resource。
例如 一个app 可以用微信登录,那就是指这个app会获取用户的微信name, logo 之类的信息去创建用户并登录。
当用户点击使用微信登录时,app会弹出微信授权的扫描界面,当用户打开微信扫描后,用户就可以登录这个app 了。
用户扫描微信授权的二维码,这个动作意味着
一 用户已经登录了微信,即完成了authentication; 二 扫描即表示授权,完成了authorization,即同意app获取用户的微信 user_profile.
这个过程,用户的密码是没有提供给app的,密码还是保存在微信这边,微信只是在用户授权完成之后,返回一个access_token 给app。app 既可用这个access_token 去获取用户的 user_profile 信息。
Information
-
resource owner
An entity capable of granting access to a protected resource.
When the resource owner is a person, it is referred to as an end-user.例子里这个就是用户
-
resource server
The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.例子里这个就是微信平台
-
authorization server
The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.例子里这个就是微信平台
-
client
An application making protected resource requests on behalf of the resource owner and with its authorization.例子里就是 app
如何使用oauth2
Client Registration
client 在使用oauth2 之前需要先注册到authorization server 才可以使用。
- 为什么需要注册呢
通过注册 Client,授权服务器(Authorization Server)可以验证客户端的身份(例如通过 client_id 和 client_secret),确保只有合法的客户端能参与授权流程。
防止恶意应用冒充合法客户端(如钓鱼攻击)。
记录哪些客户端在访问资源,以便 追踪滥用行为(例如频繁请求令牌),统计使用情况或计费,在出现安全问题时快速撤销特定客户端的权限 等。
注册client 到authorization server, server 会返回client_id 和 client_secret 给 client。
client 注册的时候需要提供 redirection URI, client type 和其他一些信息例如 client 名字, description 等。
- client type
有 public 和 confidential 这两种。
只有confidential才可以使用 client_secret。 因为public 无法保证client_secret的安全。
Client 申请authorization 的流程
Oauth 申请authorization grant的流程,根据grant type,有4种,分别是 authorization code
, implicit
, resource owner password credentials
, and client credentials
。
这里只介绍用得最多也最通用的一种即 authorization code
。
The authorization code grant type 的类型是可以同时获取access token 和 refresh token的。
而且这种类型是一种 redirection-based 的flow,所以需要client 支持被回调。
下图这个流程是来自于RFC种的流程图,user-agent 一般是指 浏览器。
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
Note: The lines illustrating steps (A), (B), and ( C) are broken into two parts as they pass through the user-agent.
(A) The client 触发浏览器发送request 到authorization endpoint.
The client includes its client identifier, requested scope,
local state, and a redirection URI to which the authorization
server will send the user-agent back once access is granted (or denied).
这里 redirection URI 是optional的,如果不带则server使用在client registration 的提供的。
(B) 这个时候会弹出登录框提示用户输入用户名,密码来认证,认证后会显示需要授权的内容,用户再确认授权。
( C) 用户授权之后,server 会返回一个authorization code 并让浏览器通过redirection URI回到client.
类似下面这样
HTTP/1.1 302 Found Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
(D) client 带上前面收到的 authorization code 和其他的一些参数来请求 server 的token endpoint 来获取token.
参数需要 grant_type
,code
,client_id
, redirect_uri
(optional, must be identical and included if A step brings), client_secret
(optional, if need authenticate client).
这个步骤有一个问题,就是server 怎么验证client 的身份呢?正对类型为credential的,client 可以带上client_secret
,但是对于public 类型的,或者client_secret
不够安全的,容易泄露的,怎么验证申请token 的client 就是获得授权的client呢? 这个时候就可以使用 Proof Key for Code Exchange (PKCE)
了。
PKCE
是oauth2 流程的一种扩展,简单来说就是在client 在 A 步骤(authorization request)的时候带上一个随机数给server。
然后在申请token步骤中,client也带上相同的随机数,如果server校验和之前的一样,那就确实是相同的client。
(E) Authorization server 通过了client 的认证,则返回access token and, optionally, a refresh token。注意,这一步access token 是不会通过 user-agent 了,因为access token 是什么重要的,不能泄露。
当access token
无效了或过期了,就可以用refresh token
是用来获取access token
的。 注意 refresh token
是发送给oauthrization server
的,不能发给 resource server
。 而access token
是发给 resource server
去获取resource 的。
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
Figure: Refreshing an Expired Access Token
Client 获取resource
通过access token 获取 resource
+--------+ +---------------+
| |--(A)----- Access Token ------>| Resource |
| Client | | Server |
| |<-(B)--- Protected Resource ---| |
+--------+ +---------------+
Figure: Access Protected Resource