Documentation embedded in this build.
Login with AuthBox, for an application that speaks SAML
Some applications can present a client certificate. Some can speak OpenID Connect. A great many can do neither, and what they can do is SAML 2.0 — because that is what they were written against, and rewriting them is not on anybody's roadmap.

This deployment can be their identity provider. You reach the application, it sends your browser here, your browser presents the certificate it already holds, and what goes back is a signed statement of who you are. That is the whole of it. This page is what that looks like from your side, and — from the second half — what an integrator gets on the wire.
The one thing worth reading twice is the button, because every other identity provider you have used submits that page for you without showing it, and the fact that this one does not is deliberate rather than unfinished.
The login is the certificate you already hold
There is no password page, no second factor and nothing to type. The application redirects you to this deployment, your browser presents its client certificate the way it does at every other AuthBox door, and the certificate is the login. Nothing here authenticates you: the TLS handshake already did, and this surface's job is to carry that identity onward in a format the application can read.
Two consequences follow.
If your browser has no certificate, you get a page saying so — the same page, word for word, that enrolment and Login with AuthBox show for the same problem, because arriving without a credential is one problem and two differently worded pages for it would be two pages to recognise. Install your certificate and try again; if you do not have one, enrol.
If your certificate is fine but this deployment does not know it, you get a refusal saying your certificate is not known here, and the reason stays in the audit record rather than being printed to your screen. That is not evasion. Every other refusal on this door is a misconfiguration between two systems and says exactly what is wrong; this one is the only refusal that is about a person, and a page that explained in detail why a stranger's certificate resolves to nobody would be a page that answers questions for strangers.
The button you will see, and why it is there
At the end of the exchange you land on a page that says roughly this:
Continue to https://app.example.com/sso
This deployment has signed a statement that you are
CN=alice,OU=D009,O=acme,C=US, and is ready to hand it to https://app.example.com/sso athttps://app.example.com/saml/acs.Nothing has been sent yet. Pressing the button below posts the signed assertion to that address and nowhere else.
[ Continue to https://app.example.com/sso ]
Everywhere else in the world that page auto-submits and you never see it. The conventional HTTP-POST binding response carries an inline onload handler that posts the form the instant it renders. This product's content security policy admits no inline script at all, so that handler would not be disapproved of in review — it would be refused by your browser, and the login would simply stop.
The answer here is not an exception carved into the policy. It is a real form with a real button, which is scriptless by construction and, as it turns out, the honest rendering of what is happening: a page that silently posts a signed statement about you to a third party without showing you which one is a page nobody can review from the outside. You get to read the name of the application and the address before the assertion moves.
The page sets its own policy naming that one destination in form-action, so even a browser that had been persuaded to submit the form elsewhere would refuse. The server enforces the same rule from the other end — the address comes from the registration an operator wrote, never from the request — which is why the two halves agree.
What the assertion says
Two things, and the second is empty unless somebody wrote it down.
Who you are. The NameID is your canonical distinguished name, in SAML's own format for exactly that (urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName). This product keys every receipt, audit record, decision and delegation chain on your DN, and minting a second, per-application identity to sit beside the first was considered and declined: one identity, named the same way everywhere, is worth the friction it causes at the far end.
That friction is the first thing a real integration runs into, so it is worth stating here rather than leaving to the runbook. X509SubjectName is the correct format for a product whose identity is a DN — and an application that keys its local accounts on an email address, which is most of them, will not match one against it. The way through is the email attribute: declare it in that application's registration and point the far side's account matching at the attribute rather than at the NameID. That works, and it is configuration on somebody else's side, which is where this kind of friction actually gets paid. The operator's page has the three options in order of preference.
A short, declared list of identity attributes. Each application's registration names which attributes its assertions may carry — saml.service_providers[].attributes — and the default is none. The permitted names are the same three the OIDC provider's claims use, because one rule over two wire formats is one list to maintain:
| Name | On the wire | What it is |
|---|---|---|
org |
urn:oid:2.5.4.10 |
The O components of your own DN |
ou |
urn:oid:2.5.4.11 |
The OU components of your own DN, most specific first |
email |
urn:oid:0.9.2342.19200300.100.1.3 |
Your own address, from your own record |
org and ou disclose nothing new: they are bytes you can read off the certificate in your own hand, and the assertion already publishes the whole DN as the NameID. What they add is legibility. They are read from the DN itself and never from an attribute of the same name, which is what keeps write access to the dataset from becoming a way into a signed document.
email is a real disclosure and does not borrow the other argument. It is admitted because it describes who somebody is rather than what they may do, because it is released only to an application you are at that moment actively logging in to, and because it travels only where this deployment's own registration says so. Three rules keep it honest: it is read from your own record; if you have no address you get no attribute rather than an empty one; and if the authority that owns your address has stopped vouching for it, it is not sent at all — a document that outlives the request must not carry a value whose source has gone quiet.
What it will never say
No mission. No classification level. No project. No agreement. No marking. No group membership. Not by configuration, not by request, not on a Tuesday.
An application's registration naming any of them is refused when this deployment loads its configuration, by name, quoting the rule — so a deployment that tried is a deployment that does not start, rather than one that quietly discloses. And there is a second refusal behind the first: the code that builds an assertion has nothing to produce such a statement from even if a registration somehow reached a running provider.
The rule is one sentence:
An identity fact may travel in a token or an assertion. What this deployment's engine decides may not.
The reason is about time. Your DN is a fact; your address is a fact; both are true of you independently of anything this deployment computed. A mission, a classification level, a project, an agreement and a marking are answers — computed at an instant, from a dataset and a policy, for one question that was being asked right then. An assertion outlives the request that produced it: the application receives it, writes a local account from it, and keeps acting on it through every change nobody re-evaluated. A claim that outlives the request is an authorization decision nobody re-evaluated, and that is the sentence this whole product's token surfaces are built around.
What an application should do instead, when it needs a decision, is ask for one at the moment it needs it — which is what asking for access and the authorizing door are for. An assertion tells it who walked in.
What an integrator gets
The metadata is at GET /saml/v1/metadata on the enrolment listener, unauthenticated, because a machine being configured to trust this authority holds no credential from it yet. It carries the entity id, the single sign-on URL, the NameID format and the signing key, it is signed under the key it publishes, and — this is the part worth knowing — its bytes do not change between restarts. There is no random document id and no expiry, so you can re-fetch it and diff it to nothing. If it differs, something actually changed.
Single sign-on is at /saml/v1/sso, answering both browser bindings at one path: GET is HTTP-Redirect, POST is HTTP-POST. The method decides the binding, which is what lets the metadata publish one Location for both.
Five things this deployment does not do, each refused deliberately rather than pending:
- It verifies no inbound XML signature. Ever. The metadata says
WantAuthnRequestsSigned="false", and a signedAuthnRequestis refused by name with the reason on the page rather than accepted-and-ignored. Signature wrapping and canonicalization confusion are vulnerability classes of the verifier, and this deployment does not need your signature: the assertion goes only to the address in its own registration, so a request signature would prove nothing that is acted on. Accepting the request while ignoring its signature was the alternative, and it would leave an operator believing a protection is in force that is not. - It sends the assertion only to the registered address. An
AssertionConsumerServiceURLin your request that disagrees with the registration is refused rather than honoured and rather than silently corrected — one side or the other is misconfigured, and an identity provider that quietly substituted its own would hide which.RelayStateis echoed back untouched, bounded at the 80 bytes the specification bounds it at, and refused rather than truncated if you exceed it. - No Single Logout. SAML's SLO profiles are a swamp of partial failure with no honest completion semantics. This deployment's answer to "end the session" is a short assertion lifetime and a credential that can be revoked.
- No encrypted assertions. The channel to your assertion consumer is already TLS, and XML-ENC is more XML surface for a property that hop already has.
- AuthBox is not a service provider. It will not consume somebody else's assertion to bootstrap a credential here. That is a question about enrolment assurance rather than about a protocol, and folding it into a signing feature would be answering it by accident.
One deliberate omission will show up in your testing before any of the above: no xsi:type is written on an attribute value. Every common implementation emits xsi:type="xs:string" and it is optional in SAML; this one omits it for a canonicalization reason that the operator's page states in full, along with what to do if your product insists.
Where to read on
- The SAML identity provider — turning it on, registering an application, the key, and the two constraints a real integration hits.
- Login with AuthBox — the same requirement answered for applications that can speak OpenID Connect, which is the better choice where you have it.
- Integrations and protocols — everything this deployment speaks, in one table.