SAML 1.1 browser profiles
iya-sts is a SAML 1.1 identity provider: the Browser/POST and Browser/Artifact profiles of the OASIS SAML 1.1 profiles and bindings specifications (OASIS Security Services TC), the SOAP SAML responder behind the artifact profile — which is also an attribute authority — and Shibboleth 1.x’s request profile. Every trust realm has its own identity provider, with its own providerID, signing key, relying parties and metadata.
It is not the SAML 2.0 identity provider with the version turned down.
SAML 1.1 has no request message: there is no <AuthnRequest>, the
browser profiles are identity-provider-initiated, a flow begins when a browser
arrives at the inter-site transfer service carrying a TARGET (the URL at the
relying party it wants to end up at), and almost everything that reads oddly on
this page follows from that. The SAML 2.0 profile is
SAML 2.0 Web Browser SSO.
Features
Endpoints
| Path | What it is |
|---|---|
GET\|POST /saml11/sso[/{rp}] |
the inter-site transfer service — SAML 1.1’s name for the single sign-on service |
POST /saml11/responder[/{rp}] |
the SAML responder, SOAP over HTTP (bindings section 3.1): resolves artifacts, returns assertions by AssertionID, and answers AttributeQuery and AuthenticationQuery |
GET /saml11/metadata[/{rp}] |
signed metadata, one document per relying party |
GET\|POST /saml11/rp |
a mock relying party (not part of any specification) |
GET /saml11/autopost.js |
the one script the Browser/POST profile runs |
GET /saml11 |
a page describing all of the above and every parameter this realm reads |
In a trust realm every path is under /realm/{id}. The full, current list is
at /admin/sts-metadata.
Starting a flow
A browser arrives at /saml11/sso with:
| Parameter | What is done with it |
|---|---|
TARGET |
the resource the person wants; echoed back byte for byte — SAML 1.1’s RelayState. Shibboleth’s request profile spells it target, which is read the same way |
shire |
Shibboleth’s name for the assertion consumer URL, the only thing in the protocol that carries one |
providerId |
who the assertion is for — its audience |
time |
read and logged, not enforced |
profile |
non-spec: post or artifact; without it, the binding the relying party registered for its shire decides |
format |
non-spec: the NameIdentifier format to answer with |
Shibboleth’s request profile (shire, target, providerId, time,
identified as urn:mace:shibboleth:1.0:profiles:AuthnRequest) is supported and
advertised in the metadata, although it is not a standard — it is what every
real SAML 1.1 service provider sends, and a mock that could not be told where to
send the assertion would be a mock nobody could point at anything.
The relying party cannot name itself in the protocol, so the audience comes
from providerId, from the {rp} path segment of a scoped endpoint, or —
failing both — is guessed from the origin of the TARGET. The guess is
logged, and /admin/saml11 marks an identifier that looks like a bare origin as
probably guessed. A relying party expecting urn:example:app and handed an
assertion for https://app.example.com refuses it with nothing saying why:
send providerId.
The person signs in at the same authentication service
every protocol here uses, so a session from OAuth, SAML 2.0 or WS-Federation is
reused. A flow is held for saml11.requestTtlMin while the person is at the
screen.
Browser/POST and Browser/Artifact
The two profiles are chosen here. Nothing in SAML 1.1 lets a relying
party ask in the protocol, but a relying party whose metadata was consumed
registered each assertion consumer service WITH its profile — a Shibboleth SP
publishes /SAML/POST as browser-post and /SAML/Artifact as
artifact-01 — so the shire it sends chooses, which is what a Shibboleth
identity provider does. Otherwise saml11.defaultProfile decides, and the
non-spec profile parameter (or an arriving SAMLart) overrides it — the same
kind of device as WS-Trust’s /sts?encrypt=1, and marked as non-spec wherever
it appears.
- Browser/POST (profiles section 4.2) puts the whole signed Response in a
self-submitting form. The assertion is confirmed
urn:oasis:names:tc:SAML:1.0:cm:bearer. It carries noDoNotCacheConditionunlesssaml11.doNotCacheConditionis on (off by default): the profile does not ask for the condition — single use is the relying party’s to keep — and a Shibboleth SP’s stock policy refuses an assertion carrying it. - Browser/Artifact (section 4.1) sends a 42-byte artifact (type
0x0001, with a SourceID that is the SHA-1 of the providerID) on a redirect, and the relying party fetches the assertion from the responder over SOAP — the assertion never passes through the browser. It is confirmedurn:oasis:names:tc:SAML:1.0:cm:artifact.
The confirmation method is the profile. Section 4.1.1.4 requires
...:cm:artifact for Browser/Artifact and section 4.2.1.4 requires
...:cm:bearer for Browser/POST. It is the assertion’s own statement of how it
reached the relying party, so the two are not interchangeable: an
artifact-profile assertion confirmed as bearer claims to have travelled
through the browser when it did not. A relying party that checks refuses it;
one that does not works with either. The mock relying party checks.
An artifact here stands for an assertion, not a message: the
<samlp:Response> around it is built at resolution time, so its InResponseTo
names the SOAP request and its Recipient names whoever asked.
The SAML responder and attribute authority
/saml11/responder answers four request types:
| Request | Answer |
|---|---|
AssertionArtifact |
the assertion — exactly once, across every node of a cluster: resolving destroys it (bindings section 3.2.3), and a second attempt is refused with a status naming the reason. saml11.artifactTtlS only bounds how long an unresolved one lives |
AssertionIDReference |
an assertion this realm issued, from a cache of saml11.assertionCacheMax; not one-shot, since holding the reference means already holding the assertion |
AttributeQuery |
an assertion carrying the person’s attributes and no AuthenticationStatement — in product under a release policy, below |
AuthenticationQuery |
answered from a live, authenticated session for that name, with its real method and instant; with no such session, Success and no assertion — in product under the same policy |
The fifth type, AuthorizationDecisionQuery, is refused by name: this service
makes no authorization decisions. AttributeQuery and AuthenticationQuery
are SAML 1.1’s attribute authority, the half Shibboleth deployments leaned
on hardest; once the endpoint exists for the artifact profile, an
<AttributeQuery> is the same assertion builder behind the same envelope.
Nothing authenticates a query in development mode. An artifact is
protected by its twenty random bytes and the one-shot rule; an
AttributeQuery is protected by nothing — anybody who can reach the port can
ask for an assertion about anybody, by name, with no credential and no
attribute release policy. A real attribute authority uses mutual TLS and a
policy. Every query is logged saying so. Product mode answers a query only
under a release policy: from a registered relying party (its Resource, or
the responder’s path segment), authenticated as it — a signed Request or its
registered certificate at the TLS handshake — about a person it is signed in
for from this service now, by the NameIdentifier it was given. Anything else is
refused (see the mode table).
Resolving an artifact asks who is calling. The caller must be the relying party the artifact was issued to (named by the responder path segment), and where signed requests are required it must prove it — by signing the request, or by presenting its registered certificate as the TLS client certificate. A refused caller does not spend the artifact.
The assertion
SAML 1.1 is a different specification, not a dialect of 2.0, and several
spellings differ in ways that break a parser: the assertion’s Issuer is an
attribute rather than an element, its id is AssertionID (the Response’s
is ResponseID) rather than ID, the version is two attributes, the Subject
sits inside each statement, an attribute is AttributeName +
AttributeNamespace rather than one Name, the condition is
AudienceRestrictionCondition, status codes are QNames (samlp:Success) rather
than URIs, and ds:Signature goes last in an assertion and first in a
response.
The assertion carries an AuthenticationStatement whose
AuthenticationMethod says how the session really authenticated — am:password for a password, multipleauthn for
two factors, am:HardwareToken for a security key alone, urn:ietf:rfc:2246
for a TLS client certificate, urn:ietf:rfc:1510 for Kerberos, a federation
partner’s own method where it gave one, and am:unspecified otherwise — an
audience restriction, and an attribute statement: the claim URIs split into
namespace and name, Shibboleth’s names — AttributeName="urn:mace:dir:attribute-def:uid"
in AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri", which
a Shibboleth SP’s stock attribute map reads — and any SAML 1.1
custom SAML attributes.
Both the assertion (saml11.signAssertion) and the Response
(saml11.signResponse) are signed by default. Browser/POST requires the
Response signature (oasis-sstc-saml-bindings-1.1 section 4.1.2.4) and lets
the assertions in it be signed; this service signs the assertion as well, so
one that leaves its Response — over the artifact channel, or by
AssertionIDReference — still carries a signature. Turning either off is
development mode’s alone (#181): product signs both whatever the setting or
the relying party’s override says, and refuses to turn either off. The algorithm and canonicalization are the shared
saml.signatureAlgorithm and saml.canonicalizationAlgorithm. Lifetime,
signing, NameID format and artifact lifetime are the SAML 1.1 half of the
assertion settings every application inherits,
each overridable per relying party (saml11AssertionLifetimeMin,
saml11SignAssertion, saml11SignResponse, saml11NameIdFormat,
saml11ArtifactTtlS).
Metadata
SAML 1.1 has no metadata specification, so /saml11/metadata/{rp} is a SAML
2.0 <EntityDescriptor> whose protocolSupportEnumeration is
urn:oasis:names:tc:SAML:1.1:protocol — what every SAML 1.1 relying party reads
today — and, on the IDPSSODescriptor, urn:mace:shibboleth:1.0 too, which a
Shibboleth SP requires before it will send Shibboleth’s request profile. It holds two descriptors: an IDPSSODescriptor for the browser
profiles and an AttributeAuthorityDescriptor for the responder, where a
Shibboleth service provider looks for its attribute authority.
Both descriptors also carry the TLS certificate the responder presents
(#248) — a use="signing" KeyDescriptor after the XML signing key — so a
relying party that authenticates the SOAP back channel (artifact resolution
and the attribute query) from metadata needs no anchor configured for it. The
details, and the warning about a listener key you supply yourself, are the
SAML 2.0 page’s: Metadata, one document per service provider.
As with SAML 2.0 it is per relying party and, in development, minted for
anything asked for — in product mode /saml11/metadata/{rp},
/saml11/sso/{rp} and /saml11/responder/{rp} answer 404 for a name that is
not a registered SAML 1.1 relying party:
the providerID becomes {providerID}:{slug} and the endpoints sit under the
same segment (saml11.perApplicationProviderId). The slug is the same one the
SAML 2.0 profile uses for the same application. In development, a relying
party named for the first time — by a TARGET, a metadata request or an
artifact resolution — gets an application entry when
saml11.autocreateApplications is on; product mode creates nothing because
something named it.
The mock relying party
/saml11/rp is the default assertion consumer and a test harness. It starts a
flow on either profile, accepts a Browser/POST response or an artifact (which it
resolves under the same one-shot rule, so reloading the result page shows the
second attempt refused), and verifies the Response check by check: SAML version,
status, Recipient, InResponseTo, both signatures, AssertionID, issuer,
audience, validity window, the confirmation method against the profile, the
DoNotCacheCondition against the profile, the authentication statement, the
subject and the attributes.
Not implemented
- Single Logout — SAML 1.1 has none; it arrived with SAML 2.0. A signed-in
relying party is still recorded, so
/admin/saml11can show who holds an assertion nothing here can recall. ForceAuthn,IsPassiveandRequestedAuthnContext— no spelling exists in the protocol.- An error response — with no request there is nothing to answer and no
InResponseToto name, so a failure is a page. AuthorizationDecisionQuery, and assertion encryption (there is no request to carry a recipient certificate).
Development and product mode
| Development | Product | |
|---|---|---|
shire |
used as it stands; with none, the mock relying party | must be a registered samlAssertionConsumerService, exact match, no mock fallback; an address development merely observed is refused until confirmed |
AttributeQuery, AuthenticationQuery |
answered, to anybody who can reach the port — logged as such | answered only to a registered, authenticated relying party about a person it holds a live session for, by the NameIdentifier it was given |
| Artifact resolver authentication | not required (follows saml2.requireSignedAuthnRequests) |
required |
| Given name, surname, mail, display name | invented | read off the directory entry, or omitted |
Empty saml11.providerId |
replaced with urn:sts:idp:saml11 |
SSO and metadata refuse, naming the setting |
The query policy in product is not a bare client-certificate gate on purpose: a gate alone would answer any holder of any trusted certificate about anybody, so the certificate (or signature) names the relying party and the live session names the people it may ask about. The one-shot artifact, the resolver being the right relying party, and the authentication statement reflecting a real session hold in both modes. See What is not checked.
Configuration
| Setting | Environment variable | Default | Runtime? | What it does |
|---|---|---|---|---|
saml11.providerId |
STS_SAML11_PROVIDER_ID |
urn:sts:idp:saml11 |
yes | This identity provider’s name: the assertion Issuer, the metadata entityID, and what every artifact’s SourceID is a hash of. |
saml11.perApplicationProviderId |
STS_SAML11_PER_APPLICATION_PROVIDER_ID |
true |
yes | Give each relying party its own providerID, {providerID}:{slug}; this also changes every artifact’s SourceID. |
saml11.assertionLifetimeMin |
STS_SAML11_ASSERTION_LIFETIME_MIN |
60 |
yes | Assertion lifetime; per relying party with saml11AssertionLifetimeMin. |
saml11.signAssertion |
STS_SAML11_SIGN_ASSERTION |
true |
yes | Sign the assertion (required by Browser/POST); per relying party with saml11SignAssertion. Off is development mode only: product signs every assertion, and turning it off is refused, here and per relying party (#181). |
saml11.signResponse |
STS_SAML11_SIGN_RESPONSE |
true |
yes | Sign the Response; per relying party with saml11SignResponse. Off is development mode only: Browser/POST requires a signed Response, so product always signs it and refuses turning it off (#181). |
saml11.nameIdFormat |
STS_SAML11_NAMEID_FORMAT |
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified |
yes | The NameIdentifier format, unless the non-spec format overrides it; per relying party with saml11NameIdFormat. |
saml11.defaultProfile |
STS_SAML11_DEFAULT_PROFILE |
post |
yes | post or artifact, when the request does not say. |
saml11.doNotCacheCondition |
STS_SAML11_DO_NOT_CACHE_CONDITION |
false |
yes | A <saml:DoNotCacheCondition/> on a Browser/POST assertion. Off by default (#189): the profile does not require it, and the Shibboleth SP’s stock policy refuses it. Warning: turn it on only for relying parties known to honour it. |
saml11.artifactTtlS |
STS_SAML11_ARTIFACT_TTL_S |
300 |
yes | How long an unresolved artifact lives (it is one-shot regardless); per relying party with saml11ArtifactTtlS. |
saml11.autocreateApplications |
STS_SAML11_AUTOCREATE_APPLICATIONS |
true |
yes | Create an application entry the first time a relying party is named. |
saml11.requestTtlMin |
STS_SAML11_REQUEST_TTL_MIN |
10 |
yes | How long a flow is held while the person signs in. |
saml11.assertionCacheMax |
STS_SAML11_ASSERTION_CACHE_MAX |
500 |
yes | Assertions kept per realm for AssertionIDReference, oldest out first. |
The shared saml.* settings — signature algorithm, canonicalization, SHA-1
verification, clock skew, the metadata organisation — are in the
SAML 2.0 configuration table; artifact resolver
authentication follows saml2.requireSignedAuthnRequests. This table is a copy;
the live source is /admin/saml11 and GET /admin-api/config. See
Configuration for how a value is resolved; a setting is
changed on /admin/saml11 or /admin/saml-assertions, or with
POST /admin-api/config/set.
Design decisions
- A separate implementation from SAML 2.0. No request message, no Single Logout, a different artifact and a different set of spellings mean a merged identity provider would branch in every function. The two share the application registry, the session and the application slug.
- A guessed audience is guessed out loud. With no
Issuerto read, falling back to theTARGET’s origin keeps a relying party working, and logging it and marking it on the console keeps the guess from being mistaken for a registration. - The confirmation method follows the profile. An artifact-profile
assertion confirmed as
bearerwould claim to have travelled through the browser; a relying party that does not check never notices, which is why the mock checks. - An artifact resolves into a Response built at that moment, so
InResponseToandRecipientname the SOAP caller rather than the browser’s destination. - The responder answers all four request types. It has to exist for the artifact profile, and the attribute authority is the half of SAML 1.1 that Shibboleth deployments leaned on.
- Queries sign nothing that did not happen. An
AuthenticationQueryabout somebody with no session gets no assertion, and anAttributeQueryanswer carries no inventedAuthenticationStatement. - No release policy, so product refuses queries rather than gating them.
- The metadata is a SAML 2.0 document, because that is what every SAML 1.1 relying party consumes.
- A separate providerID from the SAML 2.0 entityID. A relying party that trusts this service for 1.1 and not for 2.0 is the ordinary case.
- No POST-to-GET step. A SAML 1.1 flow arrives as a top-level GET, which
carries the
SameSite=Laxsession cookie, so there is nothing to hold.
In the running service
- Protocols → SAML → SAML 1.1 identity provider (
/admin/saml11): every relying party, its metadata URL, which profile it used and where the assertion went, and which identifiers look guessed. - SAML assertions (
/admin/saml-assertions) and Custom SAML attributes (/admin/saml-attributes) in the same group — see SAML 2.0 Web Browser SSO. GET /admin-api/saml11(the console page as JSON) andPOST /admin-api/saml11/register(register a relying party by identifier); the full shape is in/admin-api/openapi.json.GET /saml11describes the profiles and every parameter as this realm treats them. Failures are recorded underSTS-SAML-NNNNcodes — see Error codes.
Related
- SAML 2.0 Web Browser SSO, including the assertion settings and custom attributes shared with this profile
- WS-Federation, whose default token is a SAML 1.1 assertion from the same builder
- WS-Trust
- Authentication, Sessions, Signing out
- Trust realms, What is not checked, Configuration