OAuth2之authorization code
(移动)互联网发展到现在,一些基础类的产品(SNS、Email、IM…)已经相当完善。重复建设这些基础类的产品,未必能做到其更好。但是,基础类产品之间的自由组合,却能给到用户更好的体验效果(如,在新邮件到达的时候在QQ/wechat上直接提醒邮件到达、读取邮件)。同样,在我们自己的产品上中,整合这些基础产品之后,也能让我们的产品的使用成本大大降低(如,支持使用 google/facebook 账号登陆可以减少用户重复注册账号的麻烦,降低用户使用产品的壁垒、在 gmail 中收取到电商平台的发货通知邮件后,自动把物流信息追加AfterShip mobile tracking APP进行物流跟踪提醒,省去用户从邮件复制快递单号手动添加AfterShip mobile tracking APP的麻烦)。
要做到上面这些功能,最核心的问题是要解决鉴权问题。Gmail 如何确保 AfterShip 能有哪个用户的权限而允许读取哪个用户的邮件内容?当然,用户把 Gmail 账号密码告诉 AfterShip 是最简单。但是随之而来的问题:当用户修改密码?只想让 AfterShip 读取 Gamil 邮件但是不让发送邮件…?这些该如何控制?由此 OAuth2协议应运而生(这个协议的历史并不很遥远https://tools.ietf.org/html/draft-hammer-oauth2-00):独立了一层授权层。用户首先登Gmail授权层,然后选择授权哪部分权限给第三方,第三方再拿着用户的授权层给的令牌去资源服务器获取。果然,计算界中的任何问题,都可以通过加一个中间层来解决(ip地址不好记那就www.google.com;编码时0x0201=1麻烦就var int a=1…)
OAuth2 简介
为了解决各种场景,oauth2提供了好几种授权模式:
-
授权码模式(authorization code) ,功能最完整,流程最严密模式
-
简化模式,指对授权码模式的简化,比其少一个 code 换 token 步骤,其他完全一样
-
密码模式,用户把密码给客户端,客户端以自己的名义所要授权,这种通常用户对客户端高度信任的情况,比如客户端是操作系统的一部分
-
客户端模式,调用者是一个后端的模块,没有用户界面的时候,可以使用客户端模式。鉴权服务器直接对客户端进行身份验证,验证通过后,返回 token
-
更新令牌,严格意义上他不算事一种授权模式,只是对上面模式的一种补充,当上面模式中获取到的token(令牌过期后),自动用 refresh_token 换一个新的 token
这些 OAuth2 模式,并没有优缺点之分,是为了针对不同的场景提供的决方案。在这些模式中,授权码模式,因为功能最完整,流程最严密,大多数场景也都满足,因此成了使用最多的模式:用户首先授权,授权完后跳回三方开发者的地址并带回了authorization code,三方开发者凭借authorization code再次向授权成请求返回 token。
起初,在web领域跑的很好,没有问题。但是随后而来的是移动互联网的兴起,大量的移动app使用授权码模式,产生了问题:当用户授权完后,把 authorization code 附在redirect_uri 跳转然后拉起 APP,是可能被其他应用窃取的(假设 redirect_uri 是aftership://com.aftership.tracking?code=authroization_code),那么这个aftership:// scheme是可能被其他恶意app抢注的,造成 authorization code 被其他 APP 窃取。因此又提出了适用移动app的授权码模式 authorization code with pkce。
在 authorization code with pkce 中,客户端拼装授权链接时,随机生成一个字符串,我们叫他 code_verifier,然后对此随机字符串进行非对称加密(加密算法必须保持和服务器一致)得到一个字符串,我们叫他 code_challenge,然后在把 code_challenge 拼到授权连接上。服务端下发 authorization_code 后,同时记住了这个 authorization code 对应的 code_chanllenge,然后客户端根据 authorzation code 和 code_verifier 再次换取 access token,服务端会检验根据同样的算法对 code_verifier 进行计算得到 secret 和 authorization code 对应的code_challenge对比,以确定是否是被冒用。假设在获取 authorization code 时被恶意 APP 获取,但是恶意 APP 是没有 code_verifier 这个值。pkce 主要解决移动端的场景,如果是移动端 APP 应该优先选用此模式,防止 authorization code 被截取。
在整个授权码模式中,我们看到,用 code 换取 token 时,万事已经尘埃落定了。所以对接过程中,这里一般不会有问题。相反,在拼接授权链接时,这些参数控制着整个授权行为。整个拼接授权链接是最重要的地方。而且 OAuth2 并没有统一的规范,不同厂家支持程度不一,需要仔细阅读厂商的文档。
Google OAuth2 授权链接参数
https://developers.google.com/identity/protocols/oauth2/web-server#creatingclient
client_id |
必填 |
应用标识 |
redirect_uri |
必填 |
用户授权完成跳转地址(在get参数上会带回authorization code)。这个地址必须在google后台配置白名单才能被使用 |
scope |
必填 |
需要让用户授权的权限列表,多个权限用空格隔开(space-delimited) |
access_type |
建议填写 |
枚举值,online和offline,当为offline时才会返回refresh token,为online时不会返回refresh token |
state |
建议填写 |
透传字段,授权链接上指定的scope会在用户授权完成后跳转redirect_uri时带上此字段。用处很多,比如,防止cross-site攻击 |
include_granted_scopes |
可选 |
枚举值,true或者false。当为true时,表示增量授权,即当用户授权后拿到的access token同时拥有本次授权的scopes和之前曾经授权过的scopes |
login_hint |
可选 |
如果你知道你想要哪个google账号授权(如,重新授权时,我们期待他授权该账号时) |
prompt |
可选 |
当用户已经授权过给应用后,默认以后不再展示授权页面给用户,登陆后直接跳转redirect_uri。此项可控制是否弹出授权页面。多个控制用空格隔开(space-delimeted)且大小写敏感。可控制的值: consent每次让用户进入选择同意授权。 select_account弹出让用户选择使用哪个账号。 |
Goole refresh token有效期限https://developers.google.com/identity/protocols/oauth2#expiration
-
用户已经取消您的应用程序访问权限.
-
6个月没有使用过 refresh token 刷新 access token.
-
您要了 Gmail 的相关权限,用户修改密码
-
用户超过了他能授权的最大 refrsh token 数量限制
目前每个用户只能授权给每个应用最多50个 refresh token,如果超出,新创建 refresh token 时候会自动作废最老的一个 refresh token 并且不会弹出警告。
注意事项
-
按照 OAuth2 协议,使用 refresh token 刷新 access token 时,如果 refresh token 未过期,那么在返回 body 体消息是不会返回 refresh token 字段的,需要从get参数上(请求时用的) refresh token 取出.(见golang oauth2 sdk golang.org/x/oauth2/internal/token.go第226行 )
-
google oauth2中,在使用authorization code换取token时,仅在用户第一次授权时返回refresh token,以后不再返回(即使acceess_type=offline)。此时,我们可以在https://myaccount.google.com/permissions页面取消对应用的授权,那么再次授权就变成了第一次授权,会返回refresh token。另外,也可以在授权链接中prompt=consent每次让用户进入到授权确认页面,即可每次返回 refresh token
名词解释
-
authorization code,或者authorization_code,即授权码。有时简称code,指用户授权后,授权服务器临时生成的一个随机字符串,有效期通常在分钟级别内
-
token,即令牌。他是一组名词的总称。包括access_token,expires_in,refresh_token,而且token并不一定总是同时包括这些信息。
-
access_token,三方开发者请求资源服务器的凭证,通常有效期很短,仅几个小时之内。
-
expires_in,即(access_token)有效时间,单位通常是秒,如3600
-
refresh token,或者refresh_token,通常access_token的有效期非常短(几个小时之内),而refresh token的有效期基本上都是很久,比如google是6个月。三方开发者可以凭借此refresh token获取到新的token。
转载请注明:飞嗨 » OAuth2之authorization code