基本原理
首先要明确OAuth OpenID Connect学习起来比较难,对谁都会比较难,所以心态要放好,因为有很多专业属于、缩写等等,你要是之前不知道,就基本上很难看懂。而且OAuth和OpenID Connect不像HTTP这样的协议,有固定的格式,OAuth和OpenID Connect其实没有很固定的格式,没有人告诉你,一定要怎么做。
所以本文都是大白话,让大家更容易看懂。
为什么要用OAuth 和 OIDC来做授权和身份验证?
原来,是怎么进行身份验证的?
简单的表格登录验证模式,密码哈希存在数据库里面,用户输入用户名密码,找后台数据库进行比对,从而进行身份验证。
但是这种模式非常不好,不好的原因有两个:
- 不安全:需要自己去考量安全技术、更上最新的安全算法
- 成本高:需要自己维护验证的流程
所以OAuth和OpenID Connect,就是来取代这种传统简单的验证方式,成为行业身份验证的最佳实践。
历史
2006/2007 没有智能手机,当时的Web App也会面临很多的登录问题,十年前用的是简单登录(表格和cookie的模式),采用SAML进行跨站点SSO(SAML其实比OAuth还难懂,更晦涩)
所以,对于手机端的应用程序,你可以登录进行进行使用,但是如果你关掉应用程序,如何保持用户的登录状态,cookie又不能支持手机应用,所以如何来解决这个问题呢?
其次,另外一个问题是如何进行委派授权呢?为什么要进行委派授权,因为由于app越来越多,有些app开发不一定很好,如果你在上门注册你的用户名和密码在他们的数据库里面,这样会造成安全风险,因为你把密码给了一个不守信的应用。
所以,总得来说OAuth和OpenID Connect就是想实现,不给这些三流应用程序密码,而是让三流应用程序去找大厂的应用程序进行身份验证,这样密码只给大厂应用,比如微信微博,然后微信微博就充当一个身份标识提供方
所以,委派授权让应用可以获得你所允许的用户资料内容,比如你允许一个三方应用程序访问你的微信头像、昵称,而你的生日、年龄则不被获得。
举个反面例子:
yelp.com Web应用如上图。
这里我们输入了Gmail的用户名和密码,注意这里的密码不是yelp.com的密码,而是你自己邮箱的密码,yelp.com获得了你Gmail的用户名密码,这样的话,yelp就可以直接登录你的私人邮箱,这样是非常不安全的。所以这是一个非常糟糕的身份验证方式。
所以,怎么来解决这样的问题呢?我们也不可能每个应用都用不同的用户名和密码。
但是,就像之前所说,虽然我们不信任yelp,但是我们信任Gmail,如果验证发生在Gmail那边,而是yelp,Gmail只需要告诉yelp,我是我,我能够进行访问,这样就完美了。
所以,就形成了OAuth 2.0。
如上图,是经典的OAuth flow,yelp提示可以通过google来进行登录,然后页面就会跳转道google.com,然后我在google.com的页面上进行用户名和密码登录,这样身份验证的密码其实是输在了google这端,而不是yelp这个我们不认识的应用程序,这样显然就安全多了。登录了谷歌之后,google就会问用户,“你允许yelp去访问你的公开资料和联系人吗?”,如果你点yes,那么google来进行身份验证的流程就走完了,然后就又跳转到yelp.com/callback。然后,yelp就可以去找google.com的资源API请求所需的资源(公开资料和联系人)。
所以,验证是由google来完成的,此外google还给了yelp一些用户信息。这是由OAuth主要完成的功能。
OAuth专业术语
- resource owner:资源所有者,就是用户这个活人,在google上创建自己的用户名和资料,是这些资料资源的所有者,在问是否可以访问你的资料的时候,来点击yes。
- Client:客户端,yelp
- Authorization Server:授权服务器,account.google.com,来进行授权验证,是否让client能够访问用户资料。Authorization server 来说yes or no,来让yelp客户端来或者用户资料。
- Resource Server:存放用户资源的服务器。
一般authorization server 和 resource server 是一台服务器,但是也可以是分开的两台服务器,如果验证是由第三方系统来进行的,那么这种一般就是分开的两台服务器 - Authorization grant:“你是否允许yelp来访问你的用户资料?”就是这句话,如果你点了yes,你就进行了authorization grant,you say consent to allow yelp to access your google profile.
- Redirect URL: 一般callback或者redirect URL,跳转回到哪个页面
- Assess token:用访问令牌去找resource server来换取资料。(OAuth最关键的东西)
OAuth 验证细节
- 客户端Client点击第三方验证程序,去找google.com authorization server去进行授权验证,redirect到google的时候,也会携带很多信息,比如Redirect URI(在google完成授权验证之后,再跳回到哪个界面,这个在最开始的时候就要定义好),response type:code(什么样的authorization grant type,“你是够允许yelp来访问你的google资料?”你说完yes之后,google会给你一个authorization code,代表你被允许了。这里要定义好,你想要什么样的授权反馈模式)
- 在Google的页面进行了用户名和密码的登录
- “你是够允许yelp来访问你的google资料?”你说完yes之后,google会给你一个authorization code,代表你被允许了。基于之前的response type,google的授权服务器会给出一个授权code。
- 重定向跳转到yelp.com,with authorization code
- yelp.com拿着authorization code去找Google的授权服务器去换一个access token。
- 然后yelp.com在授权服务器那边就有了个access token,这样有了access token,就可以去找resource server请求Google 的用户资料。
这样整个流程里面:用户名和密码是由google来进行验证的,而且yelp也可以直接或者用户的资料。这样安全又方便。
下一个问题
这样yelp就可以访问Google的用户资料,但是我们并不想yelp可以访问Google的所有用户资料,比如,我只想让yelp获得我的昵称和性别,但是不能获得我生日。
所以这样细颗粒度的访问,是需要控制的。
所以,这里又引入了两个术语:
Scope:可以访问哪些资源,哪些不能访问,哪些只读,哪些可编辑
Consent:用户是否同意允许yelp去访问昵称和性别,用户say yes,就是给出了consent,但是这个consent是由scope的,昵称和性别可以访问,但是生日不可以访问。
如下图,第一次请求有附scope
问题:为什么有了authentication code,还需要access token???
首先讲两个概念:
- back channel (Highly secure channel)
后端server之间的通信,加密严格,不好别窃取。 - Front channel (less secure channel)
web app里面如果存放了code token,通过web资源,就可以之间看到这些数据,所以不安全。
什么是可信设备:后端我们自己的服务器。
什么是不可信设备:前端的浏览器。
所以所有的流程图,实线代表front channel流程,虚线代表back channel流程。
所以,authentication token可以存在于front channel,在query 里面附有,存在浏览器里面,这个code被看到无所谓,因为如果偷走了这个code,也无法像authorization server请求access token,因为小偷没有secert key,而且access token只存在在后端服务器里面,不会存在浏览器。所以,即使偷走了code,也无法换取拿走access token。
认证流的开始
- client ID(注意这里是client ID,不是user 的ID,所以后面会讲,为什么要引入OpenID Connect,来解决user的身份验证。)
- redirect URL
- scope
- response type
- state
以上两图有Application ID 和Application Secret,可以叫client ID 和Client Secret
这两个是google和facebook平台上开发app的界面,App ID 和App Secret是用来告诉authentication server,我是我,我是yelp client。
App ID是可以放在前端浏览器通信里面的,只是证明我是这个app,不需要保密。
但是,app secret必须要保密,因为app secret放在后端服务器,用来交换token的,app secret在Azure AD B2C创建custom policy的时候,会用RSA 来生产一个密钥签名。
Link: https://docs.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-get-started#add-signing-and-encryption-keys
由上图所示,当user click yes to the question"你是否允许yelp去访问你Google的资料?"
那么authentication server就会返回一个code 进行回调,此时的state状态就是foobar,如果user,click no,那么就会报错,access_denied.
authentication code是在重定向URL里面,如下图所示:
那么有了authentication code,怎么拿code去换access token?
client 拿着code,client ID,client secret,grant type来去找authentication server 换access token。
注意这里有client secret(再继续看看拿来干嘛的????)
然后authorization server 会返回access token,并且附上有效实践,token 的种类为bearer。access token 也叫bearer token。
那么access token的request长什么样子?
如下入所示,URL会如下,有资源api,附上access toke,要access token的种类为bearer。
然后,有了token API会进行token验证,是否在有效期内,时候被篡改等等,然后基于token,将scope范围内的资料返回给client
OAuth Flow种类
- authorization code,最经典的模式,用code来换token,这个最安全。
- implicit,如果没有后端服务器,没有back channel,比如static java script web page,single web page,那么可不可以直接给我access token,我不要code。
后端服务器之间的通信,后端一个服务器有个凭据,拿去找另外一个后端服务器要access token,不会涉及任何浏览器,这两个不常见
- resource owner password credentials (back channel only)为了向前兼容的,不建议用于新开发的应用程序
- client credentials (back channel only) machine 和machine或者services之间的通信
Implicit Flow
web app mobile app一般都有后端服务器,这是一般采用authentication code的模式,但是比如single page web,没有后端服务器,这种情况下就要用implicit flow,不需要去找后端要code,直接发access token。
所以,如下图所示,reponse type是token的模式。然后去找resource owner去要consent,决定是否能够访问你的用户资料。
这个模式不安全,但是也就只能这种方法了,对于Single page app。
OAuth 2.0 非常管用,但是还是差点什么,所以引入OpenID Connect
2009年左右,第一个第三方facebook登录第一次出现。
OAuth 可以用来做简单身份 验证
OAuth也可以做跨站点SSO 验证
手机端的应用程序也可以用来OAuth来登录 验证
但是OAuth最关键的还是用户委派授权,用googel来委派授权yelp能够获得部分的用户资料 授权
总的来说:OAuth的Auth主要是authorization 而不是authentication,OAuth主要设计是用来进行授权的,而不是User身份验证。
这是为什么呢?
OAuth用的是client ID 而不是User ID来进行请求。
其次,如果用OAuth来进行身份验证的话,不是个明确的选择,因为OAuth没有一个标准的方法来搜集user用户本身的资料,比如说,你想登录yelp,作为应用yelp的商业角度来说,最好的情况是yelp直接从user那里或者邮箱地址啊,姓名啊,年纪啊等等信息,但是OAuth的协议流程里面并没有针对user信息本身,而是针对于授权,scope,给出client什么信息,OAuth其实并不在意是哪个user,你是谁。
所以,如果把OAuth专门用来做身份验证,就会出现很多问题,没有第三方登录验证应用程序,没有一个标准的方式来获得用户信息,而且每个获得信息的方式也不一样,而且应用获得的scope都不一样。
所以这里就引入了OpenID Connect
因为,OAuth主要用在授权,缺失了身份验证的功能,所以,OpenID Connect就是用来弥补OAuth这个身份验证空白的。
其实OpenID Connect不太能够视为一个单独的验证协议,因为OpenID Connect需要和OAuth一起使用,底层授权用OAuth,上一层的身份验证用OpenID Connect。
相当于OAuth的扩展。
OpenID Connect添加了什么?
- ID token (user的信息)
- 如果需要更多的用户信息,可以请求userinfo endpoint
- 有一个标准的scope
- 部署方式标准化
OpenID Connect的流程图
OpenID Connect和OAuth从技术上来讲,哪里有什么不一样呢?就在第一步向授权服务器进行请求时,会请求openid profile的scope。
access token和ID token一起获得,access token可以向resource server要profile,ID token可以证明是哪个用户进行了登录,但是这个ID token关于user的信息有限,如果cliet还想要更多关于user的信息,可以调用/userinfo API。
ID token 长什么样?
Jason Web Token (JWT)
红色是header,蓝色是用户信息,email 姓名 有效期等等。
绿色是签名,为了防篡改,防抵赖。
如果Client 有了ID token, JWT的防篡改 防抵赖性质,让client不需要再找authorization server再次进行身份验证,就能直接用ID token来进行身份验证。
如果ID Token的用户信息不够,那么可以调用userinfo endpoint来获取更多的用户信息
用access token来访问userinfo API,去要更多的用户信息,比如想要用户的头像照片信息。
所以OAuth 2.0 主要是用来进行授权,OpenID Connect主要进行验证。