文档写的云里雾里,不深入了解 很难搞清楚 okx 几个登录之间的关系。
自己整理了一张图(如有错漏,欢迎指正):

下面是关于 OKX OAuth 2.0 集成的关键点整理,以及 Web 端使用 PKCE 模式授权码模式 的详细流程整理:


一、OKX OAuth 2.0 集成关键点

  1. 功能和特点

    • 基于 OAuth 2.0 协议(RFC 6749)以及部分 OAuth 2.1 草案新特性。
    • 用户无需提供 API Key 或登录密码,只需授权即可完成交易。
    • 支持 Web移动应用 两种接入场景。
  2. 授权模式

    • 授权码模式
      • 适用于有服务器的应用,需存储 client_secret
      • 更适合服务器与 OKX OAuth 服务端进行密钥交互。
    • PKCE 模式
      • 适用于无服务器或不愿后端参与授权的场景。
      • 使用临时密钥 code_verifier,无需存储 client_secret
  3. 令牌机制

    • 访问令牌(Access Token):有效期 1 小时,用于调用 API。
    • 刷新令牌(Refresh Token):有效期 3 天,用于刷新访问令牌。
    • 当刷新令牌失效后,需重新授权获取新令牌。
  4. 安全性措施

    • PKCE 模式需绑定设备号(term_id),防止令牌被盗用。
    • 授权码模式需校验 IP 地址是否在白名单内。

二、授权码流程

1. 第三方应用发起授权

调用 SDK API 发起授权接口。

const { OKEXOAuthSDK } = window;
if (OKEXOAuthSDK) {
    OKEXOAuthSDK.authorize({
        response_type: 'code',
        access_type: 'offline',
        client_id: 'YOUR_CLIENT_ID',
        redirect_uri: encodeURIComponent("https://example.com/oauth/callback"),
        scope: 'read_only,trade',
        state,
        channelId: "xxxxxxxx"
    });
} else {
    console.error('sdk has not been loaded');
}

请求参数

参数名参数类型是否必须描述
response_typeString必须为 code,不传默认 code
access_typeString必须为 offline
client_idString第三方应用 client_id
redirect_uriString回调地址,即授权成功后的跳转地址。需要做 URL 编码。
如果不在注册的跳转地址白名单中,则会授权失败。
scopeString授权范围。多个权限用半角逗号分隔:read_only(只读)或 trade(交易)
stateString随机生成的一串字符,用于防止 CSRF 攻击。可通过生成 State 接口获取
code_challengeStringcode_verifier 经过 hash 和 base64 urlencode 处理后的字符
code_challenge_methodString必须为 s256,授权码模式下,该字段非必填

2. 用户授权

第三方应用发起授权后,会跳转到 OKX 授权页面,用户可以选择同意授权或者拒绝授权,并可以调整授权范围。

3. 跳转回第三方应用

用户同意授权后,会跳转到发起授权请求时指定的回调地址。授权码和 state 会以参数方式一起返回。

返回示例

GET https://example.com/oauth/callback?code=f8d24cf76b5d41ccafeb646be65e383965TtKv&state=4YrCgpWQrsD8jdnv

4. 用授权码换取令牌

第三方应用拿到上一步获取的授权码,第三方后端服务调用 REST API 获取令牌。

POST /v5/users/oauth/token

body {
    "grant_type": "authorization_code",
    "code": "f8d24cf76b5d41ccafeb646be65e383965TtKv",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
}

请求参数

参数名参数类型是否必须描述
grant_typeStringauthorization_code:获取访问令牌
client_idString第三方应用 client_id
client_secretString第三方应用 client_secret
codeString发起授权请求成功后获得的授权码
code_verifierString可选初始请求中生成的 code_verifier,对应于发起授权请求中的 code_challenge 参数
term_idString设备号,用于绑定令牌,可通过 SDK API 生成设备号接口获取

成功返回示例

{
    "access_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4NTI4ODYwLCJzdWIiOiIxMC4yNTQuMjcuMTIwIiwiYW95IjoiOSIsInZlciI6IjEiLCJkZXYiOiIxMjM0NTY3ODkwMTIzNDU2NyIsImd0eSI6ImF1dGhvcml6ZSJ9.u0mnMizduK1zGBY9Ysej2OZWCRpdMzLf7JXehZUDYxIWOCQo6wOX-rPk7QtR9TfYN-Rg64cUkjXbIquwN8rNKg",
    "refresh_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4Nzg0NDYwLCJzdWIiOiIxMC4yNTQuMjcuMTIwIiwiYW95IjoiOSIsInZlciI6IjEiLCJkZXYiOiIxMjM0NTY3ODkwMTIzNDU2NyIsImd0eSI6InJlZnJlc2gifQ.CyD4NMAzuBAiTPN58xzLQVUwOnFEKaXIRUQacx77kHZQESHRqH83b8SEiFGKkprDlcguMn1A9fHIzPrulFCsnw",
    "token_type": "bearer",
    "expires_in": 3600
}

5. 刷新令牌

参看 REST API 刷新令牌接口。

6. 撤销令牌

参看 REST API 撤销令牌接口。


三、PKCE 流程

1. 第三方应用发起授权

调用 SDK API 发起授权接口。

const { OKEXOAuthSDK } = window;
if (OKEXOAuthSDK) {
    OKEXOAuthSDK.authorize({
        response_type: 'code',
        access_type: 'online',
        client_id: 'YOUR_CLIENT_ID',
        redirect_uri: encodeURIComponent("https://example.com/oauth/callback"),
        scope: 'read_only,trade',
        code_challenge: '198ba82d6817cc0d42b23cf5e90262c8a3976b90a444e802f341593d784fb89d',
        code_challenge_method: 's256',
        state
    });
} else {
    console.error('sdk has not been loaded');
}

请求参数

参数名参数类型是否必须描述
response_typeString必须为 code,不传默认 code
access_typeString必须为 online
client_idString第三方应用 client_id
redirect_uriString回调地址,即授权成功后的跳转地址。需要做 URL 编码。
如果不在注册的跳转地址白名单中,则会授权失败。
scopeString授权范围。多个权限用半角逗号分隔:read_only(只读)或 trade(交易)
stateString随机生成的一串字符,用于防止 CSRF 攻击。可通过 SDK API 生成 State 接口获取
code_challengeStringcode_verifier 经过 hash 和 base64 urlencode 处理后的字符
code_challenge_methodString必须为 s256,PKCE 模式下,该字段必填

2. 用户授权

第三方应用发起授权后,会跳转到 OKX 授权页面,用户可以选择同意授权或者拒绝授权,并可以调整授权范围。

3. 跳转回第三方应用

用户同意授权后,会跳转到发起授权请求时指定的回调地址。授权码和 state 会以参数方式一起返回。

返回示例

GET https://example.com/oauth/callback?code=f8d24cf76b5d41ccafeb646be65e383965TtKv&state=4YrCgpWQrsD8jdnv

4. 用授权码换取令牌

第三方应用拿到上一步获取的授权码,第三方 WEB 应用调用 SDK API 获取令牌。

OKEXOAuthSDK.requestToken({
    grant_type: 'authorization_code',
    client_id: 'YOUR_CLIENT_ID',
    code_verifier: 'YOUR_CODE_VERIFIER',
    code,
    term_id: termId
}).then((res) => {
    const token = res.access_token;
    console.log(`获取 token 成功:${token}`)
}).catch((err) => {
    console.error(err?.msg);
});

请求参数

参数名参数类型是否必须描述
grant_typeStringauthorization_code:获取访问令牌
client_idString第三方应用 client_id
code_verifierString初始请求中生成的 code_verifier,对应于发起授权请求中的 code_challenge 参数
codeString发起授权请求成功后获得的授权码
term_idString设备号,用于绑定令牌,可通过 SDK API 生成设备号接口获取

成功返回示例

{
    "access_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4NTI4ODYwLCJzdWIiOiIxMC4yNTQuMjcuMTIwIiwiYW95IjoiOSIsInZlciI6IjEiLCJkZXYiOiIxMjM0NTY3ODkwMTIzNDU2NyIsImd0eSI6ImF1dGhvcml6ZSJ9.u0mnMizduK1zGBY9Ysej2OZWCRpdMzLf7JXehZUDYxIWOCQo6wOX-rPk7QtR9TfYN-Rg64cUkjXbIquwN8rNKg",
    "refresh_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4Nzg0NDYwLCJzdWIiOiIxMC4yNTQuMjcuMTIwIiwiYW95IjoiOSIsInZlciI6IjEiLCJkZXYiOiIxMjM0NTY3ODkwMTIzNDU2NyIsImd0eSI6InJlZnJlc2gifQ.CyD4NMAzuBAiTPN58xzLQVUwOnFEKaXIRUQacx77kHZQESHRqH83b8SEiFGKkprDlcguMn1A9fHIzPrulFCsnw",
    "token_type": "bearer",
    "expires_in": 3600
}

5. 刷新令牌

参看 SDK API 刷新令牌接口。

6. 撤销令牌

参看 SDK API 撤销令牌接口。


四、附录 REST API

  • 刷新令牌:访问令牌过期后使用刷新令牌获取新令牌。
  • 撤销令牌:可以通过 API 或 SDK 主动撤销令牌。

一、授权码模式令牌请求

1. 获取令牌(授权码模式)

请求示例

POST /v5/users/oauth/token
body { 
  "grant_type": "authorization_code", 
  "code": "f8d24cf76b5d41ccafeb646be65e383965TtKv", 
  "client_id": "YOUR_CLIENT_ID", 
  "client_secret": "YOUR_CLIENT_SECRET"
}

请求参数

参数名参数类型是否必须描述
grant_typeStringauthorization_code: 获取访问令牌
client_idString第三方应用 client_id
client_secretString第三方应用 client_secret
codeString发起授权请求成功后获得的授权码
code_verifierString可选初始请求中生成的 code verifier,对应于发起授权请求中的 code_challenge 参数
term_idString设备号,用于绑定令牌,可通过生成设备号接口获取

返回参数

参数名参数类型描述
access_tokenString访问令牌
refresh_tokenString刷新令牌
token_typeStringBearer
expires_inNumber访问令牌有效期,单位为秒

返回示例

{
  "access_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4NTI4ODYwLCJzdWIiOiIxMC4yNTQuM...
  "refresh_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4Nzg0NDYwLCJzdWIiOiIxMC4yNTQuM...
  "token_type": "bearer",
  "expires_in": 3600
}

2. 刷新令牌(授权码模式)

请求示例

POST /v5/users/oauth/token
body { 
  "grant_type": "refresh_token", 
  "refresh_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg0MzU4NTk3MDc2MTFERjk0OEZFNUNFQTUwZUlURiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODQzNTg1OSwiZXhwIjoxNjM4Njk1MDU5LCJzdWIiOiIxMC4yNTQuMjcuMTIwIiwiYW95IjoiOSIsInZlciI6IjEiLCJndHkiOiJyZWZyZXNoIn0.Kfs0jBfbfsrnc4HE44ShXNxp2TuHio5R8GpBebEx5rALA_BTgqBtJRwC6330TdU9J6kOZiNp97Zd8RNPJYjXHw", 
  "client_id": "YOUR_CLIENT_ID", 
  "client_secret": "YOUR_CLIENT_SECRET"
}

请求参数

参数名参数类型是否必须描述
grant_typeStringrefresh_token: 刷新访问令牌
client_idString第三方应用 client_id
client_secretString第三方应用 client_secret
refresh_tokenString刷新令牌

返回参数

参数名参数类型描述
access_tokenString访问令牌
refresh_tokenString刷新令牌
token_typeStringBearer
expires_inNumber访问令牌有效期,单位为秒

返回示例

{
  "access_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4NTI4ODYwLCJzdWIiOiIxMC4yNTQuM...
  "refresh_token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg1MjUyNjA2MzI0MzM1ODE5NkJDQkFFMkQ3a0hrUiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODUyNTI2MCwiZXhwIjoxNjM4Nzg0NDYwLCJzdWIiOiIxMC4yNTQuM...
  "token_type": "bearer",
  "expires_in": 3600
}

3. 撤销令牌(授权码模式)

撤销访问令牌或刷新令牌

请求示例

POST /v5/users/oauth/revoke
body { 
  "token": "eyJhbGciOiJIUzUxMiIsImNpZCI6ImFhIn0.eyJqdGkiOiJleDExMDE2Mzg0MzU4NTk3MDc2MTFERjk0OEZFNUNFQTUwZUlURiIsInVpZCI6IlEybEZxMnY2N0VybnVMZ0o1cFYzdUE9PSIsIm1pZCI6InFGbG5lVEc4dnlJeDNMSnNSa29qZ0E9PSIsImlhdCI6MTYzODQzNTg1OSwiZXhwIjoxNjM4Njk1MDU5LCJzdWIiOiIxMC4yNTQuMjcuMTIwIiwiYW95IjoiOSIsInZlciI6IjEiLCJndHkiOiJyZWZyZXNoIn0.Kfs0jBfbfsrnc4HE44ShXNxp2TuHio5R8GpBebEx5rALA_BTgqBtJRwC6330TdU9J6kOZiNp97Zd8RNPJYjXHw"
}

请求参数

参数名参数类型是否必须描述
tokenString待撤销的令牌

返回参数

参数名参数类型描述
codeString错误码
msgString错误描述

返回示例

{
  "code": "0",
  "msg": ""
}

二、PKCE 模式令牌请求

1. 获取令牌(PKCE模式)

代码示例

OKEXOAuthSDK.requestToken({
  grant_type: 'authorization_code',
  client_id: 'YOUR_CLIENT_ID',
  code_verifier: 'YOUR_CODE_VERIFIER',
  code,
  term_id: termId
}).then((res) => {
  const token = res.data.access_token;
  console.log(`获取 token 成功:${token}`);
}).catch((err) => {
  console.error(err?.msg);
});

请求参数

参数名参数类型是否必须描述
grant_typeStringauthorization_code: 获取访问令牌
client_idString第三方应用 client_id
code_verifierString初始请求中生成的 code verifier,对应于发起授权请求中的 code_challenge 参数,仅适用于 PKCE 模式
codeString发起授权请求成功后获得的授权码
term_idString设备号,用于绑定令牌,可通过生成设备号接口获取

2. 刷新令牌(PKCE模式)

代码示例

OKEXOAuthSDK.requestToken({
    grant_type: 'refresh_token', 
    client_id: 'YOUR_CLIENT_ID', 
    your_refresh_token, 
    term_id: termId
}).then((res) => {
    const token = res.data.access_token;
    console.log(`获取 token 成功:${token}`)
}).catche((err) => {
    console.err(err?.msg)
});

请求参数

参数名参数类型是否必须描述
grant_typeStringrefresh_token: 刷新访问令牌
client_idString第三方应用 client_id
refresh_tokenString刷新令牌
term_idString设备号,用于校验和令牌绑定的设备号是否一致

3. 撤销令牌(PKCE模式)

代码示例

OKEXOAuthSDK.revokeToken({ token, term_id: termId })
  .then((res) => {
    console.log('取消 token 成功');
  })
  .catch((err) => {
    console.error(err?.msg);
  });

请求参数

参数名参数类型是否必须描述
term_idString设备号,用于校验和令牌绑定的设备号是否一致
tokenString待撤销的令牌

错误码

错误码HTTP 状态码error_description (中)error_description (英)
53000400无效的 tokenInvalid token
53001400无效的授权,用户已取消授权Authorization canceled
53002400token 已过期Token expired
53003400token 已撤销Token revoked
53004400用户已被冻结Account has been frozen
53005400刷新令牌不正确Wrong refresh token
53006401无效的设备Invalid device
53009400授权失败Authorization failed
53010400参数 {0} 错误Parameter {0} error
53011400必填参数 {0} 不能为空Parameter {0} cannot be empty
53012400授权码已过期Authorization code expired
53013400接口权限不足You need higher permissions
53014401无效的 IPInvalid IP
53015400参数 {参数名} 长度超过最大限制 {长度}Parameter {name} length cannot exceed {max length}
53016400无效的 redirect_uriInvalid redirect_uri