Skip to content

Add webauthn+mTLS auth method binding tokens to TLS client cert (RFC 8705) #1200

Description

@gtema

Summary

Add a new authentication method, webauthn combined with mutual-TLS
certificate-bound access tokens per RFC 8705
("OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access
Tokens"). On successful auth, the client's TLS certificate thumbprint
(x5t#S256, the base64url SHA-256 digest of the DER-encoded cert, per RFC 8705
§3.1) is minted into the token payload. On every subsequent request, the
current mTLS peer certificate's thumbprint must match the one bound into the
token, or the token is rejected — this is the "proof of possession" defense
RFC 8705 adds on top of plain webauthn/passkey auth: even a stolen bearer
token is useless without the private key of the bound client certificate.

This is a token confirmation mechanism, not a replacement for the existing
webauthn credential check (crates/webauthn/) — it complements it, the same
way RFC 8705 complements OAuth2 rather than replacing the token issuance flow.

Background: prior art in python-keystone

python-keystone's fernet token formatter has a numbered set of payload
classes (keystone/token/token_formatters.py); payload id 10 is the
OAuth2 access-token payload used for the mTLS-bound OAuth2 flow, and it
carries the certificate thumbprint (cnf.x5t#S256) alongside the standard
scope fields so that validation can re-derive and compare it against the live
TLS session on every request.

Proposed approach in keystone-rs

  • crates/core-types/src/token.rs / crates/core-types/src/token/payload/:
    add a new FernetToken payload variant (or extend an existing scoped
    payload, mirroring python-keystone's payload-10 approach) carrying the
    x5t#S256 thumbprint alongside the usual scope fields.
  • crates/core-types/src/auth.rs: extend AuthenticationContext (currently
    WebauthN has no cert-binding variant — see enum AuthenticationContext,
    ~line 1294) with a cert-bound variant, or add a cert_thumbprint field
    usable by the existing WebauthN arm.
  • Peer certificate extraction: reuse the existing mTLS/x509 peer-cert
    plumbing already present for SPIFFE listeners
    (crates/keystone/src/server/listener/spiffe_tls.rs,
    spiffe_common.rs) rather than inventing new TLS termination handling —
    same pattern, different trust domain (webauthn users' own client certs
    instead of SPIFFE workload certs).
  • TokenService::validate_to_context_impl
    (crates/core/src/token/service.rs): on every validation of a cert-bound
    token, recompute the current connection's peer-cert thumbprint and reject
    (new dedicated error, mirroring the PluginVersionMismatch /
    ruleset_version TOCTOU pattern already used elsewhere in this codebase)
    if it doesn't match the thumbprint minted into the token.
  • Policy/OPA input must not carry the raw certificate or thumbprint value
    beyond what's needed for the match itself — consistent with the existing
    "no EC2 keys/TOTP seeds in OPA" secrets-stripping rule in
    doc/src/contributor/security-model.md.

Relation to other in-flight work

  • Complements TLS based 2FA #263 (TLS based 2FA / trusted CA registration): TLS based 2FA #263 is about
    registering trusted CAs per domain and validating the client cert against
    them as a complementary factor; this issue is specifically about
    binding the minted token to the cert's thumbprint (RFC 8705 token
    confirmation), which is orthogonal and can reuse whatever CA-trust/peer-cert
    extraction TLS based 2FA #263 introduces.
  • Should read doc/src/contributor/security-model.md before implementation
    (security decisions must be keyed on the authentication chain, not scope;
    see repo-wide security invariants), and probably warrants its own ADR given
    the pattern of ADR 0025/0026/0028 for other auth/token changes in this
    repo.

Acceptance criteria

  • New/extended fernet token payload carries the client cert's x5t#S256
    thumbprint.
  • Token validation recomputes the live mTLS peer cert thumbprint and
    rejects on mismatch or on a request with no client cert presented.
  • Webauthn registration/auth flow (crates/webauthn/) gains an
    opt-in mode to require and bind the client cert at login time.
  • Unit tests in the relevant crates + test_api coverage (valid
    auth + positive/negative policy, invalid auth, per this repo's CRUD
    handler test convention).
  • Docs: doc/src/admin/features/ page + ADR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions