From ae65c8475f8f48ce15ea0d7c9aedcb9b06c3b6e2 Mon Sep 17 00:00:00 2001 From: Edouard Vanbelle Date: Mon, 27 Jul 2026 06:31:53 +0200 Subject: [PATCH] test(opaque): add test that coverable by hurl --- tests/api/opaque_substrate.hurl | 69 +++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/api/opaque_substrate.hurl b/tests/api/opaque_substrate.hurl index 7c1d5bed..0f2c847e 100644 --- a/tests/api/opaque_substrate.hurl +++ b/tests/api/opaque_substrate.hurl @@ -74,3 +74,72 @@ Content-Type: application/json { "exchangeId": "unused-phase-0", "finishLoginRequest": "unused-phase-0" } HTTP 401 + + +# ============================================================= +# Phase 1 — Register endpoints (authenticated wire coverage) +# ============================================================= +# The register/{start,finish} endpoints are wired behind auth + +# CSRF middleware. Sending an authenticated request with an +# intentionally-malformed body proves: +# +# 1. Auth middleware unlocks the endpoint (401 → 400). +# 2. Bearer auth is CSRF-exempt (no 403 CSRF). +# 3. The handler is REACHABLE and its error-type contract +# (`OpaqueMalformedRequest`, `OpaqueCiphersuiteMismatch`) +# is stable. +# +# The FULL crypto handshake with real opaque-ke messages is +# proved separately in the Rust integration test at +# `src/infrastructure/repositories/pg/opaque_pg_repository.rs` +# (`envelope_persists_across_register_and_serves_a_matching_login`). +# That test drives the crypto pipeline end-to-end without HTTP — +# same crypto shape, same PG persistence path the handlers use. +# ============================================================= + +# ───────────────────────────────────────────────────────────── +# Log in as the seed admin and capture the bearer token. Bearer +# auth bypasses the CSRF check per the CSRF middleware doc, so +# subsequent OPAQUE POSTs don't need an X-CSRF-Token header. +# ───────────────────────────────────────────────────────────── +POST {{base_url}}/api/auth/login +Content-Type: application/json +{ "username": "{{username}}", "password": "{{password}}" } + +HTTP 200 +[Captures] +opaque_access_token: jsonpath "$.access_token" + + +# ───────────────────────────────────────────────────────────── +# Case 5 — Authenticated register/start with garbage base64 +# in `registrationRequest`. Handler reaches the +# `B64.decode` path and returns 400 with the +# `OpaqueMalformedRequest` error_type. +# ───────────────────────────────────────────────────────────── +POST {{base_url}}/api/auth/opaque/register/start +Authorization: Bearer {{opaque_access_token}} +Content-Type: application/json +{ "registrationRequest": "not-valid-base64!" } + +HTTP 400 +[Asserts] +jsonpath "$.error_type" == "OpaqueMalformedRequest" + + +# ───────────────────────────────────────────────────────────── +# Case 6 — Authenticated register/finish with a ciphersuite +# version the server does NOT accept. Proves the +# ciphersuite-mismatch guard (server v1, client says +# v999) is enforced BEFORE the envelope is decoded, +# so a client cached against a rotated suite can't +# silently write an unusable envelope. +# ───────────────────────────────────────────────────────────── +POST {{base_url}}/api/auth/opaque/register/finish +Authorization: Bearer {{opaque_access_token}} +Content-Type: application/json +{ "registrationRecord": "AAAA", "ciphersuiteVersion": 999 } + +HTTP 400 +[Asserts] +jsonpath "$.error_type" == "OpaqueCiphersuiteMismatch"