项目
版本

选择合适的流程

OpenIddict 提供了对 OAuth 2.0OpenID Connect 核心规范中定义的所有标准流程的内置支持:授权码流程隐式流程混合流程(基本上是前两个流程的结合)、资源所有者密码凭据授权客户端凭据授权

尽管不是 OpenIddict 特有的,但在实现自己的授权服务器时,为您的应用程序选择最佳的流程是一个 重要的先决条件 。以下是 OAuth 2.0/OpenID Connect 不同流程的快速概览:

非交互式流程

资源所有者密码凭据流程(不建议新应用使用)

直接借鉴了 基本认证,资源所有者密码凭据授权(简称 ROPC)概念上是 最简单的 OAuth 2.0 流程 :客户端应用程序向用户请求用户名/密码,将用户凭据(以及根据授权服务器定义的客户端认证策略,可能还包括其自身的客户端凭据)发送给授权服务器的令牌请求,并收到可以用来检索用户资源的访问令牌。

资源所有者密码流程

POST /connect/token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"bearer",
  "expires_in":3600
}
此流程 不受 OAuth 2.0 规范推荐 ,因为它是唯一一个用户密码直接暴露给客户端应用程序的授权类型,这违反了最小权限原则,并且对于不能被授权服务器完全信任的第三方客户端应用程序来说是不合适的

虽然流行且易于实现(因为它不需要任何重定向或同意表单,而且与交互式流程不同,不需要实现跨站请求伪造(XSRF)防护措施来防止会话固定攻击),在新应用中不推荐使用。相反,鼓励用户使用授权码流程,该流程不向客户端应用程序暴露密码,并且不限于密码认证。

客户端凭据授权(推荐用于机器到机器通信)

客户端凭据授权几乎与资源所有者密码凭据授权相同,除了它专门设计用于 客户端到服务器场景 (此流程中不涉及用户):客户端应用程序发送包含其凭据的令牌请求,并收到可用于查询自身资源的访问令牌。

客户端凭据流程

POST /connect/token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=s6BhdRkqt3&client_secret=gX1fBat3bV
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"bearer",
  "expires_in":3600
}
与资源所有者密码凭据授权不同,使用客户端凭据授权时 客户端认证不是可选项,并且 OpenIddict 总是会拒绝未认证的令牌请求这是 OAuth 2.0 规范的要求

这意味着 您无法在公共应用(如浏览器、移动或桌面应用)中使用客户端凭据授权,因为它们无法保密其凭据。

交互式流程

授权码流程(推荐新应用使用)

虽然授权码流程可能是最复杂的流程(因为它涉及 用户代理重定向和后端通信 ),但对于涉及最终用户的任何场景(无论他们是使用密码、PIN、智能卡还是外部提供商登录),它是推荐的流程。作为其复杂性的回报,当在服务器端应用中使用时,此流程有一个很大的优势:access_token 不会被用户代理拦截。

授权码流程基本上有两个步骤:授权请求/响应和令牌请求/响应。

授权码流程

  • 步骤 1:授权请求

    在此流程中,客户端应用程序始终通过生成包含强制性 response_type=code 参数、其 client_idredirect_uri 以及可选的 scopestate 参数的授权请求来启动认证过程,这些参数 允许传递自定义数据并有助于缓解 XSRF 攻击

    大多数情况下,客户端应用程序将简单地返回一个带有 Location 头的 302 响应以重定向用户代理到授权端点,但根据您使用的 OpenID Connect 客户端,也可能支持 POST 请求以允许发送大型授权请求。此功能 通常通过自动提交 HTML 表单实现

    HTTP/1.1 302 Found
    Location: server.example.com/authorize?response_type=code&client_id=s6BhdRkqt3&state=af0ifjsldkj&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    
    GET /connect/authorize?response_type=code&client_id=s6BhdRkqt3&state=af0ifjsldkj&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1
    Host: server.example.com
    

    身份提供者处理授权请求的方式取决于具体实现,但在大多数情况下,会显示同意表单询问用户是否同意与客户端应用程序共享其个人数据。

    同意表单

    当同意后,用户代理将携带 一个唯一且短寿命的令牌 ——称为 授权码 —— 重定向回客户端应用程序,客户端将能够用其交换访问令牌。

    HTTP/1.1 302 Found
    Location: client.example.org/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=af0ifjsldkj
    
    为防止 XSRF/会话固定攻击,客户端应用程序必须确保身份提供者返回的 state 参数与原始 state 相符,如果这两个值不匹配则停止处理授权响应。这通常是通过生成不可猜测的字符串和相应的关联 cookie 完成的
  • 步骤 2:令牌请求

    当客户端应用程序收到授权码时,必须立即通过发送 grant_type=authorization_code 的令牌请求将其兑换为访问令牌。

    为了帮助身份提供者 抵御假冒客户端攻击,原始的 redirect_uri 也必须发送。

    如果客户端应用程序是机密应用程序(即已分配客户端凭据的应用程序),则需要认证。

    POST /connect/token HTTP/1.1
    Host: server.example.com
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&client_id=s6BhdRkqt3&client_secret=gX1fBat3bV&scope=openid
    
    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache
    
    {
    "access_token":"2YotnFZFEjr1zCsicMWpAA",
    "token_type":"bearer",
    "expires_in":3600
    }
    

    为了增加安全性,可以指定额外的参数如 code_challengecode_challenge_method 来绑定授权端点返回的授权码到原始的授权请求。这种机制被称为 代码交换的证明密钥,并完全受 OpenIddict 支持。

隐式流程(不建议新应用使用)

隐式流程与授权码流程类似,只是没有令牌请求/响应步骤:访问令牌直接作为授权响应的一部分以 URI 片段形式(或使用 response_mode=form_post 时在请求表单中)返回给客户端应用程序。

隐式流程

GET /connect/authorize?response_type=token&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=openid&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj HTTP/1.1
Host: server.example.com
HTTP/1.1 302 Found
Location: client.example.org/cb#access_token=SlAV32hkKG&token_type=bearer&expires_in=3600&state=af0ifjsldkj
最初为浏览器应用设计,此流程本质上不如授权码流程安全,并且不支持 代码交换的证明密钥。因此,在新应用中它不被推荐使用。
为防止XSRF/会话固定攻击,客户端应用必须确保身份提供商返回的 state 参数与原始 state 值相对应,如果这两个值不匹配,则应停止处理授权响应。这通常通过生成一个不可猜测的字符串以及在本地存储中保存的相应值来实现

使用隐式流程时,客户端应用还必须确保访问令牌没有被颁发给其他应用,以防止 困惑副手攻击。在OpenID Connect中,这可以通过使用 response_type=id_token token 并检查 JWT 身份令牌中的 aud 声明来完成,该声明必须与客户端应用的 client_id 匹配或包含 client_id
在本文档中