# 使用 OIDC 授权

结合 Authing 实现 OIDC 授权的方法。

# 术语

  1. End-User:终端用户,也可以理解为使用您软件的人
  2. RP(Relying-Party):服务器后端
  3. OP(OIDC Provider): Authing 服务器

P.S. 文档中出现的 testapp.authing.cnexample.authing.cn 两个域名是可以在控制台配置的二级域名。

# 在 Authing 中创建一个用户池

使用 OIDC 需要先注册一个 Authing 账号 (opens new window)新建一个用户池

# 创建应用

注册完 Authing 账号并创建一个用户池之后,需要创建一个应用,然后你就可以使用 OIDC 协议完成用户登录和信息授权,创建应用请参考:

创建 OIDC 应用

# OIDC 授权登录的基本流程

  1. 用户访问授权链接进行登录;
  2. 登录成功后回调到开发者配置好的 redirect_uri 中并在 URL query 附带相关参数;
  3. 如果返回类型是 code,那么开发者需要在后端使用 code 和 secret(用户创建完 OIDC 应用后会得到)换取 access_token;
  4. 如果返回类型是 id_token token,那么在用户登录成功后的回调 URI 中会直接附带 id_token 和 access_token;
  5. 在后端使用 access_token 可以换取用户信息(userInfo),完成身份认证;

如果你想直观的体验 OIDC 认证流程,请点击这里查看我们提供的示例 (opens new window)点击这里可视化的理解 OIDC (opens new window)

如果你对如何在后端处理 OIDC 有困惑,请参考 Github 上的示例代码:oidc-demo (opens new window)

# 使用授权码模式(Authorization Code Flow)

授权码模式是 OIDC 授权登录中最常用的模式,OP 服务器返回一个授权码 code 给开发者后端服务器,在后端完成 code 换取 access_token,再用 access_token 换取用户信息的操作,从而实现用户的身份认证。

# 01 - 在控制台配置 OIDC 应用

进入控制台 > 应用 > 应用列表,找到你的应用,点击「配置」,在应用配置选项卡,找到授权模式,打开 authorization_code 模式,并在下方返回类型处选择 code 返回类型。

配置 OIDC 应用

# 02 - 发起登录请求

GET
https://<你的应用域名>.authing.cn/oidc/auth

拼接一个链接并让终端用户在浏览器中访问,发起 OIDC 授权登录请求。

发起授权需要拼接一个用来授权的 URL,并让终端用户在浏览器中访问,具体参数如下:

Query Parameters
client_id
REQUIRED
string

应用 ID。

redirect_uri
REQUIRED
string

回调链接,用户在 OP 认证成功后,OP 会将授权码以 URL query 的形式发送到这个地址。这个值必须出现在控制台配置的回调地址中,否则 OP 不允许向该地址回调。

scope
REQUIRED
string

需要请求的权限,必须包含 openid。如果需要获取手机号email 需要包含 phone email;如果需要 refresh_token 需要包含 offline_access。多个 scope 请用空格分隔id_token 解码后的内容中会包含这些 scope 对应的用户信息相关的字段。

response_type
REQUIRED
string

返回类型,可以为 code, id_token, id_token token, code id_token, code token, code id_token token。登录成功后,指定 OP 要返回哪些信息,如果包含 code,OP 会返回授权码 code,如果包含 id_token OP 会返回用户的 id_token,如果包含 token,OP 会返回用户的 access_token。

prompt
OPTIONAL
string

可以为 none,login,consent 或 select_account,指定 OP 与 End-User 的交互方式,如需 refresh_token必须为 consent

state
REQUIRED
string

一个随机字符串,用于防范 CSRF 攻击,如果 response 中的 state 值和发送请求之前设置的 state 值不同,说明受到攻击。

nonce
OPTIONAL
string

一个随机字符串,用于防范 Replay 攻击。

请求示例:

https://<你的应用域名>.authing.cn/oidc/auth?client_id=5c9b079883e333d55a101082&redirect_uri=https://www.example.cn/example&scope=openid profile&response_type=code&state=52378542395

如需后续刷新 access_token,请按照以下方式拼接登录链接

带刷新 token 功能的登录请求示例:

https://<你的应用域名>.authing.cn/oidc/auth?client_id=5c9b079883e333d55a101082&redirect_uri=https://example.com&scope=openid profile offline_access&response_type=code&prompt=consent&state=235345

参考资料:

  1. scope 与用户信息对应表
  2. response_type 参数 OIDC 规范 (opens new window)
  3. prompt 参数 OIDC 规范 (opens new window)

# 03 - 用户登录

发起 OIDC 登录之后,如果用户先前未在 OP 登录过,OP 会将用户重定向到登录页面,引导用户完成在 OP 的认证,此时用户需要选择一种方式进行登录:

你可以前往这个网址体验:https://sample-sso.authing.cn/login (opens new window)

用户登录

Authing 将验证此用户是否合法,验证通过后会将浏览器重定向到发起授权登录请求时指定redirect_uri 并通过 URL query 传递授权码 code 参数。

# 04 - 使用 code 换取 token

POST
https://<你的应用域名>.authing.cn/oidc/token

client_secret_post 方式换取 token

如果你在控制台配置 OIDC 时,换取 token 身份验证方式设置的为 client_secret_post,那么按照下面这种方法换取 token。

Headers
Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Form Data Parameters
client_id
REQUIRED
string

应用 ID

client_secret
REQUIRED
string

应用 Secret

grant_type
REQUIRED
string

authorization_code

redirect_uri
REQUIRED
string

发起 OIDC 授权登录时的 redirect_uri 值,必须与发起登录请求时的参数一致

code
REQUIRED
string

获取到的授权码,一个 code 仅限一次性使用,用后作废,有效期 10 分钟

200: OK
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJ4R01uczd5cmNFckxiakNRVW9US1MiLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ1Mzc4NjksImV4cCI6MTU1NDU0MTQ2OSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyBwaG9uZSBlbWFpbCIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.wX05OAgYuXeYM7zCxhrkvTO_taqxrCTG_L2ImDmQjMml6E3GXjYA9EFK0NfWquUI2mdSMAqohX-ndffN0fa5cChdcMJEm3XS9tt6-_zzhoOojK-q9MHF7huZg4O1587xhSofxs-KS7BeYxEHKn_10tAkjEIo9QtYUE7zD7JXwGUsvfMMjOqEVW6KuY3ZOmIq_ncKlB4jvbdrduxy1pbky_kvzHWlE9El_N5qveQXyuvNZVMSIEpw8_y5iSxPxKfrVwGY7hBaF40Oph-d2PO7AzKvxEVMamzLvMGBMaRAP_WttBPAUSqTU5uMXwMafryhGdIcQVsDPcGNgMX6E1jzLA",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjIyMTIxIiwiYXRfaGFzaCI6Ik5kbW9iZVBZOEFFaWQ2T216MzIyOXciLCJzaWQiOiI1ODM2NzllNC1lYWM5LTRjNDEtOGQxMS1jZWFkMmE5OWQzZWIiLCJhdWQiOiI1Y2E3NjVlMzkzMTk0ZDU4OTFkYjE5MjciLCJleHAiOjE1NTQ1NDE0NjksImlhdCI6MTU1NDUzNzg2OSwiaXNzIjoiaHR0cHM6Ly9hdXRoaW5nLmNuIn0.IQi5FRHO756e_eAmdAs3OnFMU7QuP-XtrbwCZC1gJntevYJTltEg1CLkG7eVhdi_g5MJV1c0pNZ_xHmwS0R-E4lAXcc1QveYKptnMroKpBWs5mXwoOiqbrjKEmLMaPgRzCOdLiSdoZuQNw_z-gVhFiMNxI055TyFJdXTNtExt1O3KmwqanPNUi6XyW43bUl29v_kAvKgiOB28f3I0fB4EsiZjxp1uxHQBaDeBMSPaRVWQJcIjAJ9JLgkaDt1j7HZ2a1daWZ4HPzifDuDfi6_Ob1ZL40tWEC7xdxHlCEWJ4pUIsDjvScdQsez9aV_xMwumw3X4tgUIxFOCNVEvr73Fg",
  "refresh_token": "WPsGJbvpBjqXz6IJIr1UHKyrdVF",
  "scope": "openid profile offline_access phone email",
  "token_type": "Bearer"
}

这里有完整的 nodejs 示例代码 (opens new window)

let code2tokenResponse;
try {
  code2tokenResponse = await axios.post(
    "https://<你的应用域名>.authing.cn/oidc/token",
    qs.stringify({
      code,
      client_id: oidcAppId,
      client_secret: oidcAppSecret,
      grant_type: "authorization_code",
      redirect_uri,
    }),
    {
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
    }
  );
} catch (error) {
  ctx.body = error.response.data;
  return;
}

使用 curl 发送请求示例:

curl --location --request POST 'https://<你的应用域名>.authing.cn/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'code=61yhuOVrgyhKlFTU~bnEKA_fnnz' \
--data-urlencode 'client_id=5e37979f7b757ead14c534af' \
--data-urlencode 'client_secret=64b517f8de3648091654eb4ee9b479d3' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://baidu.com'

返回示例:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJqdGkiOiJQZU41YXg1b3FabGRhcUJUMzQzeUkiLCJzdWIiOiI1Y2U1M2FlYTlmODUyNTdkZDEzMmQ3NDkiLCJpc3MiOiJodHRwczovL29hdXRoLmF1dGhpbmcuY24vb2F1dGgvb2lkYyIsImlhdCI6MTU4MTQyMDk1NywiZXhwIjoxNTgxNDI0NTU0LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGF1dGhpbmdfdG9rZW4gZW1haWwgcGhvbmUgYWRkcmVzcyBvZmZsaW5lX2FjY2VzcyIsImF1ZCI6IjVkMDFlMzg5OTg1ZjgxYzZjMWRkMzFkZSJ9.rtpRSL3_U03zXShZUCILquSR_KEDuS-OldWpy8RLztWUNG_tMyrg2g9CG4hC7pJUwmgzZKtp7vsVrj6W0eyo_ehE4KGz9iKnyd46DFbx9W9pi-mieRW5HuVMGL2zvDH8zF467WXET2SVB3LUhFLNmEbxpvjPZ5Ksvbcd7nqHfnUN4-z3SqAvhGWWfcmt7QDFlLtWPw4LzyznEqmM9sDkNiNDnTkjmcjm7yHJR-yv5FvpzQB2kraQVOrrdAixbHf29ihOVO25CrjmgeKemg1vuLNGUcOrr_XWn7xaCSvyAfXrBuRalecW9RA4p_Cp6YslHc_572awekt3kUO2TebUQA",
    "expires_in": 3597,
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ.eyJzdWIiOiI1Y2U1M2FlYTlmODUyNTdkZDEzMmQ3NDkiLCJiaXJ0aGRhdGUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImdlbmRlciI6IiIsImdpdmVuX25hbWUiOiIiLCJsb2NhbGUiOiIiLCJtaWRkbGVfbmFtZSI6IiIsIm5hbWUiOiIiLCJuaWNrbmFtZSI6IiIsInBpY3R1cmUiOiJodHRwczovL3VzZXJjb250ZW50cy5hdXRoaW5nLmNuL2F1dGhpbmctYXZhdGFyLnBuZyIsInByZWZlcnJlZF91c2VybmFtZSI6IiIsInByb2ZpbGUiOiIiLCJ1cGRhdGVkX2F0IjoiIiwid2Vic2l0ZSI6IiIsInpvbmVpbmZvIjoiIiwiY29tcGFueSI6IiIsImJyb3dzZXIiOiIiLCJsb2dpbnNfY291bnQiOjEwMywicmVnaXN0ZXJfbWV0aG9kIjoiZGVmYXVsdDp1c2VybmFtZS1wYXNzd29yZCIsImJsb2NrZWQiOmZhbHNlLCJsYXN0X2lwIjoiMTIxLjIxLjU2LjE3MSIsInJlZ2lzdGVyX2luX3VzZXJwb29sIjoiNWM5NTkwNTU3OGZjZTUwMDAxNjZmODUzIiwibGFzdF9sb2dpbiI6IjIwMjAtMDItMTFUMTE6MzU6MTUuNjk2WiIsInNpZ25lZF91cCI6IjIwMTktMDUtMjJUMTI6MDQ6NTguMjk0WiIsInRva2VuIjoiZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmtZWFJoSWpwN0ltVnRZV2xzSWpvaWRHVnpkRE5BTVRJekxtTnZiU0lzSW1sa0lqb2lOV05sTlROaFpXRTVaamcxTWpVM1pHUXhNekprTnpRNUlpd2lZMnhwWlc1MFNXUWlPaUkxWXprMU9UQTFOVGM0Wm1ObE5UQXdNREUyTm1ZNE5UTWlmU3dpYVdGMElqb3hOVGd4TkRJd09URTFMQ0psZUhBaU9qRTFPREkzTVRZNU1URjkuM0l0X0NJQTNFbUpoYWcyMW92WjNwd0RfY0owcTVTZkJjSURSZThRX3FoayIsImVtYWlsIjoidGVzdDNAMTIzLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicGhvbmVfbnVtYmVyIjoiMTMxMTIzNDEyMzQiLCJhZGRyZXNzIjoiIiwiYXRfaGFzaCI6IjV6QnNUOHF4RHc1RmNYdU55UFg4YUEiLCJzaWQiOiJkNmZiOTE5Ny00NmE3LTQ1ZGEtOGVkMC05ODhjZjg0ZjQwZWUiLCJhdWQiOiI1ZDAxZTM4OTk4NWY4MWM2YzFkZDMxZGUiLCJleHAiOjE1ODE0MjQ1NTQsImlhdCI6MTU4MTQyMDk1NywiaXNzIjoiaHR0cHM6Ly9vYXV0aC5hdXRoaW5nLmNuL29hdXRoL29pZGMifQ.VZzqULytIteyBfouww5TsHQ50gEhM06kUWMeDiO3FVFSCW9ys2bFPos5p6LFzliK4Ce09ypOwVQiRnE2gNYsukLvlUPlKDIP_Xk5W19frKi1Z8ImuIPvUqVMKbFutVNS0TfIPCPJVBl8C1j5OXeIs6z0V90QrvyJao6FqVEa3axOHxbhpo1fH2hP04-wkGOp_l10d7RFhGcnPyPnz9-C5X6A4UEsCSDCVw1mDQHxDSFP9OPaB_OlCG_Bi6G-CeLhPa3V5hyIefdBvxC9SIpK-6qY-_BfsNKkBHDVKMb0xodgN2hzn3UTUGBuuoiaB4JhCv72EZ7eiXKIXFz6zVcogA",
    "refresh_token": "DuSPlrUFPAvCZ1WQKarv5MbEsXN",
    "scope": "openid profile authing_token email phone address offline_access",
    "token_type": "Bearer"
}

id_token 中会包含 scope 参数请求的信息,例如邮箱、手机号,解析后的 id_token:

{
  "sub": "5f64afd1ad501364e3b43c1e",
  "birthdate": null,
  "family_name": null,
  "gender": "U",
  "given_name": null,
  "locale": null,
  "middle_name": null,
  "name": null,
  "nickname": null,
  "picture": "https://usercontents.authing.cn/authing-avatar.png",
  "preferred_username": "test1",
  "profile": null,
  "updated_at": "2020-09-27T06:06:29.853Z",
  "website": null,
  "zoneinfo": null,
  "email": "test1@123.com",
  "email_verified": false,
  "phone_number": null,
  "phone_number_verified": false,
  "nonce": "EhoXn8m7vy",
  "at_hash": "QVtna22pWzdkeSBW3f91pg",
  "aud": "5f17a529f64fb009b794a2ff",
  "exp": 1601468174,
  "iat": 1601464574,
  "iss": "http://console.xxx.localhost:3000/oidc"
}
POST
https://<你的应用域名>.authing.cn/oidc/token

client_secret_basic 方式换取 token

如果你在控制台配置 OIDC 时,换取 token 身份验证方式设置的为 client_secret_basic,那么按照下面这种方法换取 token。(client_secret_basic 是使用 HTTP Basic authentication 模式进行认证。)

Headers
Authorization
REQUIRED
string

Basic NWNhNzY1ZTM5MzE5NGQ1ODxxxx

Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Form Data Parameters
grant_type
REQUIRED
string

authorization_code

redirect_uri
REQUIRED
string

发起 OIDC 授权登录时的 redirect_uri 值,这个参数不能随意填写,必须与发起登录请求时的参数一致。

code
REQUIRED
string

获取到的授权码,一个 code 仅限一次性使用,用后作废,有效期 10 分钟。

200: OK
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJ4R01uczd5cmNFckxiakNRVW9US1MiLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ1Mzc4NjksImV4cCI6MTU1NDU0MTQ2OSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyBwaG9uZSBlbWFpbCIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.wX05OAgYuXeYM7zCxhrkvTO_taqxrCTG_L2ImDmQjMml6E3GXjYA9EFK0NfWquUI2mdSMAqohX-ndffN0fa5cChdcMJEm3XS9tt6-_zzhoOojK-q9MHF7huZg4O1587xhSofxs-KS7BeYxEHKn_10tAkjEIo9QtYUE7zD7JXwGUsvfMMjOqEVW6KuY3ZOmIq_ncKlB4jvbdrduxy1pbky_kvzHWlE9El_N5qveQXyuvNZVMSIEpw8_y5iSxPxKfrVwGY7hBaF40Oph-d2PO7AzKvxEVMamzLvMGBMaRAP_WttBPAUSqTU5uMXwMafryhGdIcQVsDPcGNgMX6E1jzLA",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjIyMTIxIiwiYXRfaGFzaCI6Ik5kbW9iZVBZOEFFaWQ2T216MzIyOXciLCJzaWQiOiI1ODM2NzllNC1lYWM5LTRjNDEtOGQxMS1jZWFkMmE5OWQzZWIiLCJhdWQiOiI1Y2E3NjVlMzkzMTk0ZDU4OTFkYjE5MjciLCJleHAiOjE1NTQ1NDE0NjksImlhdCI6MTU1NDUzNzg2OSwiaXNzIjoiaHR0cHM6Ly9hdXRoaW5nLmNuIn0.IQi5FRHO756e_eAmdAs3OnFMU7QuP-XtrbwCZC1gJntevYJTltEg1CLkG7eVhdi_g5MJV1c0pNZ_xHmwS0R-E4lAXcc1QveYKptnMroKpBWs5mXwoOiqbrjKEmLMaPgRzCOdLiSdoZuQNw_z-gVhFiMNxI055TyFJdXTNtExt1O3KmwqanPNUi6XyW43bUl29v_kAvKgiOB28f3I0fB4EsiZjxp1uxHQBaDeBMSPaRVWQJcIjAJ9JLgkaDt1j7HZ2a1daWZ4HPzifDuDfi6_Ob1ZL40tWEC7xdxHlCEWJ4pUIsDjvScdQsez9aV_xMwumw3X4tgUIxFOCNVEvr73Fg",
  "refresh_token": "WPsGJbvpBjqXz6IJIr1UHKyrdVF",
  "scope": "openid profile offline_access phone email",
  "token_type": "Bearer"
}

其中 Authorization 请求头 Basic<空格> 后的值为 <client_id>:<client_secret> 的 base64 值。

POST
https://<你的应用域名>.authing.cn/oidc/token

none 方式换取 token

如果你在控制台配置 OIDC 时,换取 token 身份验证方式设置的为 none,那么换取 token 时无需传递 client_secret,其他参数和上表一样。

Headers
Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Form Data Parameters
client_id
REQUIRED
string

应用 ID。

grant_type
REQUIRED
string

authorization_code

redirect_uri
REQUIRED
string

发起 OIDC 授权登录时的 redirect_uri 值,这个参数不能随意填写,必须与发起登录请求时的参数一致。

code
REQUIRED
string

获取到的授权码,一个 code 仅限一次性使用,用后作废,有效期 10 分钟。

200: OK
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJ4R01uczd5cmNFckxiakNRVW9US1MiLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ1Mzc4NjksImV4cCI6MTU1NDU0MTQ2OSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyBwaG9uZSBlbWFpbCIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.wX05OAgYuXeYM7zCxhrkvTO_taqxrCTG_L2ImDmQjMml6E3GXjYA9EFK0NfWquUI2mdSMAqohX-ndffN0fa5cChdcMJEm3XS9tt6-_zzhoOojK-q9MHF7huZg4O1587xhSofxs-KS7BeYxEHKn_10tAkjEIo9QtYUE7zD7JXwGUsvfMMjOqEVW6KuY3ZOmIq_ncKlB4jvbdrduxy1pbky_kvzHWlE9El_N5qveQXyuvNZVMSIEpw8_y5iSxPxKfrVwGY7hBaF40Oph-d2PO7AzKvxEVMamzLvMGBMaRAP_WttBPAUSqTU5uMXwMafryhGdIcQVsDPcGNgMX6E1jzLA",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjIyMTIxIiwiYXRfaGFzaCI6Ik5kbW9iZVBZOEFFaWQ2T216MzIyOXciLCJzaWQiOiI1ODM2NzllNC1lYWM5LTRjNDEtOGQxMS1jZWFkMmE5OWQzZWIiLCJhdWQiOiI1Y2E3NjVlMzkzMTk0ZDU4OTFkYjE5MjciLCJleHAiOjE1NTQ1NDE0NjksImlhdCI6MTU1NDUzNzg2OSwiaXNzIjoiaHR0cHM6Ly9hdXRoaW5nLmNuIn0.IQi5FRHO756e_eAmdAs3OnFMU7QuP-XtrbwCZC1gJntevYJTltEg1CLkG7eVhdi_g5MJV1c0pNZ_xHmwS0R-E4lAXcc1QveYKptnMroKpBWs5mXwoOiqbrjKEmLMaPgRzCOdLiSdoZuQNw_z-gVhFiMNxI055TyFJdXTNtExt1O3KmwqanPNUi6XyW43bUl29v_kAvKgiOB28f3I0fB4EsiZjxp1uxHQBaDeBMSPaRVWQJcIjAJ9JLgkaDt1j7HZ2a1daWZ4HPzifDuDfi6_Ob1ZL40tWEC7xdxHlCEWJ4pUIsDjvScdQsez9aV_xMwumw3X4tgUIxFOCNVEvr73Fg",
  "refresh_token": "WPsGJbvpBjqXz6IJIr1UHKyrdVF",
  "scope": "openid profile offline_access phone email",
  "token_type": "Bearer"
}

# 05 - 验证 access_token 和 id_token 的合法性

# 使用应用的 App Secret 检验

Authing 默认使用应用的 Secret 对 token 进行签名(也就是在创建应用时默认选择 HS256 算法)。

如果你使用 javascript 那么可以使用 jsonwebtoken 进行验证:

const jwt = require('jsonwebtoken');
let decoded = jwt.verify(token, <appSecret>);

如果是其他语言,那么你在服务端需要用 app_secret 作为 HS256 签名参数来计算签名和 JWT 中的签名进行对比,伪代码如下:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  "1133fd20c14e4cc29b6ecb71fb8eb952"// app_secret
)

# 使用公钥验证签名

如果使用 RS256 签名算法,需要使用公钥验证签名。Authing 将使用应用的私钥进行签名,请使用 https://<应用域名>.authing.cn/oidc/.well-known/jwks.json 中的公钥来验证签名。Authing 颁发的 access_token 和 id_token 都可以使用上述公钥进行验签。

如果你使用 javascript,可以使用 jose 库来验证 RS256 签名:

const jose = require("jose");
// 下面的参数内容是将 https://<应用域名>.authing.cn/oidc/.well-known/jwks.json 返回的内容原封不动复制过来
const keystore = jose.JWKS.asKeyStore({
  keys: [
    {
      e: "AQAB",
      n:
        "o8iCY52uBPOCnBSRCr3YtlZ0UTuQQ4NCeVMzV7JBtH-7Vuv0hwGJTb_hG-BeYOPz8i6YG_o367smV2r2mnXbC1cz_tBfHD4hA5vnJ1eCpKRWX-l6fYuS0UMti-Bmg0Su2IZxXF9T1Cu-AOlpgXFC1LlPABL4E0haHO8OwQ6QyEfiUIs0byAdf5zeEHFHseVHLjsM2pzWOvh5e_xt9NOJY4vB6iLtD5EIak04i1ND_O0Lz0OYbuV0KjluxaxoiexJ8kGo9W1SNza_2TqUAR6hsPkeOwwh-oHnNwZg8OEnwXFmNg-bW4KiBrQEG4yUVdFGENW6vAQaRa2bJX7obn4xCw",
      kty: "RSA",
      alg: "RS256",
      use: "sig",
      kid: "TfLOt3Lbn8_a8pRMuessamqj-o3DBCs1-owHLQ-VMqQ",
    },
  ],
});
// 选项中 issuer 的内容是 https://<应用域名>.authing.cn/oidc,audience 的内容是 应用 ID
// id_token 很长,请向右滑动 ->
const res = jose.JWT.IdToken.verify(
  "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlRmTE90M0xibjhfYThwUk11ZXNzYW1xai1vM0RCQ3MxLW93SExRLVZNcVEifQ.eyJzdWIiOiI1ZjcxOTk0NjUyNGVlMTA5OTIyOTQ5NmIiLCJiaXJ0aGRhdGUiOm51bGwsImZhbWlseV9uYW1lIjpudWxsLCJnZW5kZXIiOiJVIiwiZ2l2ZW5fbmFtZSI6bnVsbCwibG9jYWxlIjpudWxsLCJtaWRkbGVfbmFtZSI6bnVsbCwibmFtZSI6bnVsbCwibmlja25hbWUiOm51bGwsInBpY3R1cmUiOiJodHRwczovL2ZpbGVzLmF1dGhpbmcuY28vdXNlci1jb250ZW50cy9waG90b3MvOWE5ZGM0ZDctZTc1Ni00NWIxLTgxZDgtMDk1YTI4ZTQ3NmM2LmpwZyIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3QxIiwicHJvZmlsZSI6bnVsbCwidXBkYXRlZF9hdCI6IjIwMjAtMDktMzBUMDc6MTI6MTkuNDAxWiIsIndlYnNpdGUiOm51bGwsInpvbmVpbmZvIjpudWxsLCJlbWFpbCI6InRlc3QxQDEyMy5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInBob25lX251bWJlciI6bnVsbCwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjpmYWxzZSwibm9uY2UiOiJFNjViMVFvVVl0IiwiYXRfaGFzaCI6IkIzSWdPWUREYTBQejh2MV85cVpyQXciLCJhdWQiOiI1ZjE3YTUyOWY2NGZiMDA5Yjc5NGEyZmYiLCJleHAiOjE2MDE0NTM1NTgsImlhdCI6MTYwMTQ0OTk1OSwiaXNzIjoiaHR0cHM6Ly9vaWRjMS5hdXRoaW5nLmNuL29pZGMifQ.Z0TweYr9bCdYNJREVdvbJYcjXSfSsSNHBMqxTJeW-bnza0IIpBpEEVxlDG0Res6FZbcVzsQZzfJ9pj_nFgLjZxUUxv7Tpd13Sq_Ykg2JKepPf3-uoFqbORym07QEj4Uln0Quuh094MTb7z6bZZBEOYBac46zuj4uVp4vqk5HtCUSB4ASOAxwi7CeB1tKghISHz6PDcf6XJe_btHdzX1dparxtML-KvPxjpcHlt5emN88lpTAOX7Iq0EhsVE3PKrIDfCkG8XlL5y9TIW2Dz2iekcZ5PV17M35G6Dg2Q07Y_Apr18_oowOiQM5m_EbI90ist8CiqO9kBKreCOLMzub4Q",
  keystore,
  {
    issuer: "https://oidc1.authing.cn/oidc",
    audience: "5f17a529f64fb009b794a2ff",
  }
);
console.log(res);

输出结果:

{
  sub: '5f719946524ee1099229496b',
  birthdate: null,
  family_name: null,
  gender: 'U',
  given_name: null,
  locale: null,
  middle_name: null,
  name: null,
  nickname: null,
  picture: 'https://files.authing.co/user-contents/photos/9a9dc4d7-e756-45b1-81d8-095a28e476c6.jpg',
  preferred_username: 'test1',
  profile: null,
  updated_at: '2020-09-30T07:12:19.401Z',
  website: null,
  zoneinfo: null,
  email: 'test1@123.com',
  email_verified: false,
  phone_number: null,
  phone_number_verified: false,
  nonce: 'E65b1QoUYt',
  at_hash: 'B3IgOYDDa0Pz8v1_9qZrAw',
  aud: '5f17a529f64fb009b794a2ff',
  exp: 1601453558,
  iat: 1601449959,
  iss: 'https://oidc1.authing.cn/oidc'
}

# 将 token 或 id_token 发送到 Authing 提供的 token 验证接口进行验证

GET
https://<你的应用域名>.authing.cn/api/v2/oidc/validate_token

在线验证 access_token / id_token 合法性

Authing 提供了接口用于直接在线验证 access_token 或 id_token 的合法性。

Path Paramter
access_token
OPTIONAL
string

值为 access_token

id_token
OPTIONAL
string

值为 id_token

200: OK

验证 access_token 或 id_token 时会有以下几种返回结果

// access_token 或 id_token 合法时,返回 access_token / id_token 解码后的的内容

// access_token 检验后的返回结果:
{
    "jti": "K5TYewNhvdGBdHiRifMyW",
    "sub": "5f64afd1ad501364e3b43c1e",
    "iat": 1601456894,
    "exp": 1601460494,
    "scope": "openid profile email phone",
    "iss": "https://oidc1.authing.cn/oidc",
    "aud": "5f17a529f64fb009b794a2ff"
}

// id_token 检验后的返回结果:
{
    "sub": "5f64afd1ad501364e3b43c1e",
    "birthdate": null,
    "family_name": null,
    "gender": "U",
    "given_name": null,
    "locale": null,
    "middle_name": null,
    "name": null,
    "nickname": null,
    "picture": "https://usercontents.authing.cn/authing-avatar.png",
    "preferred_username": "test1",
    "profile": null,
    "updated_at": "2020-09-27T06:06:29.853Z",
    "website": null,
    "zoneinfo": null,
    "email": "test1@123.com",
    "email_verified": false,
    "phone_number": null,
    "phone_number_verified": false,
    "nonce": "CQsguqUdl7",
    "at_hash": "10iOtwuTNtyQLzlNYXAHeg",
    "aud": "5f17a529f64fb009b794a2ff",
    "exp": 1601460494,
    "iat": 1601456894,
    "iss": "https://oidc1.authing.cn/oidc",
}

// access_token 或 id_token 非法时,返回以下错误信息

{
  code: 400,
  message: 'id_token 不合法',
}

{
  code: 400,
  message: 'access_token 不合法',
}

参考链接

  1. JWKS 参考规范 (opens new window)
  2. 可以在线检验 JWT 的签名的网站:https://jwt.io (opens new window)
  3. RSA 公私钥 PEM 格式 与 JWK 格式互转:https://8gwifi.org/jwkconvertfunctions.jsp (opens new window)
  4. 生成 JWK:https://mkjwk.org (opens new window)

# 06 - 使用 access_token 换取用户信息

开发者应该在自己的后端服务器使用 access_token 换取用户信息。如果发起授权登录时的 scope 参数不同,这里的返回信息也会不同,返回信息中的字段取决于 scope 参数。字段符合 OIDC 规范 (opens new window),用户信息字段与 scope 对应关系请参考 scope 参数对应的用户信息

GET
https://core.authing.cn/oidc/me

使用 access_token 换取用户信息

Query Parameters
access_token
REQUIRED
string

access_token

200: OK
{
  "sub": "5f7174df27e0eb9c6d21436d",
  "birthdate": null,
  "family_name": null,
  "gender": "U",
  "given_name": null,
  "locale": null,
  "middle_name": null,
  "name": null,
  "nickname": null,
  "picture": "https://usercontents.authing.cn/authing-avatar.png",
  "preferred_username": null,
  "profile": null,
  "updated_at": "2020-09-28T05:33:15.892Z",
  "website": null,
  "zoneinfo": null
}

请求链接示例:https://core.authing.cn/oidc/me?access_token=<access_token>

返回示例:

{
  "sub": "5f7174df27e0eb9c6d21436d",
  "birthdate": null,
  "family_name": null,
  "gender": "U",
  "given_name": null,
  "locale": null,
  "middle_name": null,
  "name": null,
  "nickname": null,
  "picture": "https://usercontents.authing.cn/authing-avatar.png",
  "preferred_username": null,
  "profile": null,
  "updated_at": "2020-09-28T05:33:15.892Z",
  "website": null,
  "zoneinfo": null
}

更多字段解释请参考用户信息字段含义

# 07 - 刷新 token

POST
https://<你的应用域名>.authing.cn/oidc/token

使用 refresh_token 刷新用户的 access_token 和 id_token。

如需使用刷新 token 功能,需要进入控制台 > 应用 > 应用列表,找到你的应用,点击「配置」,在应用配置中勾选 refresh_ token

refresh_token

注意 ⚠️⚠️⚠️:发起登录请求时必须填写正确的 URL query 参数,只勾选 refresh_token授权登录参数错误或者直接输入应用认证地址,Authing 不会返回任何 refresh_token。scope 参数中必须有 offline_accessprompt 参数值必须为 consent。能够返回 refresh_token 的登录请求示例:
https://example.authing.cn/oidc/auth?client_id=5c9b079883e333d55a101082&redirect_uri=https://example.com&scope=openid profile offline_access&response_type=code&prompt=consent&state=235345

Headers
Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Form Data Parameters
client_id
REQUIRED
string

OIDC 应用 ID。

client_secret
REQUIRED
string

OIDC 应用 Secret。

grant_type
REQUIRED
string

refresh_token

refresh_token
REQUIRED
string

使用 code 换 token 时返回的 refresh_token。例:WPsGJbvpBjqXz6IJIr1UHKyrdVF

200: OK
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJ4MjlRNnIzWkpndVViWHB5RGR0ZVciLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ2MTI0NjQsImV4cCI6MTU1NDYxNjA2NCwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBvZmZsaW5lX2FjY2VzcyBwaG9uZSBlbWFpbCIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.VgrdtZRCbapS0hCe5BiV-8rUTXd4x-ZMoFPHV5Zh_HCw-OsJoYN0mVwB1UQ0ZkrA4ojpcZ3MrLnKzRC81BgEnfvaInTqXW8qP36TvR-vl7JkVT-ThkBr0Xdilk0hCfWaMbX9qtCjWYT0b9zxDAdkBKygjztZ74TwKbxNI83vdKSj9A6OfwX9MG4k-Q3ZbKAj1fwncBAp2DEsv1Bd_-4y_n_w-2QtbzZf3409UEotKuU_wGLoVE3DLxJFvEtmunbxQOkqxOGS_JaIvFdhpTZ6I3H_DC5KO8xOR2A6nZGFOhYOZZfnr6tmY_EnOIEsnp4glgTCOqHhd1xoBoDcnEmWEA",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjIyMTIxIiwiYXRfaGFzaCI6InVySTYzZ3hyeU01UzNqejRLMmpWeGciLCJzaWQiOiIxOTdlOGExMy0wMzE4LTRkZDEtYjQ3Mi0xZjI0MDk5ZTUzOWYiLCJhdWQiOiI1Y2E3NjVlMzkzMTk0ZDU4OTFkYjE5MjciLCJleHAiOjE1NTQ2MTYwNjQsImlhdCI6MTU1NDYxMjQ2NCwiaXNzIjoiaHR0cHM6Ly9hdXRoaW5nLmNuIn0.wh3kCIGyu7IHvkbqCeu9OHg9mdLg-wSbU-1UBLPcNxl5MeXsGxtxjPyM6aONxLt_ZXfBFNZM7FWfGpV_qGSNmeGp0UYV_bK-N0wgB5ZkTN1O4EMECqy7qCExwK3kjsOa-o0KkkJxxcDkfEJ3Icn2Nr3q5ozMz_3oGJWqSt0KxQaR_rCtjbLV6dIpPL1MTpWElORXjsoKb1RVOHF0Qpfq8iuGVJAw828tq4cyLH9-IkE9TGX2L6dWmPaY1xd0ho0N1mqnWJrqacljrvX8qPTfGAB9-9rDk2EvFrZkFY6O6bKlMqdyX4ktxYMlku4-H74wxOqkQ_ZWlI3SUG_m-DNDWg",
  "refresh_token": "wlfsGj5oSm5xmdUV_HqS9FTQpaj",
  "scope": "openid profile offline_access phone email",
  "token_type": "Bearer
}

# 08 - 撤回 token

POST
https://<你的应用域名>.authing.cn/oidc/token/revocation

08 - 撤回 token

只有 access_tokenrefresh_token 可以被撤回,id_token 无法撤回

Headers
Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Authorization
OPTIONAL
string

在控制台配置 token 撤回身份验证方式为 client_secret_basic 时必填,形式为:Basic base64(应用 ID + ':' + 应用 Secret)

Form Data Parameters
token
REQUIRED
string

要撤回的 token 值

token_type_hint
OPTIONAL
string

要撤回的 token 类型,可选值为 access_token、refresh_token

client_id
OPTIONAL
string

应用 ID,在控制台配置撤回 token 身份验证方式为 client_secret_postnone 时必填

client_secret
OPTIONAL
string

应用 Secret,在控制台配置撤回 token 身份验证方式为 client_secret_post 时必填

200: OK

无任何内容,HTTP 响应码为 200,代表撤回成功。

# 09 - 检验 token 状态

POST
https://<你的应用域名>.authing.cn/oidc/token/introspection

09 - 检验 token 状态

只有 access_tokenrefresh_token 可以检测状态,id_token 无法检测

Headers
Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Authorization
OPTIONAL
string

在控制台配置检验 token 身份验证方式为 client_secret_basic 时必填,形式为:Basic base64(应用 ID + ':' + 应用 Secret)

Form Data Parameters
token
REQUIRED
string

要检验的 token 值

token_type_hint
OPTIONAL
string

要检验的 token 类型,可选值为 access_token、refresh_token

client_id
OPTIONAL
string

应用 ID,在控制台配置检验 token 身份验证方式为 client_secret_postnone 时必填

client_secret
OPTIONAL
string

应用 Secret,在控制台配置检验 token 身份验证方式为 client_secret_post 时必填

200: OK

当 token 有效时返回以下格式内容

{
  "active": true,
  "sub": "5f623f30d85f84c58f141777",
  "client_id": "5d01e389985f81c6c1dd31de",
  "exp": 1600634105,
  "iat": 1600274405,
  "iss": "https://oidc1.authing.cn",
  "jti": "hoV44FPNR-_EfxTP7s7vw",
  "scope": "openid profile email phone offline_access",
  "token_type": "Bearer"
}

当 token 无效时(过期,错误,被撤回)返回以下格式内容

{
  "active": false
}

# 使用隐式模式(Implicit Flow)

OIDC 隐式模式不会返回授权码 code,而是直接将 access_tokenid_token 通过 URL hash 发送到回调地址前端后端无法获取到这里返回的值,因为 URL hash 不会被直接发送到后端。

# 在控制台配置 OIDC 应用

进入控制台 > 应用 > 应用列表,找到你的应用,点击「配置」,在应用配置选项卡,找到授权模式,勾选 implicit 模式,并在下方返回类型中勾选 id_token tokenid_token

id_token token / id_token

# 发起授权

发起隐式模式的授权登录需要拼接一个 URL,并让终端用户在浏览器中访问,不能直接输入认证地址域名。具体参数如下:

参数名 意义
client_id 应用 ID。
redirect_uri 回调链接,用户在 OP 认证成功后,OP 会将 id_token、access_token 以 URL hash 的形式发送到这个地址。这个值必须出现在控制台配置的回调地址中,否则 OP 不允许向该地址回调。启用隐式模式时,控制台配置的所有 redirect_uri 建议使用 https 地址,否则 access_token 将会在明文状态下传输,造成安全隐患。如果你要使用 http 地址,请在控制台打开「不强制 implicit 模式回调链接为 https」开关。
scope 需要请求的权限,必须包含 openid。如果需要获取手机号email 需要包含 phone email;多个 scope 请用空格分隔。同时 id_token 中会包含相关的字段。隐式模式不支持返回 refresh_token,所以 offline_access 字段无效。
response_type 返回类型,可以为 id_token, id_token token。用于指定 OP 返回 id_token 和 access_token。参考 OIDC 规范 (opens new window)
prompt 可以为 none,login,consent 或 select_account,指定 OP 与 End-User 的交互方式。参考 OIDC 规范 (opens new window)
state 一个随机字符串,用于防范 CSRF 攻击,如果 response 中的 state 值和发送请求之前设置的 state 值不同,说明受到攻击。
nonce 一个随机字符串,用于防范 Replay 攻击,implicit 模式下必填

假设你创建了一个域名为 example 的 OIDC 应用,那么发起隐式模式 OIDC 授权登录的网址是:

GET https://example.authing.cn/oidc/auth?client_id=5ca765e393194d5891db1927&redirect_uri=https://example.com&scope=openid profile&response_type=id_token token&state=6223573295&nonce=1831289

# 获取 id_token 和 access_token

id_token、access_token 会以 URL hash 的形式传递,跳转后链接示例:

https://authing.cn/#id_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1Y2QwMjZlZjNlZDlmOTRkODBmZTM2YWUiLCJub25jZSI6IjE4MzEyODkiLCJzaWQiOiI4YzgzN2I5My01OTNlLTQzZjctYWMzNC0yYjRmZDU3ZGFhMTciLCJhdF9oYXNoIjoiVFFtbFlEVTVPZGF1Zjl0U0VKdHY5USIsInNfaGFzaCI6Ind3SDNXclV2b0hiSUp5TWVZVHU4bHciLCJhdWQiOiI1ZDAxZTM4OTk4NWY4MWM2YzFkZDMxZGUiLCJleHAiOjE1NjA0MDkzNjgsImlhdCI6MTU2MDQwNTc2OCwiaXNzIjoiaHR0cHM6Ly9vYXV0aC5hdXRoaW5nLmNuL29hdXRoL29pZGMifQ.T9M0s6rk4Teq6VOOBRIElgHK9KyM3q0ZJj2aS0VD_Fw&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3OE9XcVJNVXJEUXpMMXpHVzVtUWoiLCJzdWIiOiI1Y2QwMjZlZjNlZDlmOTRkODBmZTM2YWUiLCJpc3MiOiJodHRwczovL29hdXRoLmF1dGhpbmcuY24vb2F1dGgvb2lkYyIsImlhdCI6MTU2MDQwNTc2OCwiZXhwIjoxNTYwNDA5MzY4LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIiwiYXVkIjoiNWQwMWUzODk5ODVmODFjNmMxZGQzMWRlIn0.mR0MZDwlZWGRMsAZjQ27sDFFqYoDgZ6WHTK4C7JbML4&expires_in=3600&token_type=Bearer&state=jazz&session_state=26ec053be9f47d68dc430f84b97efb1095469fe10169a9e00ef4092718714b8b

换取用户信息的流程和授权码模式相同。

为什么信息在 URL hash 里而不是 query 里?因为 hash 内容不会直接发送到服务器,避免 id_token、access_token 被盗用。

参考资料:

scope 与用户信息对应表

# 使用混合模式(Hybrid Flow)

混合模式支持同时将 codeaccess_tokenid_token 返回到回调地址前端,都以 URL Hash 的形式传递,后端无法直接接收到,因为 URL hash 不会直接发送到服务器。

# 在控制台配置 OIDC 应用

进入控制台 > 应用 > 应用列表,找到你的应用,点击「配置」,在应用配置选项卡,找到授权模式,选择 authorization_codeimplicit,并在下方返回类型勾选 code id_token tokencode id_tokencode token

code、access_token 和 id_token

# 发起授权

发起授权需要拼接一个用来授权的 URL,并让终端用户在浏览器中访问,不能直接访问认证地址域名。具体参数如下:

参数名 意义
client_id 应用 ID。
redirect_uri 回调链接,用户在 OP 认证成功后,OP 会将 code、id_token、access_token 以 URL hash 的形式发送到这个地址。这个值必须出现在控制台配置的回调地址中,否则 OP 不允许向该地址回调。使用混合模式时,控制台配置的所有 redirect_uri 建议使用 https 地址,否则 access_token 将会在明文状态下传输,造成安全隐患。如果你要使用 http 地址,请在控制台打开「不强制 implicit 模式回调链接为 https」开关。
scope 需要请求的权限,必须包含 openid。如果需要获取手机号email 需要包含 phone email,多个 scope 请用空格分开。同时 id_token 中会包含相关的字段。如果需要刷新 token,需要有 offline_access 参数,同时 response_type 参数中必须包含 code并使用 code 换取 token,在后续 code 换 token 的结果中才会返回 refresh_token。
response_type 返会类型,此处为 code id_token token。用于指定 OP 返回 code、id_token 和 access_token。参考 OIDC 规范 (opens new window)
prompt 可以为 none,login,consent 或 select_account,指定 OP 与 End-User 的交互方式。参考 OIDC 规范 (opens new window)
state 一个随机字符串,用于防范 CSRF 攻击,如果 response 中的 state 值和发送请求之前设置的 state 值不同,说明受到攻击。
nonce 一个随机字符串,用于防范 Replay 攻击,混合模式下必填。

假设你创建了一个域名为 example 的 OIDC 应用,那么授权网址是:

GET https://example.authing.cn/oidc/auth?client_id=5ca765e393194d5891db1927&redirect_uri=https://example.com&scope=openid profile&response_type=code id_token token&state=23758235&nonce=1831289

code、access_token 和 id_token 通过 URL hash 传递,跳转后链接示例:

https://example.com/#code=pIY83Jl_bcerNN9Wt57Sq0TAjTr&id_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJub25jZSI6IjE4MzEyODkiLCJzaWQiOiIxOTdlOGExMy0wMzE4LTRkZDEtYjQ3Mi0xZjI0MDk5ZTUzOWYiLCJhdF9oYXNoIjoiUFlXaTFER29jRlotYmlYd0d5WXlpZyIsImNfaGFzaCI6Ik4yUmkyUFpidktYdXRmdGhZbUhrM2ciLCJzX2hhc2giOiJ3d0gzV3JVdm9IYklKeU1lWVR1OGx3IiwiYXVkIjoiNWNhNzY1ZTM5MzE5NGQ1ODkxZGIxOTI3IiwiZXhwIjoxNTU0NjE1NjcyLCJpYXQiOjE1NTQ2MTIwNzIsImlzcyI6Imh0dHBzOi8vYXV0aGluZy5jbiJ9.a--JC_6CyUi0Z7z3DCKT51wJkKT7MmtlVHhrNujhxHCfgQqzqS3wMxVj6oEe_cfjVQNgJ-Xe1oiL8uMAxVN-cM1Ra1JQcavUujua2IxxtG4Nkh84rTukqsrPfuNhNO7MRP6Fa9qIIdKeKkQKyh1zBKE6322zK_ECdfGd2sWdqqXiQyJXg6ODhPZDidsGuluV3bZiAY3brMSMmh6QC99StOP5ZwSKtlRMyYE3MIRWsQ4W2HkHBrk67T_scQ6XN6mdBKi2OZW-E7fXeyVwH-ibWDzlUpmFSaj3a-WbkDe3nfCv8MHj439aJNU-AXfIgLsckvCO5_dJOUWGHg6hemT9bw&access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiIxUzgyaUtSdXFlWW1DUmFrMFl1S0kiLCJzdWIiOiI1YzlmNzVjN2NjZjg3YjA1YTkyMWU5YjAiLCJpc3MiOiJodHRwczovL2F1dGhpbmcuY24iLCJpYXQiOjE1NTQ2MTIwNzIsImV4cCI6MTU1NDYxNTY3Miwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSIsImF1ZCI6IjVjYTc2NWUzOTMxOTRkNTg5MWRiMTkyNyJ9.tHwxiH5QXXA46Y4mIwcBck3uDArMj5TMGEBAQ8Eeln6oFbwBY3aS5cSV6e3anZDwKZrdgrdFlyj9-Bl1T5V1rNJK-Xz_aFnM6XxyO1jSHcn-6KXGwmz68D50VIHior39cuoj9OXbNCei5RVghjh2cRT3SenYki7UeJBgmfQA6l2aZZpBrn9aphXr9OoPS47T59I0Ynn2yMIYIMDOX7hh8E5oV1hrK3hyjAvp3ghmzyRfj2BlG9rBo1hd_d5E8x6OIzNdvPKXwVASJZRxov2Dx0ma36zxzSObyXgCloUv2KlbmL9-Wj8d3H6FhHC75DLfJYx-uRgNqW7CFKGeRkPjkQ&expires_in=3600&token_type=Bearer&state=jazz&session_state=101666b6b70cfb4406ad9c0c906039de39776140e66e48acdb63ab8acb309701

换取用户信息的流程和授权码模式相同。

参考资料:

scope 与用户信息对应表

# 使用 Password 模式

不推荐使用此模式,尽量使用其他模式。

# 在控制台配置 OIDC 应用

进入控制台 (opens new window) > 应用 > 应用列表,找到你的应用,点击「配置」,在应用配置中勾选 password。点击「保存」。

password

POST
https://<你的应用域名>.authing.cn/oidc/token

使用登录凭据换取 token

在 Password 模式中,可以直接使用用户的登录凭据换取 OIDC token

Body Paramter
scope
OPTIONAL
string

授权范围,一个字符串,openid 为必选,可选值有 profile email address phone offline_access。不同的值之间用空格分开。profile 是用户基本信息,email 是用户邮箱,address 是用户地址,phone 是用户手机号,offline_access 会返回 refresh_token,用于后续刷新 OIDC Token。默认为 openid profile。

password
REQUIRED
string

密码

phone
OPTIONAL
string

手机号,与邮箱、用户名互斥。

email
OPTIONAL
string

邮箱,与用户名、手机号互斥。

username
OPTIONAL
string

用户名,与邮箱、手机号互斥。

grant_type
REQUIRED
string

必须填 password

client_secret
REQUIRED
string

应用 Secret。

client_id
REQUIRED
string

应用 ID。

200: OK

用户登录凭证正确,返回 OIDC 相关 Token。

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlJJQ015UV9yVFFESlZGYkJEclBDdml0ME9LczBSNWNRX3N1Vmt4alRmOUkifQ.eyJqdGkiOiJXWEl3TldJZ3ZCZ0tXN0lncnVzdXQiLCJzdWIiOiI1ZTg1YWRlZDljYzE4ZTRjYzY3ZTc4MGEiLCJpc3MiOiJodHRwczovL3Rlc3Q4OC5jZWxlYmVzLmxpdmUvb2F1dGgvb2lkYyIsImlhdCI6MTU4NTgxOTExOCwiZXhwIjoxNTg1ODIyNzE4LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHBob25lIG9mZmxpbmVfYWNjZXNzIiwiYXVkIjoiNWU1ZmMzNGZmMTRiYjI1OTkyZWUyNzgxIn0.Tk4npueRIyJPVxrfiOVQgMKEbk4z_KOvOkOGNyVKyeMtSHEcvU8tMRhjBP_ZrISaT4XO-vu_O1tDUBFhCKMDXWnPMwNotbbcRwRdmzueoe1G0YyxHvcuNihAFyfT_99Skaq3TuG7EzeeFuuvkUejFKRmaODraQY1vQrJl_0WNX1f6NZVYNUcOTCslb_R6qNodFQvjfOJv73FyArETKRAKN5sdTtUWuwxf9QfNm5jwJ_iratqSb5GYU-hd6U-47JKzqv_NEEVrGcRSDrW4ICrulOVPduKOwUqwg7VjHqpvAk2cIt5UdgSh2aaj3KpBhRWm2Exp2AY62sP-oLU3qigBQ",
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlJJQ015UV9yVFFESlZGYkJEclBDdml0ME9LczBSNWNRX3N1Vmt4alRmOUkifQ.eyJzdWIiOiI1ZTg1YWRlZDljYzE4ZTRjYzY3ZTc4MGEiLCJiaXJ0aGRhdGUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImdlbmRlciI6IiIsImdpdmVuX25hbWUiOiIiLCJsb2NhbGUiOiIiLCJtaWRkbGVfbmFtZSI6IiIsIm5hbWUiOiIiLCJuaWNrbmFtZSI6IiIsInBpY3R1cmUiOiJodHRwczovL3VzZXJjb250ZW50cy5hdXRoaW5nLmNuL2F1dGhpbmctYXZhdGFyLnBuZyIsInByZWZlcnJlZF91c2VybmFtZSI6IiIsInByb2ZpbGUiOiIiLCJ1cGRhdGVkX2F0IjoiIiwid2Vic2l0ZSI6IiIsInpvbmVpbmZvIjoiIiwidXNlcm5hbWUiOiJwNGdnMnVhcWRjZUB0ZXN0LmNvbSIsImNvbXBhbnkiOiIiLCJicm93c2VyIjoiIiwiZGV2aWNlIjoiIiwibG9naW5zX2NvdW50IjoxLCJyZWdpc3Rlcl9tZXRob2QiOiJkZWZhdWx0OnVzZXJuYW1lLXBhc3N3b3JkIiwiYmxvY2tlZCI6ZmFsc2UsImxhc3RfaXAiOiIxMjQuMjA0LjU2Ljk4IiwicmVnaXN0ZXJfaW5fdXNlcnBvb2wiOiI1ZTE5OTQyMTg4YjAxMzA3ODEyN2MwMjQiLCJsYXN0X2xvZ2luIjoiMjAyMC0wNC0wMlQwOToxODozNy4zNDJaIiwic2lnbmVkX3VwIjoiMjAyMC0wNC0wMlQwOToxODozNy4xODFaIiwiZW1haWwiOiJwNGdnMnVhcWRjZUB0ZXN0LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicGhvbmVfbnVtYmVyIjoiIiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjpmYWxzZSwiYXdzIjoiZGVtbyIsImF1ZCI6IjVlNWZjMzRmZjE0YmIyNTk5MmVlMjc4MSIsImV4cCI6MTU4NTgyMjcxOCwiaWF0IjoxNTg1ODE5MTE4LCJpc3MiOiJodHRwczovL3Rlc3Q4OC5jZWxlYmVzLmxpdmUvb2F1dGgvb2lkYyJ9.XUOWiKi0mpkcw570rCBZVz0wHWz1gBi5N5Bgz8mqU08FA2dlFradoZ9m_pZYlZPHW6A9R54rI7MzONQlt-sDjGqxLkL6wSNrYIJuYlysRldK3E1NRmziVukMQn8jkyq1DLhKK3WzX_ODbkasHTxdFmJ6iAgouuTjdCdcGv1B1ZTXIKJoIgXwMnYjrWbDULkJg_5_o7eP1GCVG8l5UgIRy5YNunEg7nEVLAu0aj-ob613x5k7ceb-jYLjCX2_9PVIEDeE5exGbz-3txhAxn77xjTi7m1-NEhusTHhd_p315fs0ziCYCaXXsO9eRlJ7I78geP87Thq3-vgQH7YgNy8tA",
    "refresh_token": "vRZXKWcvyVE9_kKq26OD2gFyame",
    "expires_in": 3600,
    "token_type": "Bearer",
    "scope": "openid profile email phone offline_access"
}
400: Bad Request

用户登录凭证错误,返回错误信息。

{
    error: 'invalid_grant',
    error_description: 'invalid credentials provided',
}

用户标识字段「email、phone、username」是互斥的,你不能同时传入其中任何两个或以上的参数组合。

参考资料

  1. scope 与用户信息对应表
  2. 什么时候使用 Password 模式?「视频」 (opens new window)
  3. Password 模式仅用于向前兼容「视频」 (opens new window)

# 使用 authingToken 模式

# 在控制台配置 OIDC 应用

进入控制台 (opens new window) > 应用 > 应用列表,找到你的应用,点击「配置」,在应用配置中勾选 authingToken。点击「保存」。

authingToken

POST
https://<你的应用域名>.authing.cn/oidc/token

使用 Authing Token 换取 OIDC token

在 authingToken 模式中,可以直接使用用户的 Authing Token 换取 OIDC token。

Body Paramter
scope
OPTIONAL
string

授权范围,一个字符串,openid 为必选,可选值有 profile email address phone offline_access。不同的值之间用空格分开。profile 是用户基本信息,email 是用户邮箱,address 是用户地址,phone 是用户手机号,offline_access 会返回 refresh_token,用于后续刷新 OIDC Token。默认为 openid profile。

authingToken
REQUIRED
string

用户的 Authing Token。

grant_type
REQUIRED
string

必须填 authingToken

client_secret
REQUIRED
string

应用 Secret。

client_id
REQUIRED
string

应用 ID。

200: OK

用户登录凭证正确,返回 OIDC 相关 Token。

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlJJQ015UV9yVFFESlZGYkJEclBDdml0ME9LczBSNWNRX3N1Vmt4alRmOUkifQ.eyJqdGkiOiJXWEl3TldJZ3ZCZ0tXN0lncnVzdXQiLCJzdWIiOiI1ZTg1YWRlZDljYzE4ZTRjYzY3ZTc4MGEiLCJpc3MiOiJodHRwczovL3Rlc3Q4OC5jZWxlYmVzLmxpdmUvb2F1dGgvb2lkYyIsImlhdCI6MTU4NTgxOTExOCwiZXhwIjoxNTg1ODIyNzE4LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHBob25lIG9mZmxpbmVfYWNjZXNzIiwiYXVkIjoiNWU1ZmMzNGZmMTRiYjI1OTkyZWUyNzgxIn0.Tk4npueRIyJPVxrfiOVQgMKEbk4z_KOvOkOGNyVKyeMtSHEcvU8tMRhjBP_ZrISaT4XO-vu_O1tDUBFhCKMDXWnPMwNotbbcRwRdmzueoe1G0YyxHvcuNihAFyfT_99Skaq3TuG7EzeeFuuvkUejFKRmaODraQY1vQrJl_0WNX1f6NZVYNUcOTCslb_R6qNodFQvjfOJv73FyArETKRAKN5sdTtUWuwxf9QfNm5jwJ_iratqSb5GYU-hd6U-47JKzqv_NEEVrGcRSDrW4ICrulOVPduKOwUqwg7VjHqpvAk2cIt5UdgSh2aaj3KpBhRWm2Exp2AY62sP-oLU3qigBQ",
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlJJQ015UV9yVFFESlZGYkJEclBDdml0ME9LczBSNWNRX3N1Vmt4alRmOUkifQ.eyJzdWIiOiI1ZTg1YWRlZDljYzE4ZTRjYzY3ZTc4MGEiLCJiaXJ0aGRhdGUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImdlbmRlciI6IiIsImdpdmVuX25hbWUiOiIiLCJsb2NhbGUiOiIiLCJtaWRkbGVfbmFtZSI6IiIsIm5hbWUiOiIiLCJuaWNrbmFtZSI6IiIsInBpY3R1cmUiOiJodHRwczovL3VzZXJjb250ZW50cy5hdXRoaW5nLmNuL2F1dGhpbmctYXZhdGFyLnBuZyIsInByZWZlcnJlZF91c2VybmFtZSI6IiIsInByb2ZpbGUiOiIiLCJ1cGRhdGVkX2F0IjoiIiwid2Vic2l0ZSI6IiIsInpvbmVpbmZvIjoiIiwidXNlcm5hbWUiOiJwNGdnMnVhcWRjZUB0ZXN0LmNvbSIsImNvbXBhbnkiOiIiLCJicm93c2VyIjoiIiwiZGV2aWNlIjoiIiwibG9naW5zX2NvdW50IjoxLCJyZWdpc3Rlcl9tZXRob2QiOiJkZWZhdWx0OnVzZXJuYW1lLXBhc3N3b3JkIiwiYmxvY2tlZCI6ZmFsc2UsImxhc3RfaXAiOiIxMjQuMjA0LjU2Ljk4IiwicmVnaXN0ZXJfaW5fdXNlcnBvb2wiOiI1ZTE5OTQyMTg4YjAxMzA3ODEyN2MwMjQiLCJsYXN0X2xvZ2luIjoiMjAyMC0wNC0wMlQwOToxODozNy4zNDJaIiwic2lnbmVkX3VwIjoiMjAyMC0wNC0wMlQwOToxODozNy4xODFaIiwiZW1haWwiOiJwNGdnMnVhcWRjZUB0ZXN0LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicGhvbmVfbnVtYmVyIjoiIiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjpmYWxzZSwiYXdzIjoiZGVtbyIsImF1ZCI6IjVlNWZjMzRmZjE0YmIyNTk5MmVlMjc4MSIsImV4cCI6MTU4NTgyMjcxOCwiaWF0IjoxNTg1ODE5MTE4LCJpc3MiOiJodHRwczovL3Rlc3Q4OC5jZWxlYmVzLmxpdmUvb2F1dGgvb2lkYyJ9.XUOWiKi0mpkcw570rCBZVz0wHWz1gBi5N5Bgz8mqU08FA2dlFradoZ9m_pZYlZPHW6A9R54rI7MzONQlt-sDjGqxLkL6wSNrYIJuYlysRldK3E1NRmziVukMQn8jkyq1DLhKK3WzX_ODbkasHTxdFmJ6iAgouuTjdCdcGv1B1ZTXIKJoIgXwMnYjrWbDULkJg_5_o7eP1GCVG8l5UgIRy5YNunEg7nEVLAu0aj-ob613x5k7ceb-jYLjCX2_9PVIEDeE5exGbz-3txhAxn77xjTi7m1-NEhusTHhd_p315fs0ziCYCaXXsO9eRlJ7I78geP87Thq3-vgQH7YgNy8tA",
    "refresh_token": "vRZXKWcvyVE9_kKq26OD2gFyame",
    "expires_in": 3600,
    "token_type": "Bearer",
    "scope": "openid profile email phone offline_access"
}
400: Bad Request

用户登录凭证错误,返回错误信息。

{
    error: 'invalid_grant',
    error_description: 'invalid credentials provided',
}

# 退出 SSO

如果你使用了 OAuth 2.0、OIDC 或 SAML 实现了单点登录,可以访问以下地址让用户在 OP 登出:

https://<你的域名>.authing.cn/login/profile/logout?redirect_uri=<退出之后的回调地址>

redirect_uri 是退出后你想要返回的地址。

如果你想要在后端退出 SSO,可以自行维护一个 Cookie - Session 的状态,然后设置 Cookie 过期即可。

# 接下来你可能需要

OIDC 常见问题