server: bump jsonwebtoken to 10.4, on the rust_crypto provider
10.x makes the crypto backend pluggable and its default feature set
enables NEITHER provider, so the crate compiles clean and then panics on
the first sign or verify: "Could not automatically determine the
process-level CryptoProvider". cargo check was clean while 14 of 20
synckit_auth tests failed, which would have shipped a server that panics
on every sync login.
Provider is rust_crypto (Max's call, 2026-07-22, standing preference for
all projects): the same pure-Rust stack already backing sha2, hmac, and
chacha20poly1305 here. The aws_lc_rs alternative was measured, not
assumed — both are near-free on dependency count (rust_crypto nets
+ed25519/-untrusted, and the rsa crate it pulls was already in the tree
via apple-codesign and authenticode), so this is a stack preference
rather than a cost tradeoff.
Existing tokens keep working. A sync JWT built independently as raw
HMAC-SHA256 over the base64url header/payload was pinned BEFORE the bump
and still validates after, so nothing in the field is logged out on
deploy; its exp is year-2100 so the vector will not rot, and it asserts a
wrong secret is still rejected. 1890 lib + 1190 integration green,
clippy clean at --all-targets --all-features.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3 files changed,
+60 insertions,
-4 deletions
| 2277 |
2277 |
|
"cfg-if",
|
| 2278 |
2278 |
|
"cpufeatures 0.2.17",
|
| 2279 |
2279 |
|
"curve25519-dalek-derive",
|
|
2280 |
+ |
"digest 0.10.7",
|
| 2280 |
2281 |
|
"fiat-crypto",
|
| 2281 |
2282 |
|
"rand_core 0.6.4",
|
| 2282 |
2283 |
|
"rustc_version",
|
| 2283 |
2284 |
|
"subtle",
|
|
2285 |
+ |
"zeroize",
|
| 2284 |
2286 |
|
]
|
| 2285 |
2287 |
|
|
| 2286 |
2288 |
|
[[package]]
|
| 2646 |
2648 |
|
]
|
| 2647 |
2649 |
|
|
| 2648 |
2650 |
|
[[package]]
|
|
2651 |
+ |
name = "ed25519"
|
|
2652 |
+ |
version = "2.2.3"
|
|
2653 |
+ |
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2654 |
+ |
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
|
2655 |
+ |
dependencies = [
|
|
2656 |
+ |
"pkcs8",
|
|
2657 |
+ |
"signature",
|
|
2658 |
+ |
]
|
|
2659 |
+ |
|
|
2660 |
+ |
[[package]]
|
|
2661 |
+ |
name = "ed25519-dalek"
|
|
2662 |
+ |
version = "2.2.0"
|
|
2663 |
+ |
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2664 |
+ |
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
|
2665 |
+ |
dependencies = [
|
|
2666 |
+ |
"curve25519-dalek",
|
|
2667 |
+ |
"ed25519",
|
|
2668 |
+ |
"serde",
|
|
2669 |
+ |
"sha2 0.10.9",
|
|
2670 |
+ |
"subtle",
|
|
2671 |
+ |
"zeroize",
|
|
2672 |
+ |
]
|
|
2673 |
+ |
|
|
2674 |
+ |
[[package]]
|
| 2649 |
2675 |
|
name = "either"
|
| 2650 |
2676 |
|
version = "1.16.0"
|
| 2651 |
2677 |
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 4011 |
4037 |
|
|
| 4012 |
4038 |
|
[[package]]
|
| 4013 |
4039 |
|
name = "jsonwebtoken"
|
| 4014 |
|
- |
version = "9.3.1"
|
|
4040 |
+ |
version = "10.4.0"
|
| 4015 |
4041 |
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 4016 |
|
- |
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
|
4042 |
+ |
checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc"
|
| 4017 |
4043 |
|
dependencies = [
|
| 4018 |
4044 |
|
"base64 0.22.1",
|
|
4045 |
+ |
"ed25519-dalek",
|
|
4046 |
+ |
"getrandom 0.2.17",
|
|
4047 |
+ |
"hmac 0.12.1",
|
| 4019 |
4048 |
|
"js-sys",
|
|
4049 |
+ |
"p256",
|
|
4050 |
+ |
"p384",
|
| 4020 |
4051 |
|
"pem",
|
| 4021 |
|
- |
"ring",
|
|
4052 |
+ |
"rand 0.8.6",
|
|
4053 |
+ |
"rsa",
|
| 4022 |
4054 |
|
"serde",
|
| 4023 |
4055 |
|
"serde_json",
|
|
4056 |
+ |
"sha2 0.10.9",
|
|
4057 |
+ |
"signature",
|
| 4024 |
4058 |
|
"simple_asn1",
|
|
4059 |
+ |
"zeroize",
|
| 4025 |
4060 |
|
]
|
| 4026 |
4061 |
|
|
| 4027 |
4062 |
|
[[package]]
|
| 57 |
57 |
|
governor = "0.10.4"
|
| 58 |
58 |
|
|
| 59 |
59 |
|
# JWT (SyncKit)
|
| 60 |
|
- |
jsonwebtoken = "9.3.1"
|
|
60 |
+ |
jsonwebtoken = { version = "10.4.0", features = ["rust_crypto"] }
|
| 61 |
61 |
|
|
| 62 |
62 |
|
# TOTP / 2FA
|
| 63 |
63 |
|
totp-rs = { version = "5.7", features = ["qr"] }
|
| 340 |
340 |
|
const TEST_SECRET: &str = "test-secret-key-for-synckit-jwt";
|
| 341 |
341 |
|
const TEST_KEY: &str = "test-key";
|
| 342 |
342 |
|
|
|
343 |
+ |
/// A sync JWT minted before the jsonwebtoken 10 upgrade must keep
|
|
344 |
+ |
/// validating, or every client in the field is logged out on deploy. The
|
|
345 |
+ |
/// token is built independently (raw HMAC-SHA256 over the base64url
|
|
346 |
+ |
/// header/payload), so it pins the wire format rather than whatever the
|
|
347 |
+ |
/// crate happens to emit today. `exp` is year-2100 so the vector does not
|
|
348 |
+ |
/// rot; `iat` is in the past, as the future-iat guard requires.
|
|
349 |
+ |
#[test]
|
|
350 |
+ |
fn pre_upgrade_token_still_validates() {
|
|
351 |
+ |
const SECRET: &str = "known-answer-sync-secret";
|
|
352 |
+ |
const TOKEN: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMTExMTExMS0xMTExLTQxMTEtODExMS0xMTExMTExMTExMTEiLCJhcHAiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJrZXkiOiJzZGsta2V5LTEiLCJpc3MiOiJtYWtlbm90d29yay1zeW5ja2l0IiwiYXVkIjoibWFrZW5vdHdvcmstc3luY2tpdC1jbGllbnRzIiwiZXhwIjo0MTAyNDQ0ODAwLCJpYXQiOjE3MDAwMDAwMDB9.V5Iu9mkok7ryyPo_T2rQNo3jNi-i2Pq-xtuIIfVSttg";
|
|
353 |
+ |
|
|
354 |
+ |
let claims = decode_sync_token(SECRET, TOKEN).expect("pre-upgrade token must validate");
|
|
355 |
+ |
assert_eq!(claims.key, "sdk-key-1");
|
|
356 |
+ |
assert_eq!(claims.iss, SYNCKIT_JWT_ISSUER);
|
|
357 |
+ |
assert_eq!(claims.aud, SYNCKIT_JWT_AUDIENCE);
|
|
358 |
+ |
|
|
359 |
+ |
// The vector must also prove the checks still bite, not just that
|
|
360 |
+ |
// decoding succeeds: a wrong secret is rejected.
|
|
361 |
+ |
assert!(decode_sync_token("not-the-secret", TOKEN).is_err());
|
|
362 |
+ |
}
|
|
363 |
+ |
|
| 343 |
364 |
|
#[test]
|
| 344 |
365 |
|
fn oauth_access_token_round_trips_scope() {
|
| 345 |
366 |
|
let scopes = GrantedScopes::parse("profile:read perks:read");
|