FURS response-JWS verification (authenticate FURS's EOR response)#5
Merged
Conversation
verifyFursResponse(token, fursCertPem) verifies FURS's response JWS (RS256) against FURS's public certificate (X509Certificate → public key) before the EOR is trusted — guards against spoofed/tampered responses. The client enforces it when `fursResponseCertPem` is configured, raising FursResponseSignatureError on failure. Completes the FURS client for safe real use. Tests: 44 pass / 1 skip. Response verify accepts a genuine FURS-signed response and rejects tampered payloads, wrong-key signatures, and malformed tokens. Build remains unit-only; mints test responses with an ephemeral key (no live needed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The final FURS engine slice: verify that FURS's response is authentically FURS-signed, not just sign our request.
What this adds
verifyFursResponse(token, fursCertPem)— verifies a FURS response's compact JWS (RS256) against FURS's public certificate (X509Certificate(certPem).publicKey→createVerify), checksalg === RS256, and returns the decoded payload. Throws on a bad/missing signature or malformed token.FursResponseSignatureError(502) — raised when a response fails verification (potential spoof/MITM); the EOR is not trusted.makeFursClient({ …, fursResponseCertPem }): when set, every signed response is verified before the EOR is read. Omitted → decoded unverified (prior behaviour). Recommended on for production.Why
The reference FURS clients (node-furs, python-furs, jurgenwerk) all skip response-signature verification (decode-only). That leaves the EOR spoofable. This closes it, making the client safe for real use.
Verification
bun test→ 44 pass / 1 skip / 0 fail,tsc --noEmitclean. New tests cover:No live round-trip needed — tests mint a "FURS-signed" response with an ephemeral key and verify against its cert.
Notes
test-sign.pem); production has its own. The engine takes it as config (fursResponseCertPem) rather than bundling a gov cert.4afba33(P2 merged). With this, the fiscalize engine is complete: e-invoice (e-SLOG/UBL, XSD-validated) + FURS (ZOI/EOR, request-signed + response-verified). P3 (Medusa integration) is the plugin track; P4 (service/MCP) later. The bun-mTLSbun.mdline + the one-shot live-EOR confirm remain tracked for a Node-mTLS runtime.🤖 Generated with Claude Code