From c241ff9896c1080ce9eab9dcb93e005164917665 Mon Sep 17 00:00:00 2001 From: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:53:43 +0100 Subject: [PATCH 1/2] fix(scripts): stub pnpm in build-frontend.sh for environments without it crux_core TypeGen requires pnpm on PATH but never actually uses it. Provide a no-op stub when pnpm is not installed, matching the existing Dockerfile workaround. Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com> --- scripts/build-frontend.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/build-frontend.sh b/scripts/build-frontend.sh index e56992a1..0f3d855c 100755 --- a/scripts/build-frontend.sh +++ b/scripts/build-frontend.sh @@ -18,6 +18,13 @@ echo -e "${GREEN}✓${NC}" # Generate TypeScript types echo -n "Generating TypeScript types... " +# pnpm is required by crux_core TypeGen but unused; provide a dummy if missing +if ! command -v pnpm &>/dev/null; then + DUMMY_PNPM_DIR=$(mktemp -d) + printf '#!/bin/sh\nexit 0\n' > "$DUMMY_PNPM_DIR/pnpm" + chmod +x "$DUMMY_PNPM_DIR/pnpm" + export PATH="$DUMMY_PNPM_DIR:$PATH" +fi cargo build -p shared_types >/dev/null 2>&1 # Remove .js files to force Vite to use .ts sources find src/shared_types/generated/typescript -name "*.js" -delete From 6864b3c7d3ae8723c3ea262970b2e45c8183a938 Mon Sep 17 00:00:00 2001 From: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:53:31 +0100 Subject: [PATCH 2/2] fix(security): replace jwt-simple to resolve RSA vulnerability - Replace jwt-simple with jsonwebtoken in src/backend - Update TokenManager and KeycloakProvider to use jsonwebtoken - Clear Cargo.audit.ignore and bump version to 1.1.1 - Update unit tests to use new JWT implementation Signed-off-by: Jan Zachmann <50990105+JanZachmann@users.noreply.github.com> --- Cargo.audit.ignore | 1 - Cargo.lock | 512 +------------------------ Cargo.toml | 2 +- src/backend/Cargo.toml | 4 +- src/backend/src/keycloak_client.rs | 16 +- src/backend/src/middleware.rs | 124 +++--- src/backend/src/services/auth/token.rs | 60 +-- 7 files changed, 118 insertions(+), 601 deletions(-) diff --git a/Cargo.audit.ignore b/Cargo.audit.ignore index 6cdceafc..e69de29b 100644 --- a/Cargo.audit.ignore +++ b/Cargo.audit.ignore @@ -1 +0,0 @@ -RUSTSEC-2023-0071 diff --git a/Cargo.lock b/Cargo.lock index a4c5a888..b28a7d14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,7 +339,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "generic-array", ] @@ -350,18 +350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "aes" -version = "0.9.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04097e08a47d9ad181c2e1f4a5fabc9ae06ce8839a333ba9a949bcb0d31fd2a3" -dependencies = [ - "cipher 0.5.0", - "cpubits", + "cipher", "cpufeatures", ] @@ -372,23 +361,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", - "aes 0.8.4", - "cipher 0.4.4", + "aes", + "cipher", "ctr", "ghash", "subtle", ] -[[package]] -name = "aes-keywrap" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b6f24a1f796bc46415a1d0d18dc0a8203ccba088acf5def3291c4f61225522" -dependencies = [ - "aes 0.9.0-rc.4", - "byteorder", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -422,18 +401,6 @@ dependencies = [ "password-hash", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "async-channel" version = "1.9.0" @@ -505,12 +472,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.20.0" @@ -538,12 +499,6 @@ dependencies = [ "serde", ] -[[package]] -name = "binstring" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0669d5a35b64fdb5ab7fb19cae13148b6b5cbdf4b8247faf54ece47f699c8cef" - [[package]] name = "bitflags" version = "2.10.0" @@ -559,17 +514,6 @@ dependencies = [ "digest", ] -[[package]] -name = "blake2b_simd" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -663,18 +607,8 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common 0.1.7", - "inout 0.1.4", -] - -[[package]] -name = "cipher" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64727038c8c5e2bb503a15b9f5b9df50a1da9a33e83e1f93067d914f2c6604a5" -dependencies = [ - "crypto-common 0.2.0", - "inout 0.2.2", + "crypto-common", + "inout", ] [[package]] @@ -686,17 +620,6 @@ dependencies = [ "cc", ] -[[package]] -name = "coarsetime" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58eb270476aa4fc7843849f8a35063e8743b4dbcdf6dd0f8ea0886980c204c2" -dependencies = [ - "libc", - "wasix", - "wasm-bindgen", -] - [[package]] name = "combine" version = "4.6.7" @@ -726,12 +649,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "const-random" version = "0.1.18" @@ -752,12 +669,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - [[package]] name = "convert_case" version = "0.4.0" @@ -807,12 +718,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpubits" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -901,18 +806,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.7" @@ -924,28 +817,13 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "ct-codecs" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8" - [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -1017,17 +895,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - [[package]] name = "deranged" version = "0.5.5" @@ -1111,8 +978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", - "crypto-common 0.1.7", + "crypto-common", "subtle", ] @@ -1148,57 +1014,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519-compact" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ce99a9e19c84beb4cc35ece85374335ccc398240712114c85038319ed709bd" -dependencies = [ - "ct-codecs", - "getrandom 0.3.4", -] - [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "encoding_rs" version = "0.8.35" @@ -1307,16 +1128,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1466,7 +1277,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1518,17 +1328,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "h2" version = "0.3.27" @@ -1593,30 +1392,6 @@ dependencies = [ "digest", ] -[[package]] -name = "hmac-sha1-compact" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b3ba31f6dc772cc8221ce81dbbbd64fa1e668255a6737d95eeace59b5a8823" - -[[package]] -name = "hmac-sha256" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f0ae375a85536cac3a243e3a9cda80a47910348abdea7e2c22f8ec556d586d" -dependencies = [ - "digest", -] - -[[package]] -name = "hmac-sha512" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "425aae4cbcd7250fdcc9665de9cbec21348dd4b6c6a7321b2f5eaf41a3e97dcd" -dependencies = [ - "digest", -] - [[package]] name = "http" version = "0.2.12" @@ -1700,15 +1475,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hybrid-array" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b229d73f5803b562cc26e4da0396c8610a4ee209f4fac8fa4f8d709166dc45" -dependencies = [ - "typenum", -] - [[package]] name = "hyper" version = "1.8.1" @@ -1943,15 +1709,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "inout" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" -dependencies = [ - "hybrid-array", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -2026,43 +1783,16 @@ dependencies = [ ] [[package]] -name = "jwt-simple" -version = "0.12.14" +name = "jsonwebtoken" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3991f54af4b009bb6efe01aa5a4fcce9ca52f3de7a104a3f6b6e2ad36c852c48" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ - "anyhow", - "binstring", - "blake2b_simd", - "coarsetime", - "ct-codecs", - "ed25519-compact", - "hmac-sha1-compact", - "hmac-sha256", - "hmac-sha512", - "k256", - "p256", - "p384", - "rand 0.8.5", + "base64 0.22.1", + "js-sys", + "ring", "serde", "serde_json", - "superboring", - "thiserror 2.0.18", - "zeroize", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", ] [[package]] @@ -2076,9 +1806,6 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] [[package]] name = "libc" @@ -2086,12 +1813,6 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -2228,48 +1949,12 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126" -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2277,7 +1962,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -2291,7 +1975,7 @@ dependencies = [ [[package]] name = "omnect-ui" -version = "1.1.0" +version = "1.1.1" dependencies = [ "actix-cors", "actix-files", @@ -2308,7 +1992,7 @@ dependencies = [ "base64 0.22.1", "env_logger", "futures-util", - "jwt-simple", + "jsonwebtoken", "log", "log-panics", "mockall", @@ -2333,7 +2017,7 @@ dependencies = [ [[package]] name = "omnect-ui-core" -version = "1.1.0" +version = "1.1.1" dependencies = [ "base64 0.22.1", "console_log", @@ -2378,30 +2062,6 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - [[package]] name = "parking" version = "2.2.1" @@ -2475,15 +2135,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -2546,27 +2197,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "polyval" version = "0.6.2" @@ -2629,15 +2259,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2909,16 +2530,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "ring" version = "0.17.14" @@ -2933,27 +2544,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "sha2", - "signature", - "spki", - "subtle", - "zeroize", -] - [[package]] name = "rust-ini" version = "0.21.3" @@ -3119,20 +2709,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - [[package]] name = "security-framework" version = "3.5.1" @@ -3341,7 +2917,7 @@ dependencies = [ [[package]] name = "shared_types" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "crux_core", @@ -3365,16 +2941,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -3419,22 +2985,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3481,21 +3031,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "superboring" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d8c985e81c88f5694d5dfc232691b2aa34f3e1f66e860db9cd1ddf2bb6dc64" -dependencies = [ - "aes-gcm", - "aes-keywrap", - "getrandom 0.2.17", - "hmac-sha256", - "hmac-sha512", - "rand 0.8.5", - "rsa", -] - [[package]] name = "syn" version = "1.0.109" @@ -3859,7 +3394,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common 0.1.7", + "crypto-common", "subtle", ] @@ -3956,15 +3491,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasix" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332" -dependencies = [ - "wasi", -] - [[package]] name = "wasm-bindgen" version = "0.2.108" diff --git a/Cargo.toml b/Cargo.toml index 1cee723d..c6f167bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,4 @@ edition = "2024" homepage = "https://www.omnect.io/home" license = "MIT OR Apache-2.0" repository = "git@github.com:omnect/omnect-ui.git" -version = "1.1.0" +version = "1.1.1" diff --git a/src/backend/Cargo.toml b/src/backend/Cargo.toml index 34fd08c3..51a5f47a 100644 --- a/src/backend/Cargo.toml +++ b/src/backend/Cargo.toml @@ -34,9 +34,7 @@ argon2 = { version = "0.5", default-features = false, features = [ base64 = { version = "0.22", default-features = false } env_logger = { version = "0.11", default-features = false } futures-util = { version = "0.3.31", default-features = false } -jwt-simple = { version = "0.12", default-features = false, features = [ - "pure-rust", -] } +jsonwebtoken = { version = "9.3", default-features = false } log = { version = "0.4", default-features = false } log-panics = { version = "2.1", default-features = false, features = [ "with-backtrace", diff --git a/src/backend/src/keycloak_client.rs b/src/backend/src/keycloak_client.rs index c2c7eb49..16792db1 100644 --- a/src/backend/src/keycloak_client.rs +++ b/src/backend/src/keycloak_client.rs @@ -1,7 +1,7 @@ use crate::config::AppConfig; use anyhow::{Context, Result}; use base64::{Engine, prelude::BASE64_STANDARD}; -use jwt_simple::prelude::{RS256PublicKey, RSAPublicKeyLike}; +use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode}; #[cfg(feature = "mock")] use mockall::automock; use reqwest::Client; @@ -48,7 +48,7 @@ impl KeycloakProvider { .context("failed to write frontend config file") } - async fn realm_public_key(&self) -> Result { + async fn realm_public_key(&self) -> Result { let client = Client::new(); let resp = client .get(&AppConfig::get().keycloak.url) @@ -63,14 +63,20 @@ impl KeycloakProvider { .decode(resp.public_key.as_bytes()) .context("failed to decode public key from base64")?; - RS256PublicKey::from_der(&decoded).context("failed to parse public key from DER format") + Ok(DecodingKey::from_rsa_der(&decoded)) } } impl SingleSignOnProvider for KeycloakProvider { async fn verify_token(&self, token: &str) -> anyhow::Result { let pub_key = self.realm_public_key().await?; - let claims = pub_key.verify_token::(token, None)?; - Ok(claims.custom) + let mut validation = Validation::new(Algorithm::RS256); + // Keycloak tokens usually have these, but we don't strictly require them here + // as we only care about the custom claims if the token is valid. + validation.validate_exp = true; + validation.required_spec_claims.remove("iss"); // issuer might vary + + let claims = decode::(token, &pub_key, &validation)?; + Ok(claims.claims) } } diff --git a/src/backend/src/middleware.rs b/src/backend/src/middleware.rs index 1b4c294e..7a98c1b2 100644 --- a/src/backend/src/middleware.rs +++ b/src/backend/src/middleware.rs @@ -151,92 +151,66 @@ pub mod tests { }; use actix_web_httpauth::headers::authorization::Basic; use base64::prelude::*; - use jwt_simple::claims::{JWTClaims, NoCustomClaims}; - use jwt_simple::prelude::*; + use jsonwebtoken::{EncodingKey, Header, encode, get_current_timestamp}; + use serde::{Deserialize, Serialize}; use std::collections::HashMap; - fn generate_valid_claim() -> JWTClaims { - let issued_at = Clock::now_since_epoch(); - let expires_at = issued_at - .checked_add(Duration::from_hours(TOKEN_EXPIRE_HOURS)) - .unwrap(); - - JWTClaims { - issued_at: Some(issued_at), - expires_at: Some(expires_at), - invalid_before: None, - issuer: None, - subject: Some(TOKEN_SUBJECT.to_string()), - audiences: None, - jwt_id: None, - nonce: None, - custom: NoCustomClaims {}, + #[derive(Debug, Serialize, Deserialize)] + struct TestClaims { + #[serde(skip_serializing_if = "Option::is_none")] + sub: Option, + iat: u64, + exp: u64, + } + + fn generate_valid_claim() -> TestClaims { + let iat = get_current_timestamp(); + let exp = iat + TOKEN_EXPIRE_HOURS * 3600; + + TestClaims { + iat, + exp, + sub: Some(TOKEN_SUBJECT.to_string()), } } - fn generate_expired_claim() -> JWTClaims { - let now = Clock::now_since_epoch(); - let issued_at = now - .checked_sub(Duration::from_hours(2 * TOKEN_EXPIRE_HOURS)) - .unwrap(); - let expires_at = now - .checked_sub(Duration::from_hours(TOKEN_EXPIRE_HOURS)) - .unwrap(); - - JWTClaims { - issued_at: Some(issued_at), - expires_at: Some(expires_at), - invalid_before: None, - issuer: None, - subject: Some(TOKEN_SUBJECT.to_string()), - audiences: None, - jwt_id: None, - nonce: None, - custom: NoCustomClaims {}, + fn generate_expired_claim() -> TestClaims { + let now = get_current_timestamp(); + let iat = now - 2 * TOKEN_EXPIRE_HOURS * 3600; + let exp = now - TOKEN_EXPIRE_HOURS * 3600; + + TestClaims { + iat, + exp, + sub: Some(TOKEN_SUBJECT.to_string()), } } - fn generate_invalid_subject_claim() -> JWTClaims { - let issued_at = Clock::now_since_epoch(); - let expires_at = issued_at - .checked_add(Duration::from_hours(TOKEN_EXPIRE_HOURS)) - .unwrap(); - - JWTClaims { - issued_at: Some(issued_at), - expires_at: Some(expires_at), - invalid_before: None, - issuer: None, - subject: Some("some_unknown_subject".to_string()), - audiences: None, - jwt_id: None, - nonce: None, - custom: NoCustomClaims {}, + fn generate_invalid_subject_claim() -> TestClaims { + let iat = get_current_timestamp(); + let exp = iat + TOKEN_EXPIRE_HOURS * 3600; + + TestClaims { + iat, + exp, + sub: Some("some_unknown_subject".to_string()), } } - fn generate_unset_subject_claim() -> JWTClaims { - let issued_at = Clock::now_since_epoch(); - let expires_at = issued_at - .checked_add(Duration::from_hours(TOKEN_EXPIRE_HOURS)) - .unwrap(); - - JWTClaims { - issued_at: Some(issued_at), - expires_at: Some(expires_at), - invalid_before: None, - issuer: None, - subject: None, - audiences: None, - jwt_id: None, - nonce: None, - custom: NoCustomClaims {}, + fn generate_unset_subject_claim() -> TestClaims { + let iat = get_current_timestamp(); + let exp = iat + TOKEN_EXPIRE_HOURS * 3600; + + TestClaims { + iat, + exp, + sub: None, } } - fn generate_token(claim: JWTClaims) -> String { - let key = HS256Key::from_bytes(AppConfig::get().centrifugo.client_token.as_bytes()); - key.authenticate(claim).unwrap() + fn generate_token(claim: TestClaims) -> String { + let key = EncodingKey::from_secret(AppConfig::get().centrifugo.client_token.as_bytes()); + encode(&Header::default(), &claim, &key).unwrap() } async fn index() -> impl Responder { @@ -286,10 +260,8 @@ pub mod tests { let mut private_jar = cookie_jar.private_mut(&key); let session_store = CookieSessionStore::default(); - let ttl = Clock::now_since_epoch() - .checked_add(Duration::from_hours(2)) - .unwrap(); - let ttl = actix_web::cookie::time::Duration::seconds(ttl.as_secs().try_into().unwrap()); + let ttl = get_current_timestamp() + 2 * 3600; + let ttl = actix_web::cookie::time::Duration::seconds(ttl.try_into().unwrap()); let session_value = session_store .save( diff --git a/src/backend/src/services/auth/token.rs b/src/backend/src/services/auth/token.rs index c3f719db..d1371ec1 100644 --- a/src/backend/src/services/auth/token.rs +++ b/src/backend/src/services/auth/token.rs @@ -1,10 +1,20 @@ use anyhow::Result; -use jwt_simple::prelude::*; +use jsonwebtoken::{ + Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode, get_current_timestamp, +}; +use serde::{Deserialize, Serialize}; use std::sync::Arc; const TOKEN_SUBJECT: &str = "omnect-ui"; const TOKEN_EXPIRE_HOURS: u64 = 2; -const TOKEN_TIME_TOLERANCE_MINS: u64 = 15; +const TOKEN_TIME_TOLERANCE_SECS: u64 = 15 * 60; + +#[derive(Debug, Serialize, Deserialize)] +struct Claims { + sub: String, + iat: u64, + exp: u64, +} /// Centralized token management for session tokens /// @@ -20,7 +30,7 @@ pub struct TokenManager { } struct TokenManagerInner { - key: HS256Key, + key: Vec, } impl TokenManager { @@ -31,7 +41,7 @@ impl TokenManager { pub fn new(secret: &str) -> Self { Self { inner: Arc::new(TokenManagerInner { - key: HS256Key::from_bytes(secret.as_bytes()), + key: secret.as_bytes().to_vec(), }), } } @@ -40,13 +50,21 @@ impl TokenManager { /// /// Returns a signed JWT token string pub fn create_token(&self) -> Result { - let claims = - Claims::create(Duration::from_hours(TOKEN_EXPIRE_HOURS)).with_subject(TOKEN_SUBJECT); + let iat = get_current_timestamp(); + let exp = iat + TOKEN_EXPIRE_HOURS * 3600; - self.inner - .key - .authenticate(claims) - .map_err(|e| anyhow::anyhow!("failed to create token: {e:#}")) + let claims = Claims { + sub: TOKEN_SUBJECT.to_string(), + iat, + exp, + }; + + encode( + &Header::default(), + &claims, + &EncodingKey::from_secret(&self.inner.key), + ) + .map_err(|e| anyhow::anyhow!("failed to create token: {e:#}")) } /// Verify a token and check if it's valid @@ -54,23 +72,21 @@ impl TokenManager { /// Validates: /// - Signature /// - Expiration (with configurable time tolerance) - /// - Max validity (token age) /// - Required subject claim /// /// Returns true if token is valid, false otherwise pub fn verify_token(&self, token: &str) -> bool { - let options = VerificationOptions { - accept_future: true, - time_tolerance: Some(Duration::from_mins(TOKEN_TIME_TOLERANCE_MINS)), - max_validity: Some(Duration::from_hours(TOKEN_EXPIRE_HOURS)), - required_subject: Some(TOKEN_SUBJECT.to_string()), - ..Default::default() - }; + let mut validation = Validation::new(Algorithm::HS256); + validation.leeway = TOKEN_TIME_TOLERANCE_SECS; + validation.sub = Some(TOKEN_SUBJECT.to_string()); + validation.validate_exp = true; - self.inner - .key - .verify_token::(token, Some(options)) - .is_ok() + decode::( + token, + &DecodingKey::from_secret(&self.inner.key), + &validation, + ) + .is_ok() } }