v1.15.1 — PIV/CAC sign-in fix (supersedes unsigned v1.15.0)
Patch release fixing PIV/CAC sign-in, which was impossible in v1.15.0, and re-establishing a signed release image.
Upgrade note. v1.15.0's image published successfully but its signing job never ran — the build job failed on a GitHub Actions cache-export error after the image had already been pushed, which skipped
sign-verify-attest. v1.15.0 is therefore unsigned and is superseded by this release. Deployments verifying signatures should move to v1.15.1.
Fixed
PIV/CAC sign-in failed for every user (#824)
A user selected their smart-card certificate, entered the card PIN, and was told "No smart card certificate was presented" — while the gateway was forwarding the certificate and attesting verify=SUCCESS. Firefox, Chrome and Edge behaved identically, because the fault was server-side.
Three defects, two of which independently broke sign-in.
The certificate arrived but could not be parsed. HTTP headers cannot carry newlines and a PEM is defined by them, so every gateway works around it differently:
| Gateway | What arrives |
|---|---|
nginx $ssl_client_escaped_cert, ALB X-Amzn-Mtls-Clientcert |
URL-encoded (%0A) |
nginx $ssl_client_cert |
newlines folded to spaces or tabs |
| assorted | literal backslash-n written into the value |
| assorted | bare base64 DER with no BEGIN/END markers |
SPARC handled exactly one of these — it URL-decoded when it saw a percent sign. Every other shape reached OpenSSL as mangled text and was rejected. SPARC now reassembles all four before parsing, so no additional gateway configuration is required. Reassembly remains fail-closed: anything that is not a decodable certificate is still refused.
The failure message was misleading. A missing certificate and an unreadable one produced the same message, so the audit log reported "no certificate presented" while the gateway simultaneously reported successful verification — a contradiction that points diagnosis at the wrong subsystem. The two are now distinct:
| Message | Meaning |
|---|---|
| Your smart card could not be verified by the gateway. | Verify header missing or not SUCCESS |
| No smart card certificate was presented. | Verify succeeded but the certificate header was empty |
| Your smart card certificate could not be read. | A certificate arrived but could not be decoded |
Every PIV failure now records shape-only diagnostics — byte count, whether PEM markers were present, whether the value was URL-encoded, whether reassembly succeeded — so the next occurrence is diagnosable from the audit log without a redeploy. The certificate itself is never recorded, in whole or in part.
The consent banner did not persist. It re-fired on every login-page load, interposing between the certificate-bearing request and /auth/piv. Acceptance is now remembered for the browser session. Session scope is deliberate: AC-8 requires the notice before access is granted, so acceptance is not made permanent — clearing the browser session brings the notice back.
Documentation
- Authentication and MFA (wiki) — the forwarded-certificate formats SPARC accepts, and a troubleshooting table mapping each smart-card failure message to where to look.
Compliance
- Authentication component definition 1.1.0 → 1.2.0, and the NIST SP 800-53 Rev 5 mapping, updated for IA-2 / IA-2(12) to cover forwarded-certificate reassembly and the distinct auditing of absent versus undecodable certificates.
Verification
- Full RSpec suite: 3376 examples, 0 failures, 24 pending (environment-gated).
- Seven parser tests, one per gateway mangling, plus request-level tests signing in through the real request path with space-folded, URL-encoded and marker-less certificates.
- Mutation-tested: with certificate reassembly disabled, all seven mangling tests fail while the well-formed control and every negative case still pass.
- Browser tests for consent persistence pass against this build and fail against v1.15.0.
Full changelog: v1.15.0...v1.15.1