跳到主内容

OAuth 2.0

Casdoor会颁发访问令牌以对客户端进行身份验证。 本页面介绍如何通过API获取令牌、验证令牌并使用它。 或者,使用Casdoor SDK来处理该流程。

支持的授权类型:

授权类型RFC用例
授权码RFC 6749 §4.1默认;带有后端的Web/移动应用。 默认启用。
t隐式RFC 6749 §4.2仅前端应用,无后端。
资源所有者密码RFC 6749 §4.3无前端重定向的应用程序;用户凭据直接发送。
客户端凭证RFC 6749 §4.4服务间调用,无需用户参与。
刷新令牌RFC 6749 §6无需重新认证即可续订访问令牌。
设备授权RFC 8628输入受限或无浏览器的设备
令牌交换RFC 8693用现有令牌交换具有不同范围或受众的令牌。
JWT持有者RFC 7523使用签名的JWT断言而非客户端密钥进行服务身份验证。

在应用程序编辑页面上启用非默认授权类型。

OAuth授权类型

Authorization code grant

将用户重定向至:

https://<CASDOOR_HOST>/login/oauth/authorize?
client_id=CLIENT_ID&
redirect_uri=REDIRECT_URI&
response_type=code&
scope=openid&
state=STATE

作用域

作用域描述
openid(默认)sub, iss, aud
profilename、displayName、avatar
email电子邮件地址
address地址(OIDC 对象在JWT 标准中;请参阅OIDC 地址声明)
phone电话号码

:::信息 授权 URL 中的请求范围 用``%20:

https://<CASDOOR_HOST>/login/oauth/authorize?
client_id=...&
scope=openid%20email

请参阅OIDC 规范以了解详情。 :::

用户登录后,Casdoor 将重定向至:

https://REDIRECT_URI?code=CODE&state=STATE

使用 POST 将代码兑换为令牌:

https://<CASDOOR_HOST>/api/login/oauth/access_token

请求体:

{
"grant_type": "authorization_code",
"client_id": ClientId,
"client_secret": ClientSecret,
"code": Code,
}

示例响应:

{
"access_token": "eyJhb...",
"id_token": "eyJhb...",
"refresh_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 10080,
"scope": "openid"
}
备注

Casdoor支持PKCE(代码交换证明密钥),以增强安全性。 要启用PKCE,请在请求授权码时添加两个参数:

&code_challenge_method=S256&code_challenge=YOUR_CHALLENGE

代码挑战应为随机生成的代码验证器的Base64-URL编码SHA-256哈希值(长度为43至128个字符)。 请求令牌时,请包含原始code_verifier参数。 启用 PKCE 后,``client_secret变为可选,但如果提供,则必须正确。

对于在Casdoor中配置的OAuth提供商(如Twitter以及启用了PKCE的自定义提供商),Casdoor会为每个认证流程自动生成唯一的代码验证器,因此您无需手动实现PKCE。

将令牌绑定到特定服务

当您的应用程序需要调用多个后端服务时,您可能希望使用明确绑定到特定服务的令牌。 这可防止出现安全问题,即本应用于某项服务的令牌意外地被用于另一项服务。

Casdoor 支持 RFC 8707 资源指示符,允许您在请求授权时指定预期的服务。 添加``资源参数,其中包含用于标识您的服务的绝对URI:

https://<CASDOOR_HOST>/login/oauth/authorize?
client_id=CLIENT_ID&
redirect_uri=REDIRECT_URI&
response_type=code&
scope=openid&
state=STATE&
resource=https://api.example.com

When you exchange the authorization code for tokens, include the same resource parameter:

{
"grant_type": "authorization_code",
"client_id": ClientId,
"client_secret": ClientSecret,
"code": Code,
"resource": "https://api.example.com"
}

生成的访问令牌将把aud(受众)声明设置为您的资源 URI,而非客户端 ID。 您的后端服务随后可以通过检查受众声明来验证令牌是否专门为其颁发。 资源必须在授权请求和令牌请求之间完全匹配。

资源参数在基于浏览器的登录流程中得以保留。 如果用户需要完成交互式登录(例如输入密码、MFA 或 WebAuthn),该参数将贯穿整个重定向链,并在颁发授权码时一并包含。

provider_hint参数

要跳过Casdoor登录页面,直接将用户发送到特定的OAuth提供商,请在授权URL中添加provider_hint=<provider-name>

https://<CASDOOR_HOST>/login/oauth/authorize?
client_id=CLIENT_ID&
redirect_uri=REDIRECT_URI&
response_type=code&
scope=openid&
state=STATE&
provider_hint=github

Casdoor 提供一个轻量级的重定向页面(无需加载完整的 React 应用),可立即将用户引导至该提供商的 OAuth 流程。 这可缩短在设备资源受限或网络连接较慢时的重定向时间。

带有 OAuth 的注册流程

当用户通过 OAuth 授权流程注册时,他们会自动被重定向到您的应用程序的回调 URL,并附带授权代码,这与登录流程完全相同。 此前,用户在创建账户后必须手动点击进入中间页面。 现在,注册流程与登录的简化体验保持一致——注册完成后,Casdoor会立即生成授权码并重定向到您的``redirect_uri.

<b您的应用程序无需任何更改即可支持此功能。 在 OAuth 授权过程中,当用户选择创建新账户时,授权参数(client_id、response_type、redirect_uri 等)会自动通过注册流程传递。

隐式授权

对于没有后端的应用程序,请使用隐式授权. 在应用程序中启用它,然后将用户重定向到:

:::警告

隐式授权令牌端点需要有效的用户名和密码。 纯 OAuth 用户(仅通过第三方提供商创建的账户,未设置本地密码)无法使用此流程,并将收到``invalid_grant. 对社交登录用户使用授权码流程。

:::

https://<CASDOOR_HOST>/login/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=token&scope=openid&state=STATE

当您的用户通过 Casdoor 身份验证后,他会被 Casdoor 重定到:

https://REDIRECT_URI/#access_token=ACCESS_TOKEN

Casdoor也支持id_token作为response_type,这是OpenID的一个特性。

设备授权

对于输入受限或无浏览器的设备,请使用设备授权. 在应用程序中启用它,请求来自OIDC发现的device_authorization_endpoint,然后显示verification_uri(例如通过二维码或文本),以便用户完成登录。

Casdoor provides a built-in device login page at the verification_uri where the user enters the user code and signs in — no custom UI is required. Device login can also be enabled as a sign-in method on the application's Signin methods table.

其次,您应请求令牌端点以获取访问令牌,并使用rfc8628中定义的参数。

使用资源拥有者的密码凭据授权

如果您的应用程序没有前端来重定向用户到Casdoor,那么您可能需要这个功能。

在应用程序上启用密码凭据授权,然后向以下地址发送 POST 请求:

https://<CASDOOR_HOST>/api/login/oauth/access_token
{
"grant_type": "password",
"client_id": ClientId,
"client_secret": ClientSecret,
"username": Username,
"password": Password,
}

示例响应:

{
"access_token": "eyJhb...",
"id_token": "eyJhb...",
"refresh_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 10080,
"scope": "openid"
}

使用客户端凭据授权

当应用程序没有前端时,请使用客户端凭据授权。

在应用程序上启用客户端凭证授权,并发送 POST 请求至https://<CASDOOR_HOST>/api/login/oauth/access_token:

{
"grant_type": "client_credentials",
"client_id": ClientId,
"client_secret": ClientSecret,
}

示例响应:

{
"access_token": "eyJhb...",
"id_token": "eyJhb...",
"refresh_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 10080,
"scope": "openid"
}

必须指出,以这种方式获得的AccessToken 不同于前三个,因为它与应用程序相对应,而不是与用户相对应。

刷新令牌

要刷新访问令牌,请使用上面获取的``refreshToken。

在应用程序中设置刷新令牌的过期时间(默认为0小时),然后向https://<CASDOOR_HOST>发送POST请求/api/login/oauth/refresh_token

{
"grant_type": "refresh_token",
"refresh_token": REFRESH_TOKEN,
"scope": SCOPE,
"client_id": ClientId,
"client_secret": ClientSecret,
}

示例响应:

{
"access_token": "eyJhb...",
"id_token": "eyJhb...",
"refresh_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 10080,
"scope": "openid"
}

令牌交换授权

令牌交换(RFC 8693)可让您用现有令牌兑换具有不同特性的新令牌——这在以下情况下尤为有用:当一项服务需要代表用户调用另一项服务,或为特定下游服务缩小令牌的作用范围时。

要交换令牌,请向以下地址发送POST请求https://<CASDOOR_HOST>/api/login/oauth/access_token:

{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"client_id": ClientId,
"client_secret": ClientSecret,
"subject_token": SubjectToken,
"subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
"scope": "openid email"
}

主体令牌是您想要交换的令牌——通常是您已拥有的访问令牌或 JWT。 如果您想缩小新令牌的权限,请指定一个``作用域,该作用域是原始令牌作用域的子集。 当你省略scope时,新令牌将继承与主体令牌相同的范围。

Casdoor 支持三种令牌类型用于subject_token_type:

  • urn:ietf:params:oauth:token-type:access_token (default)
  • urn:ietf:params:oauth:token-type:jwt
  • urn:ietf:params:oauth:token-type:id_token

该响应会返回一个与您的主体令牌关联的全新令牌:

{
"access_token": "eyJhb...",
"id_token": "eyJhb...",
"refresh_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 10080,
"scope": "openid email"
}

例如,API网关可能会在将请求转发给下游微服务之前,用范围更广的访问令牌换取范围更窄的访问令牌。 这种模式称为范围向下作用域,可确保每个服务仅获得其所需的权限,而不会继承原始令牌的全部访问权限。

**受众绑定:**Casdoor 使用其颁发应用程序的证书(由声明标识)来验证azpsubject_token<code>azp,而非请求客户端的证书。 如果请求客户端与令牌颁发者不同,则该令牌的aud</code>声明必须包含请求客户端的 ID;否则,交换将被拒绝,并返回invalid_grant. 这可防止某个应用程序在未获得明确受众授权的情况下交换另一个应用程序的令牌(RFC 8693 §2.1)。

JWT Bearer 授权

JWT 持有者(RFC 7523)允许客户端通过呈现经过签名的 JWT 断言来获取访问令牌,而非使用客户端密钥。 这对于服务间调用非常有用,其中客户端持有私钥,并希望在不共享长期密钥的情况下进行身份验证。

在应用程序上启用JWT 持有者,然后在“安全”选项卡下的“客户端证书”字段中上传客户端的证书。 Casdoor 使用该证书中的公钥来验证 JWT 断言。

https://<CASDOOR_HOST>/api/login/oauth/access_token:

{
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": "<signed-JWT>",
"client_id": "CLIENT_ID"
}

JWT 断言(client_assertion) 必须使用客户端的私钥进行签名,并包含:

ClaimDescription
issIssuer — the client_id of the application
subSubject — the client_id of the application
aud受众 — Casdoor 令牌端点 URL
exp过期时间(Unix 时间戳)

Casdoor 会使用应用程序的客户端证书中的公钥对签名进行验证,检查标准 JWT 声明;如果验证通过,则返回与该应用程序关联的访问令牌(行为与客户端凭据授权相同)。

示例响应:

{
"access_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 10080,
"scope": "openid"
}

DPoP (sender-constrained tokens)

Casdoor supports DPoP (Demonstrating Proof of Possession, RFC 9449), which binds an access token to a client-held key so that a leaked token cannot be used without the matching private key.

To request a DPoP-bound token, send a DPoP header containing a DPoP proof JWT with your token request:

POST /api/login/oauth/access_token
DPoP: <DPoP proof JWT>

When a valid proof is supplied, Casdoor binds the issued token to the proof's public key (its JWK thumbprint, jkt) and returns token_type as DPoP instead of Bearer:

{
"access_token": "eyJhb...",
"token_type": "DPoP",
"expires_in": 10080,
"scope": "openid"
}

The same DPoP header is also accepted on the refresh token request. An invalid proof is rejected with the invalid_dpop_proof error.

The supported proof signing algorithms are advertised in the OpenID Connect discovery document (/.well-known/openid-configuration) under dpop_signing_alg_values_supported:

{
"dpop_signing_alg_values_supported": ["RS256", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512"]
}

The token introspection response also exposes the key binding through the cnf.jkt claim (RFC 9449 §8).

如何验证访问令牌

Casdoor 目前支持 令牌自省 端点。 此端点受基本身份验证保护(ClientId:ClientSecret)。

POST /api/login/oauth/introspect HTTP/1.1
Host: CASDOOR_HOST
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=

token=ACCESS_TOKEN&token_type_hint=access_token

示例响应:

{
"active": true,
"client_id": "c58c...",
"username": "admin",
"token_type": "Bearer",
"exp": 1647138242,
"iat": 1646533442,
"nbf": 1646533442,
"sub": "7a6b4a8a-b731-48da-bc44-36ae27338817",
"aud": [
"c58c..."
],
"iss": "http://localhost:8000"
}

如何使用AccessToken

使用访问令牌调用需要身份验证的Casdoor API。

例如,有两种不同的方式来请求/api/userinfo

类型1:查询参数

https://CASDOOR_HOST/api/userinfo?accessToken=your_access_token

类型2:HTTP Bearer 令牌

https://CASDOOR_HOST/api/userinfo</code>并附带以下头部信息:“Authorization: Beareryour_access_token"

Casdoor将解析access_token,并根据scope返回相应的用户信息。 响应的格式相同:

{
"sub": "7a6b4a8a-b731-48da-bc44-36ae27338817",
"iss": "http://localhost:8000",
"aud": "c58c..."
}

如果您需要更多用户信息,请在步骤授权码授权中获取AccessToken时添加作用域.

访问 OAuth 提供商令牌

当用户通过 OAuth 提供商(GitHub、Google 等)登录时,提供商的访问令牌可用于代表用户调用第三方 API;该令牌存储在用户的``originalToken字段中。

The token is available through the /api/get-account endpoint:

{
"status": "ok",
"data": {
"name": "user123",
"originalToken": "ya29.a0AfH6SMBx...",
...
}

原始Token仅在用户请求自己的账户或请求者为管理员时可见。 对于其他请求,出于隐私考虑,它会被屏蔽。

这使您的应用程序能够使用提供商的访问令牌与第三方API(例如GitHub API、Google Drive API)进行交互,而无需额外的OAuth流程。

userinfoget-account APIs之间的差异

  • /api/userinfo:此API作为OIDC协议的一部分返回用户信息。 它提供有限的信息,包括仅在OIDC标准中定义的基本信息。 有关 Casdoor 支持的可用范围列表,请参阅范围部分。

  • /api/get-account:此API用于检索当前登录账户的用户对象。 这是一个 Casdoor 特有的 API,可让您获取 Casdoor 中用户的所有信息,包括适用情况下的 OAuth 提供商的访问令牌。