Salesforce OAuth: JWT Bearer 流程

Posted by Peter Dong on January 8, 2023

JWT Bearer Flow 是一种 OAuth 流程, 其中外部应用程序(也称为客户端)向Salesforce发送一个称为 JWT 的签名 JSON 字符串, 以获得一个访问令牌(access token). 然后外部应用程序可以使用该访问令牌来读取和写入 Salesforce 中的数据.

与其他一些 OAuth 流程不同的是, JWT流程不需要最终用户操作. 外部应用程序发送 JWT 并进行自我认证,无需人工干预.

在本文开始之前, 先来简单介绍一下 JWT 的构成, 以此希望大家对整个JWT Bearer Flow的理解更加的清楚.

JWT 的数据结构

真实的 JWT 大概像下面这样:

img

JWT 是一个很长的字符串, 中间用点(.)分隔成三个部分. 需要注意的是: JWT 内部是没有换行的.

这三个部分组成如下:

  • Header(头部)
  • Payload(载荷)
  • Signature(签名)

JWT 的头部有两部分信息:

  • 声明类型, JWT 令牌统一写为JWT
  • 声明加密的算法, 默认为HMAC SHA256, 简写后HS256

示例:

1
2
3
4
{
  "alg": "HS256",
  "typ": "JWT"
}

然后将头部通过 Base64URL 加密算法转成字符串, 构成了第一部分.

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据.JWT 规定了7个官方字段, 但是这些字段不是必须的但是是推荐使用的.

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

示例:

1
2
3
4
5
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

这部分的数据也是经过 Base64Url 进行加密的,这部分加密的内容组成了 JWT 的第二部分.

请注意:针对令牌这部分的签名已经被防范篡改.但是这部分还是可以被解密的,因此请不要将任何密钥放到这部分的数据中,除非你的密钥是已经加密过的密钥.

Signature

Signature 部分是对前两部分的签名,防止数据篡改. 首先,需要指定一个密钥(secret).这个密钥只有服务器才知道,不能泄露给用户.

例如,如果你希望使用 HMAC SHA256 算法来进行签名,那么这个算法中使用的数据为:

1
2
3
4
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

签名的作用主要用于校验传输的令牌(Token)数据没有在过程中被篡改.

算出签名以后, 将上面的三个部分用.连接成一个完整的字符串, 就构成了最终的jwt.

Salesforce OAuth JWT Flow

接下来让我们开始今天的内容.在这个例子中,我们将介绍如何使用 Salesforce 中的 JWT 流程获取 Access Token.

生成 private key 和 cert

作用: 外部应用程序使用 private key 来签名 JWT, 而数字证书(cert)则由 Salesforce 用来验证签名并签发访问令牌.

使用 JWT Bearer Token flow 的第一步是创建证书. 为了能够创建证书, 你需要安装 OpenSSL

步骤1: 创建 RSA private key

运行以下OpenSSL命令:

1
openssl genrsa -des3 -passout pass:xxxx -out server.pass.key 2048

结果: img

步骤2: 从server.pass.key文件中创建 key file

运行以下OpenSSL命令:

1
2
openssl rsa -passin pass:xxxx -in server.pass.key -out server.key
rm server.pass.key (可选的, 可以删除这个文件)

结果: img

步骤3: 生成证书

运行以下OpenSSL命令:

1
openssl req -new -key server.key -out server.csr

结果: img

步骤4: 生成SSL证书

运行以下OpenSSL命令:

1
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

结果: img

在 Salesforce 中创建一个 Connected App

为了连接到我们的Salesforce org,我们需要创建一个Connected App.如果你是在 Lightning 或者 Classic中,创建方式会略有不同.

创建 connected app

  • Lightning: SetupApp Manager, 点击 New Connected App
  • Classic: Setup ⇨ Apps, 在 Connected Apps 列表选项中, 点击 New.
  1. 填入 NameEmail
  2. 开启 Enable OAuth Settings
  3. Callback URL: https://login.salesforce.com/services/oauth2/callback
  4. 开启 Use digital signatures, 上传在上一步创建的数字证书(server.crt)
  5. 选择 OAuth 范围:
    1. Manage user data via APIs (api)
    2. Manage user data via Web browsers (web)
    3. Perform requests at any time (refresh_token, offline_access)
  6. 点击 Save

预先批准 connected app

为什么需要这一步骤呢? 先说结论, 如果没有这一步, 直接生成 JWT Token, 然后调用 Salesforce API 获取 Access Token是会报错的, 如下:

1
 {"error":"invalid_grant","error_description":"user hasn't approved this consumer"}

从技术上讲, 我们的应用程序发送的 JWT Token 进行认证, 但是 salesforce 此时并不知道哪些资源是可用的, 并得到了用户的许可. 通常情况下, 我们可以发送 scope 参数作为 JWT 的一部分去实现,但 Salesforce 不支持这样做. 参考文档.

简单地说, 这意味着在使用 jwt flow 之前至少获得一次刷新令牌(下面的第二个选项)[选项2], 或执行后端审批[下面的第一个选项].

这里提供了几个不同的选项可以做到这一点:

  • 选项1: 管理员从 Salesforce 的 connected app 中进行审批
  • 选项2:User-Agent OAuth Flow 预先批准 connected app

选项1

执行如下步骤:

  • Setup -> App Manager -> YOUR APP -> Manage -> Edit Policies
  • Permitted Users 选择 Admin approved users are pre-authorized
  • 点击 Save.
  • 点击 Manage Profiles
  • 选择 System Administrator
  • 点击 Save

选项2

将下面链接中的参数替换为你自己的, 然后复制链接到浏览器中:

1
https://<your instance>.salesforce.com/services/oauth2/authorize?response_type=token&client_id=<consumer key>&redirect_uri=https://login.salesforce.com/services/oauth2/callback

img

创建 JWT Token

你可以使用你喜欢的开发语言的库来创建 JWT Token,但我这里只使用一个在线工具来演示.

JWT Header

1
2
3
4
{
  "alg": "RS256",
  "typ": "JWT"
}

JWT Payload

1
2
3
4
5
6
{
  "iss": "Consumer key",
  "sub": "Username",
  "aud": "https://<login or test>.salesforce.com",
  "exp": "now + 2 minutes in Unix timestamp"
}

其中 exp 的时间, 可以通过将下面JS复制到 chrome console里:

1
2
3
d = new Date(); 
d.setSeconds(d.getSeconds() + 180); // 有效期为3分钟
d.valueOf();

Signature

1
2
3
4
5
RSASHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  Private Key
)

从之前步骤中生成的 Server.Key 中复制 Private Key

通过 POSTMAN 使用 JWT 获取 Access Token

复制上一个步骤生成的 JWT Token:

img

总结

JWT flow 在 Salesforce 生态系统中被广泛使用,以使外部应用程序能够访问 Salesforce 数据, 而无需用户手动干预. 通过向Salesforce 发送签名的 jwt, 我们获得一个访问令牌(Access Token). 上面的步骤应该适用于您的外部应用程序所使用的任何编程语言(java,node,python等).


Buy Me a Coffee