Skip to content

Expose ES256 signature verification to Rhai scripts #9265

Description

@gijs-martens

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions