# 验证 Token

‌JWT Token 是用户登录后的唯一凭证,验证 Token 合法性和用户的登录状态有五种方法:‌

  1. 本地验证 HS256 算法签名的 IdToken
  2. 本地验证 RS256 算法签名的 IdToken
  3. 在线验证 OIDC AccessToken
  4. 在线验证 OIDC IdToken
  5. 在线验证 OAuth2 AccessToken

请根据以下信息选择验证 Token 的方式:

  1. 如果你直接调用了登录方法(loginByEmail、loginByPhone、loginByUsername)或使用了 OIDC 授权或者想要验证用户信息中的 token 字段,请先查找认证时根据你的应用签名算法配置,如果是 HS256,选择第一种方式验证,如果是 RS256,选择第二种方式验证;
  2. 如果你使用了 OIDC 协议接入了其他 SaaS,该 SaaS 想要通过 API 验证 Token,请使用第三种方法;
  3. 如果你使用了 OIDC 协议,且不想研究如何在本地验证 IdToken,请使用第四种方法;
  4. 如果你使用了 OAuth 2.0 协议,那么请使用第五种方式;

# 使用应用密钥验证 Token

如果你直接调用了登录方法(loginByEmail、loginByPhone、loginByUsername)或使用了 OIDC 授权,且 IdToken 签名算法类型设置为 HS256 时请使用此方式验证 Token。‌

密钥在控制台中应用的详情中可以获取到,如下图所示:

以下验证合法性的代码以 Node 为例(需要安装 jsonwebtoken)。

const jwt = require('jsonwebtoken');
try {
  let decoded = jwt.verify('JSON Web Token from client', 'your_secret'),
    expired = Date.parse(new Date()) / 1000 > decoded.exp;
  if (expired) {
    // 过期
  } else {
    // 合法也没过期,正常放行
  }
} catch (error) {
  // 不合法
}

为了避免在客户端暴露应用密钥,建议在服务端通过应用密钥验证 id_token 的合法性。‌

如果你不知道如何在后端处理 OIDC 授权登录的回调,请参考 Github 上的示例代码:oidc-demo (opens new window)。‌

# 使用应用公钥验证 RS256 算法签名的 IdToken

如果使用 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'
}

# 在线验证 OIDC AccessToken

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

检验 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
}

# 在线验证 OIDC IdToken

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 不合法',
}

# 在线验证 OAuth2 AccessToken

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

检验 token 状态

可以检测 access_token 的状态。

Headers
Content-Type
REQUIRED
string

application/x-www-form-urlencoded

Authorization
OPTIONAL
string

在控制台应用配置详情,「配置 OAuth2.0 身份提供商」选项卡中,配置检验 token 身份验证方式为 client_secret_basic 时必填,形式为:Basic base64(应用 ID + ':' + 应用 Secret)

Form Data Parameters
token
REQUIRED
string

要检验的 token 值

token_type_hint
OPTIONAL
string

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

client_id
OPTIONAL
string

应用 ID,在控制台应用配置详情,「配置 OAuth2.0 身份提供商」选项卡中,配置检验 token 身份验证方式为 client_secret_postnone 时必填

client_secret
OPTIONAL
string

应用 Secret,在控制台应用配置详情,「配置 OAuth2.0 身份提供商」选项卡中,配置检验 token 身份验证方式为 client_secret_post 时必填

200: OK

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

{
  "active": true,
  "sub": "5dc10851ebafee30ce3fd5e9",
  "client_id": "5cded22b4efab31716fa665f",
  "exp": 1602423020,
  "iat": 1602419420,
  "iss": "https://core.authing.cn/oauth",
  "jti": "SaPg48dbO66T77xkT8wy0",
  "scope": "user",
  "token_type": "Bearer"
}

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

{
  "active": false
}