免费注册,打造高效身份管理
博客/身份安全/干货 | 使用 OIDC 协议进行身份验证的工作原理
干货 | 使用 OIDC 协议进行身份验证的工作原理
Authing 官方2022.08.15阅读 1963

 

01

身份验证的本质

OIDC 全称是 OpenID Connect,是一个基于 OAuth 2.0 的认证 + 授权(OAuth 2.0 提供的能力)协议。

《身份云动态 | 认证和授权,都离不开的 OIDC 协议》一文中提到,OIDC 诞生的场景是 —— 被授权的主体不再是用户,而是「想要访问用户资源的第三者」,而颁发权限的主体也不再是管理员,而是用户自己。

OIDC 诞生的场景则是 ——  被授权的主体不再是用户,而是「想要访问用户资源的第三者」,而颁发权限的主体也不再是管理员,而是用户自己。

因此,想要了解 OIDC 协议,首先要厘清「认证」和「授权」的概念

认证

认证是决定一个主体(之后统称「用户」)究竟是谁的过程,换句话来说,是将「当前意图访问资源的用户」和「提前存储好的身份信息」对应起来的过程。显然,这个操作需要由身份信息的持有者来完成,我们称其为「IdP(Identity Provider)」,它存储的身份信息列表称为「用户目录」或「用户池」。

所谓的「身份信息」可以是任意格式,包含但不限于以下两种内容:用户的唯一标识符(可以是唯一的用户名、随机字符串、UUID 等),以及只有该用户才能提供,用来确认该用户身份的私密信息(密码、指纹等)。前者可以是公开的,但后者必须是私密的,只有 IdP 和用户自身才能持有。

为了完成认证,用户必须首先「宣称」自己是谁,并且这个宣称需要以某种形式和用户目录中的唯一一条记录产生关联。显然,最简单的办法就是直接向 IdP 宣布自己的唯一标识符。之后,用户需要通过某种保密的途径悄悄告诉 IdP 自己的私密信息,IdP 确认无误后,就可以将当前正在进行请求的用户和用户目录中的身份信息对应起来,如此一来,用户在 IdP 上的认证过程就完成了。

授权

确认了用户的身份之后,下一步就是确认用户到底有没有权限访问想要访问的资源了。拥有身份信息后,这一步就变得很简单了——只需根据对应资源的权限设置,检查用户的对应操作是否被允许即可。

 

 

02

身份和资源的分离

在最简单的身份模型中,身份持有者(IdP)和资源持有者运行在同一个上下文中。这意味着一旦 IdP 完成了某个用户的认证,资源持有者立刻就能知道(例如通过数据库查询)用户的身份信息。

之后用户访问资源时,资源持有者就能利用这一信息来决定是否允许用户的操作(相当于自己执行授权),或者把这一决定交给 IdP 来做(相当于 IdP 执行授权)。

这种模型的缺点显而易见——每个应用都要维护一套自己的用户目录,不同应用之间无法共享身份信息。为了解决这个问题,一个自然的想法就是将 IdP 独立出来,让所有资源持有者都从 IdP 处获取用户的身份

这种做法存在一个前提条件:当一个用户在 IdP 上完成了认证之后,资源持有者必须得知这一点,并能从 IdP 处获取用户对应的身份信息,而且这一渠道必须是可信的,身份信息不能被恶意篡改。在实际应用中,资源持有者一般会在用户访问资源时,向 IdP 获取用户的认证状态和身份信息。如果成功,之后的授权步骤就和上面一致了。

独立的 IdP 有一个天然的好处——只要用户在 IdP 上进行过了认证,其下关联的所有资源持有者都能获取到用户的身份信息,正所谓「一次认证,到处访问」。所谓的「单点登录(SSO)」本质上就是如此。

由于 Web 应用天生的无状态性,资源持有者并不能确定访问资源的用户和在 IdP 上认证过的用户是同一个,因此用户每次访问资源时都需要提供身份标识符和密码,由资源持有者向 IdP 进行确认。

为了解决这一问题,IdP 在完成认证时可以向用户颁发一个临时的「令牌(Token)」,这个 Token 存储着用户目录中的用户身份,只有用户本人才能持有,并且经过 IdP 的数字签名。

用户访问资源时,通过 Cookie 等手段自动向持有者提供 Token,持有者可以在本地利用签名验证 Token 的真实性和有效性,并且从 Token 中获取到用户的身份信息,无需再经过 IdP 了。为了防止 Token 从用户处泄露,它只在短时间内有效,失效后必须由 IdP 重新颁发。

最著名的 Token 技术是「JWT(Json Web Token)」,它也是 OIDC 协议的一部分。除此之外,SAML 协议中的「断言(Assertion)」也可以起到和 Token 相同的作用。

当然,用户的登录态也可以由资源提供者来维护,资源提供者在向 IdP 确认用户身份后自己向用户颁发一个类似的 Token,存放在用户 Cookie 中。此时的 Token 可以实际存储身份信息并进行签名,也可以作为一个索引的 Key,指向存放在资源提供者后端的,从 IdP 处获得的身份信息(也就是所谓的 Session)。

值得注意的是,在分离模型中,如果授权步骤由 IdP 执行,用户在 IdP 上进行授权时还需要提供自己想要访问的资源种类和执行的操作,IdP 签发 Token 时也需要将这些信息写在 Token 中,以便资源持有者核验。「资源」和「操作」的二元组被称为「Scope」,OIDC 登录时传人的其中一个参数就是它。

03

由用户决定的授权(OIDC 协议)

之前的讨论中存在一个隐含的假设——资源的所有权并不在用户手中,而是在外部的管理者手中,因此用户访问资源时才需要首先获取权限。然而在当今的互联网中,很大一部分信息都是用户产生的,用户理应拥有自己资源的完全控制权限。

在这种场景之下,「授权」的概念依旧存在,只不过被授权的主体不再是用户,而是「想要访问用户资源的第三者」,而颁发权限的主体也不再是管理员,而是用户自己。此时,这个「第三者」被称为「SP(Service Provider)」,也称为「Client」,而用户资源的存放处依然称为「资源提供者」。由于以下的讨论仅涉及授权而不涉及认证,为了方便起见,不妨假设「资源提供者」和「IdP」运行在同一上下文中,统称「IdP」,同时负责认证用户身份和提供资源。

举个例子,用户想在自己的微信中看到自己 Github 账号的状态,在这种情况下,微信是 SP ,Github 是 IdP,微信需要访问用户存储在 Github 下的资源(账号状态)。

由于微信和 Github 是两个互相不信任的应用,也因为用户有权控制其在 Github 下的资源,Github 不能在未取得用户许可的情况下,向微信提供任何用户资源。因此,微信首先需要指引用户前往 Github 进行认证和授权,这通常是用重定向用户浏览器来实现。

Github 会首先进行认证操作,确认用户的身份(显然只有用户本人才有权向第三者提供他的资源的访问权限)。身份验证通过后,Github 还会根据管理员配置的权限列表,确认用户本身拥有微信所请求资源的对应访问权限(因为请求的资源也可能不完全属于用户)。这一点也确认无误后,Github 就会弹出授权确认页面,向用户确认「是否授予微信对应权限」。用户确认完成后,Github 就会签发一个 Token,由用户转交给微信,微信方只需向 Github 提供这个 Token 就可以访问之前请求的资源了。

作为标准身份协议之一的 OIDC 正是为此种场景而生。OIDC 的认证和授权分为四种模式:授权码模式(Code)、隐式模式(Implicit)、密码模式(Password)、客户端证书模式(Client Credential)。

授权码模式

授权码模式是最为规范的模式。它的主要步骤如下:

1.SP 将客户端重定向到 IdP 的 OIDC 授权地址,附上自己请求的权限(Scope)和一个回调地址

2.IdP 在自己的页面上完成认证,并由用户确认权限

3.IdP 通过客户端向 SP 的回调地址发送一个授权码(Code)

4.SP 的后端向 IdP 的另一个接口发出含有 Code 的请求,得到 IdP 颁发的 Token

授权码模式的优点在于 Token 不由用户,而是由 SP 持有,降低了意外泄露带来的风险。值得注意的是,用户在 IdP 上进行认证和授权的过程是 IdP 自行规定的,和 OIDC 协议无关。在 OIDC 协议中,IdP 颁发的 Token 是 JWT。

隐式模式

隐式模式可以看作省略了 Code 的授权码模式。它的主要步骤如下:

1.SP 将客户端重定向到 IdP 的 OIDC 授权地址,附上自己请求的权限(Scope)和一个回调地址

2.IdP 在自己的页面上完成认证,并由用户确认权限

3.IdP 通过客户端向 SP 的回调地址直接发送 Token

可以看到,此时的 Token 经过了用户的客户端,容易被中间人窃取。由于访问用户资源的是 SP 而不是用户本人,只让 SP 持有 Token 永远是更为正确的选择。

密码模式

密码模式在隐式模式的基础上进一步省略了 IdP 上的授权过程。它的主要步骤如下:

1.SP 在自己的页面上请求用户输入在 IdP 上的用户名和密码

2.SP 向 IdP 的 OIDC 授权地址发起请求,附上用户输入的帐密

3.IdP 确认帐密的正确性,然后在响应中直接发送 Token

密码模式只应用在 IdP 和 SP 互相信任,或者 SP 由用户本人控制(例如移动端或桌面应用)的情况下,因为用户的帐密经过了 SP,只要 SP 愿意,完全可以通过用户的帐密在 IdP 上无限制访问用户的全部资源。也正因如此,密码模式中让用户确认权限是没有意义的,SP 获取的一定是用户资源的完全访问权限。

客户端证书模式

客户端证书模式和用户无关,只用于在 IdP 上授权特定 SP 访问特定资源。它的主要步骤如下:

1.SP 提前在 IdP 上注册一个证书(一般是账号 + 密码)

2.SP 向 IdP 的 OIDC 授权地址发起请求,附上自己的证书

3.IdP 确认证书的正确性,然后在响应中直接发送 Token

显然,客户端证书模式也只应用于 IdP 和 SP 互相信任,或是 SP 只访问用户无关资源的情况下。由于没有征得用户的同意,如非必要,IdP 不应授权 SP 访问用户的私密信息。

 

关于 Authing

Authing 是国内首款以开发者为中心的全场景身份云产品,集成了 OIDC 等所有主流身份认证协议,为企业和开发者提供完善安全的用户认证和访问管理服务。作为云原生架构下的身份云产品,Authing 在产品创建初期,目标就是服务亿级的企业和个人开发者客户,轻量级、易部署、低消耗、技术栈成熟,运维易的云原生技术产品架构,成为了 Authing 的首选。

 

点击此处了解更多行业身份管理

「解决方案」以及「最佳实践案例」

 

更多技术干货,在 Authing 技术博客搜索“身份云动态”,将身份云知识一网打尽。

文章作者

avatar

Authing 官方

0

文章总数

authing blog rqcode
关注 Authing 公众号
随时随地发现更多内容
authing blog rqcode
添加 Authing 小助手
加入 Authing 开发者大家庭