You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js与浏览器环境下Salesforce JWT签名报错修复咨询

问题描述

我希望在Node.js和浏览器环境中实现Salesforce的OAuth 2.0 JWT Bearer Flow认证,已在Windows系统通过以下OpenSSL命令生成公钥和私钥:

openssl req -x509 -sha256 -nodes -days 36500 -newkey rsa:2048 -keyout salesforce.key -out salesforce.crt

尝试使用panva/jose库通过以下TypeScript代码进行JWT签名:

import { SignJWT } from "jose";
import serverKey from "../../../credential/salesforce.key?raw";

export async function jwtSign() {
    const assertion = await new SignJWT({ prn: import.meta.env.VITE_SF_MAIL })
        .setProtectedHeader({ alg: 'RS256' })
        .setIssuer(import.meta.env.VITE_SF_CLIENT_ID)
        .setAudience(import.meta.env.VITE_SF_AUDIENCE)
        .setExpirationTime('15h')
        .sign(new TextEncoder().encode(serverKey));
    return assertion;
}

但出现以下错误:

Key must be one of type KeyObject or CryptoKey. Received an instance of Uint8Array
TypeError: Key must be one of type KeyObject or CryptoKey. Received an instance of Uint8Array
    at asymmetricTypeCheck (D:\repository\PROJECT\node_modules\jose\dist\node\cjs\lib\check_key_type.js:17:15)
    at checkKeyType (D:\repository\PROJECT\node_modules\jose\dist\node\cjs\lib\check_key_type.js:44:9)
    at FlattenedSign.sign (D:\repository\PROJECT\node_modules\jose\dist\node\cjs\jws\flattened\sign.js:55:41)
    at CompactSign.sign (D:\repository\PROJECT\node_modules\jose\dist\node\cjs\jws\compact\sign.js:14:43)
    at SignJWT.sign (D:\repository\PROJECT\node_modules\jose\dist\node\cjs\jwt\sign.js:22:20)
    at Module.jwtSign (D:/repository/PROJECT/src/lib/app/service/func.ts:10:9)
    at load (D:/repository/PROJECT/src/routes/+page.server.ts:8:10)
    at load_server_data (file:///D:/repository/PROJECT/node_modules/@sveltejs/kit/src/runtime/server/page/load_data.js:32:41)
    at file:///D:/repository/PROJECT/node_modules/@sveltejs/kit/src/runtime/server/page/index.js:168:19
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

请问如何修复该错误?或者是否有其他实现方式?

解决方案

一、修复当前panva/jose的错误问题

错误核心是直接将私钥文本转成Uint8Array传入sign方法,而jose库要求传入**KeyObject(Node.js环境)或CryptoKey(浏览器环境)**类型的密钥,需先解析私钥为对应类型:

1. Node.js环境处理方式

使用jose提供的importPKCS8方法解析PEM格式私钥:

import { SignJWT, importPKCS8 } from "jose";
import serverKey from "../../../credential/salesforce.key?raw";

export async function jwtSign() {
    // 解析PEM私钥为Node.js的KeyObject
    const privateKey = await importPKCS8(serverKey, 'RS256');
    
    const assertion = await new SignJWT({ prn: import.meta.env.VITE_SF_MAIL })
        .setProtectedHeader({ alg: 'RS256' })
        .setIssuer(import.meta.env.VITE_SF_CLIENT_ID)
        .setAudience(import.meta.env.VITE_SF_AUDIENCE)
        .setExpirationTime('15h')
        .sign(privateKey); // 传入解析后的KeyObject
    return assertion;
}

2. 浏览器环境处理方式

浏览器中同样可用importPKCS8解析私钥,但强烈不建议在浏览器直接暴露私钥,签名逻辑最好放在后端,避免密钥泄露。若必须在前端处理,代码示例如下:

import { SignJWT, importPKCS8 } from "jose";

// 私钥需通过安全渠道获取,此处仅作示例
export async function jwtSign(serverKey: string) {
    const privateKey = await importPKCS8(serverKey, 'RS256');
    
    const assertion = await new SignJWT({ prn: import.meta.env.VITE_SF_MAIL })
        .setProtectedHeader({ alg: 'RS256' })
        .setIssuer(import.meta.env.VITE_SF_CLIENT_ID)
        .setAudience(import.meta.env.VITE_SF_AUDIENCE)
        .setExpirationTime('15h')
        .sign(privateKey);
    return assertion;
}

二、其他实现方式

1. 使用jsonwebtoken库(仅Node.js环境)

如果只需要支持Node.js,可使用更传统的jsonwebtoken库:

import jwt from 'jsonwebtoken';
import fs from 'fs';
import path from 'path';

export function jwtSign() {
    const privateKey = fs.readFileSync(path.resolve(__dirname, '../../../credential/salesforce.key'), 'utf8');
    
    const assertion = jwt.sign(
        { prn: process.env.VITE_SF_MAIL },
        privateKey,
        {
            algorithm: 'RS256',
            issuer: process.env.VITE_SF_CLIENT_ID,
            audience: process.env.VITE_SF_AUDIENCE,
            expiresIn: '15h'
        }
    );
    return assertion;
}

2. 后端签名+前端请求(更安全的跨环境方案)

鉴于浏览器暴露私钥的高风险,推荐将JWT签名逻辑放在Node.js后端,前端通过API请求获取已签名的JWT,再用该JWT发起Salesforce的OAuth请求:

  • 后端:实现接口完成JWT签名并返回给前端
  • 前端:调用接口拿到JWT后,发起Salesforce认证请求

此方案既规避了浏览器环境的密钥泄露风险,又能实现跨环境的认证流程。

内容的提问来源于stack exchange,提问作者dyn-do

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:51:18