Is your feature request related to a problem? Please describe.
We're implementing DPoP (RFC 9449) sender-constrained token validation on the Apollo Router using Rhai scripts. The existing built-in functions cover almost everything we need: sha256::digest() for JWK Thumbprint computation (RFC 7638), base64::decode() and json::decode() for parsing the DPoP JWT, and unix_now() for freshness checks. See Rhai Script API Reference.
The one missing piece is ES256 signature verification. Without it, we can validate all DPoP claims and verify the key binding via SHA-256 thumbprint matching, but we cannot cryptographically prove that the presenter possesses the private key corresponding to the public key in the DPoP proof. This prevents a fully spec-compliant RFC 9449 implementation in pure Rhai.
Describe the solution you'd like
Expose an ES256 (ECDSA P-256) signature verification function to Rhai, following the same pattern as sha256::digest():
let is_valid = es256::verify(signing_input, signature_bytes, jwk);
Where:
signing_input — the JWT signing input (base64url(header) + "." + base64url(payload))
signature_bytes — the decoded signature (base64url-decoded third segment of the JWT)
jwk — a map with kty, crv, x, y fields (the EC public key)
The Router already performs ECDSA signature verification internally for the JWT authentication plugin (likely via the ring crate), so this would expose an existing capability as a Rhai binding rather than introducing a new dependency.
Describe alternatives you've considered
Custom Router binary with a native Rust plugin
We can register a custom Rhai function or write a native plugin that performs ES256 verification. This works, but requires maintaining a custom build, Dockerfile, and Cargo workspace. It means we can no longer use the stock Router image, which adds significant operational overhead for what is a single function call.
External coprocessor (Lambda or sidecar)
Offload signature verification to an HTTP service called via the coprocessor protocol. This adds a network hop and cold start latency on every request for a single cryptographic operation.
Skip signature verification entirely
Validate everything except the ES256 signature in Rhai. The SHA-256 thumbprint check (cnf.jkt binding) provides strong protection in practice, but this is not fully compliant with RFC 9449.
Additional context
DPoP is increasingly adopted alongside OAuth 2.0 for sender-constrained tokens. The sha256::digest() built-in already sets a precedent for exposing cryptographic primitives to Rhai. Adding ES256 verification would make the Router a natural fit for DPoP validation without any custom build.
Is your feature request related to a problem? Please describe.
We're implementing DPoP (RFC 9449) sender-constrained token validation on the Apollo Router using Rhai scripts. The existing built-in functions cover almost everything we need:
sha256::digest()for JWK Thumbprint computation (RFC 7638),base64::decode()andjson::decode()for parsing the DPoP JWT, andunix_now()for freshness checks. See Rhai Script API Reference.The one missing piece is ES256 signature verification. Without it, we can validate all DPoP claims and verify the key binding via SHA-256 thumbprint matching, but we cannot cryptographically prove that the presenter possesses the private key corresponding to the public key in the DPoP proof. This prevents a fully spec-compliant RFC 9449 implementation in pure Rhai.
Describe the solution you'd like
Expose an ES256 (ECDSA P-256) signature verification function to Rhai, following the same pattern as
sha256::digest():Where:
signing_input— the JWT signing input (base64url(header) + "." + base64url(payload))signature_bytes— the decoded signature (base64url-decoded third segment of the JWT)jwk— a map withkty,crv,x,yfields (the EC public key)The Router already performs ECDSA signature verification internally for the JWT authentication plugin (likely via the
ringcrate), so this would expose an existing capability as a Rhai binding rather than introducing a new dependency.Describe alternatives you've considered
Custom Router binary with a native Rust plugin
We can register a custom Rhai function or write a native plugin that performs ES256 verification. This works, but requires maintaining a custom build, Dockerfile, and Cargo workspace. It means we can no longer use the stock Router image, which adds significant operational overhead for what is a single function call.
External coprocessor (Lambda or sidecar)
Offload signature verification to an HTTP service called via the coprocessor protocol. This adds a network hop and cold start latency on every request for a single cryptographic operation.
Skip signature verification entirely
Validate everything except the ES256 signature in Rhai. The SHA-256 thumbprint check (cnf.jkt binding) provides strong protection in practice, but this is not fully compliant with RFC 9449.
Additional context
DPoP is increasingly adopted alongside OAuth 2.0 for sender-constrained tokens. The
sha256::digest()built-in already sets a precedent for exposing cryptographic primitives to Rhai. Adding ES256 verification would make the Router a natural fit for DPoP validation without any custom build.