错误信息
Invalid Compact JWS
jose (JavaScript) — JWSInvalid, code ERR_JWS_INVALID
含义
jose 无法把你的输入拆分成 JWS 紧凑序列化所要求的三个以点分隔的段(header.payload.signature)。从结构上看,你传入的字符串根本不是签名令牌;jose 没有执行任何密码学运算。它相当于 jsonwebtoken 的“jwt malformed”——和那个错误一样,它说明问题出在 JWT 库的上游,即生成或传输这个字符串的环节。
原因
令牌被截断,或与另一个令牌拼接在一起
常见丢失了最后一段的值(来自数据库列、日志行或不完整的复制),或者在同一个请求头中拼接起来的两个令牌,都不再恰好是三段。完整令牌前面的“Bearer ”前缀或外层的 JSON 引号不会触发这条消息——字符串仍然是三段,jose 报出的是“JWS Protected Header is invalid”;末尾的换行符则不会引发错误。
变量中存放的不是令牌
常见undefined 被隐式转换成的字符串“undefined”、cookie 缺失时得到的空字符串,或者不透明的(非 JWT)OAuth 访问令牌——这些都不具备 JWS 结构。失败发生在 jose 的上游。
把 JWE 传给了 JWS API
偶见加密令牌(JWE)有五段,而不是三段。如果你的颁发者对令牌加密,jwtVerify/compactVerify 会在这里拒绝它们——正确的入口是解密(jwtDecrypt),如果其中嵌套了 JWS,再验证这个 JWS。
修复方法
- 1.
清理输入并检查段数
在调用 jose 之前,先去除首尾空白、去掉认证方案前缀(如 Bearer),并断言段数——这样得到的是精确的应用层错误,而不是笼统的解析失败。
js const token = raw.replace(/^Bearer\s+/i, "").trim(); const segments = token.split(".").length; if (segments !== 3) throw new Error(`expected JWS (3 segments), got ${segments}`); - 2.
把 JWE 令牌交给解密流程
如果数出来是五段,说明令牌是加密的。应使用 jwtDecrypt 和接收方的私钥,而不是 jwtVerify。
js import { jwtDecrypt } from "jose"; const { payload } = await jwtDecrypt(token, privateKey); // 5-segment JWE - 3.
追查字符串是在哪里构造的
如果清理后的值仍然报错,就在每一跳——客户端、网关、存储、处理程序——记录它的长度和开头几个字符。签名 JWT 的第一段是 base64url 编码的 JSON,因此真正的令牌以“eyJ”开头(即 '{"' 的编码);从哪一跳开始不再如此,损坏就发生在哪一跳。
js console.log(token.length, JSON.stringify(token.slice(0, 12))); // healthy: 300+ 'eyJhbGciOiJF'