Fail-closed by design
Success with verified_human is issued only for a complete session with liveness detected and
intent confirmed. Any strong deepfake signal vetoes the pass outright.
Missing evidence produces a denial, never a pass. If the analysis backing a verdict cannot be
completed, FaceSign refuses rather than issuing an assertion that skipped a security layer.
Scope of the attestation
This boundary is deliberate. FaceSign holds no directory and performs no one-to-one match against a user record — it answers presence and intent, and nothing else. That is what makes it safe to place in front of an account your SP has already authenticated: FaceSign cannot become a second, weaker source of identity truth. The practical consequence is on the<Subject> NameID. When your AuthnRequest carries one,
FaceSign echoes it back in the assertion — for explicitly registered service providers only.
That echo binds the assertion to the identity you supplied; it is not independent
confirmation of it. Any SP that treats an echoed NameID as proof of identity has built an
impersonation path. Keep the directory binding on your own first factor.
Responsibilities
Replay rejection is worth calling out: it is a standard SAML service-provider responsibility and
FaceSign does not enforce it on your behalf. Record assertion IDs and reject duplicates.
Limits to design for
If your platform enforces its own hard timeout on the redirect, set it comfortably above five
minutes, or ask us to lower the authentication deadline to sit inside yours. Mismatched
deadlines are the most common cause of an integration appearing to fail for reasons unrelated to
verification.
Current limitations
Stated plainly so you can design around them rather than discover them. Signed AuthnRequests are not supported. The metadata declaresWantAuthnRequestsSigned="false". If your SP signs by default and cannot be configured
otherwise, raise it before integration begins.
Denial reasons are not differentiated on the wire. A refused verification and a verdict we
could not produce currently return the same signed status-only shape. If your fallback policy
needs to treat “the check failed” differently from “the check could not run”, tell us — it
changes what we expose.
facesign.repudiationArtifactPresent is reserved. The field is present and currently always
"false". Do not branch on it.
Assurance posture
What has been validated, and how, so a security reviewer can judge the evidence rather than take a claim on trust. Validated against FaceSign’s loopback service provider. A real SP-initiated round-trip completes against the live IdP, with signature and audience validated at the loopback ACS. Both the root Response and the nested Assertion are signed on success. A refusal returns signed, status-only SAML with no assertion. Subject echo is refused for service providers not registered for it. Validated independently. The success and denial contracts have been reproduced offline against a third-partyphp-saml service provider — signatures verified against the published
certificate, with correct Audience, Recipient and Conditions.
Reproduce it yourself. Run the sandbox, capture the response at
your own ACS, and verify both signatures against the certificate in the
live metadata. If your service provider validates
only one of the two signatures, it will accept material this implementation considers invalid.
Your own integration is validated when you validate it. Interoperability with any specific
service provider is established by registering your sandbox SP and running a joint test — see
Test the live sandbox. We do not claim it before you have seen it.