Blog
Half of IPSIE SL1 cannot be a default
IPSIE SL1 is the OpenID Foundation's enterprise sign-in profile. Half of it belongs in every server; the other half breaks other standards. When to use it.
By FoxAuth ·
Sooner or later a security questionnaire from an enterprise customer will contain a row like this:
| Control | Answer |
|---|---|
| Does your identity provider conform to the IPSIE SL1 OpenID Connect profile? | Yes / No / Partially |
The identity provider is the server that signs people in and issues tokens to applications. If you run one for business customers, that row is about you. Before you answer it, you need to know what SL1 is, which of its rules your server already follows, and which ones would break something if you turned them on.
I recently built SL1 into FoxAuth, and the profile split into two halves. Every server should follow the first half anyway. The second half contradicts standards that other clients depend on, so it cannot be a default. It has to be a switch, and the hard part is knowing when to turn it on.
IPSIE SL1 turns “supports SSO” into rules you can test
IPSIE stands for Interoperability Profile for Secure Identity in the Enterprise. It is a working group of the OpenID Foundation, started in October 2024 by Okta together with Microsoft, Ping Identity and others (Okta’s announcement). The group writes profiles: stricter rules on top of existing standards such as OpenID Connect, OAuth and SCIM. With a profile, a company can check what “supports single sign-on” actually means for each SaaS application it buys.
The group sorts its requirements into levels on two tracks (IPSIE levels). The Session Lifecycle track (SL) is about sign-in and sessions. The Account Lifecycle track (AL) is about creating and removing accounts, which is SCIM’s job (I wrote about IPSIE’s SCIM profile in what Entra and Okta send to SCIM). SL1 is the first session level: single sign-on with multi-factor authentication, and a session lifetime that the identity provider sets and the application obeys. SL2 adds logout and termination deadlines. SL3 adds continuous signals between the two sides.
The concrete text is the IPSIE SL1 OpenID Connect Profile. It lists what the identity provider and the application must each do, section by section. It also points to a second document, the IPSIE Common Requirements, for rules shared by every IPSIE profile, such as which signature algorithms are allowed.
Both are drafts. The editor’s draft I worked from is dated 29 September 2026. The profile has already
changed in ways that matter: draft 00 called the session claim session_lifetime and measured it in
seconds, and the current text calls it session_expiry and makes it a timestamp. A server that
implements SL1 today will have to follow the next draft too.
Most of SL1 should be your default anyway
Read the provider’s half of the profile and most rules are plain good practice. They conflict with no other standard, so nothing stops a server from following them for every client:
- Authorization codes live at most 60 seconds and are used once.
- PKCE (the code challenge that ties a code to the client that asked for it) uses S256.
- A redirect URI must match a registered one exactly.
- The authorization response carries
iss, so a client can tell which server answered (RFC 9207). - Every ID token says when the person authenticated (
auth_time), how (acrandamr), and when the application’s session must end (session_expiry).
FoxAuth does all of this with the profile off. Here is an ID token payload from a password sign-in, decoded:
{ "iss": "https://auth.example.com", "sub": "2f6c1b0e-7c55-4b8e-9d2a-3a1f0c9e8b71", "aud": "orders-web", "iat": 1792745400, "exp": 1792749000, "auth_time": 1792745391, "acr": "urn:foxauth:acr:pwd", "amr": ["pwd"], "session_expiry": 1792831800}session_expiry is the new one. It is the moment after which the application must send the person
back through the identity provider, which then checks again that they may still sign in. SL1 says it
must lie at least 300 seconds after iat. Here it lies 24 hours later, which is the default of the
idToken.relyingPartySessionLifetime setting. An application that does not read the claim
ignores it, which is why it can be on for everyone.
The other half breaks a standard someone else depends on
Four of SL1’s rules are different. Each one contradicts another text that a real class of clients follows. Make any of them a default, and those clients stop working.
- No
httpredirect URIs, not evenhttp://127.0.0.1. But RFC 8252, section 7.3, tells native apps to receive the authorization response on exactly such a loopback address. Desktop apps and command line tools do it every day. - Only clients registered in advance, and no unauthenticated dynamic registration. But MCP clients, the AI tools that connect to MCP servers, usually arrive unannounced. The MCP authorization spec asks servers to accept a client id that is the URL of a metadata document, fetched at first contact, and keeps dynamic registration as a deprecated fallback. I described that flow in your MCP server is an OAuth resource server.
acr_valuesbecomes binding. In OpenID Connect Core, section 3.1.2.1, the parameter a client uses to ask for an authentication level is a voluntary hint. A server may sign the person in some other way and say so inacr. SL1 says the server must either meet the request or return an error.- Only PS256, ES256 and EdDSA signatures (Common Requirements, section 3.3). But OpenID Connect Registration makes RS256 the default for a client that names no algorithm, and a verifier written for that default may accept nothing else.
The diagram puts the two halves side by side. The rules on the left cost nobody anything. Under each rule on the right is the text it contradicts.
None of these four is wrong. For a company that controls every application it signs into, a loopback redirect or an unregistered client is a gap in the chain it is trying to trust. For a public developer tool, they are how things work. One server can serve both kinds of customer, so these four rules belong behind a setting.
One switch turns the rules on and refuses what breaks them
In FoxAuth the setting is ipsieSl1.enabled, off by default. Every rule reads it where the rule is
enforced, through one function in lib/sl1/rules.ts:
export function sl1(): boolean { return ApplicationConfig['ipsieSl1.enabled'];}That makes the profile easy to audit: search the code for sl1() and you find every place it changes
what the server does. The token endpoint is a good example. SL1 says access tokens should be
sender-constrained with DPoP, a proof that ties each token to a key the client holds (more in
what DPoP protects you from). Under the profile, the check runs
before any grant, so no grant type is left out:
if (sl1() && !sl1Exempt(client)) { if (client.tlsClientCertificateBoundAccessTokens) { throw new InvalidClient('certificate-bound access tokens are not issued under the IPSIE SL1 profile'); } if (!dPoP) { throw new InvalidDpopProof('DPoP proof JWT not provided (IPSIE SL1 §3.2.1)'); }}A client that asks for a token without a proof gets this answer:
HTTP/1.1 400 Bad RequestContent-Type: application/json
{ "error": "invalid_dpop_proof", "error_description": "DPoP proof JWT not provided (IPSIE SL1 §3.2.1)"}The error names the section. I did that on purpose. The person who reads it is a developer of some other company’s application, and the fastest way to fix their client is to show them the rule.
The switch also refuses to turn on until the settings the profile depends on are in place. DPoP must be on and PKCE must
be required. Encrypted ID tokens and responses must be available (encryption.enabled), because the
Common Requirements say encryption must be offered. Dynamic registration must be off or demand an initial access
token. Every issuer needs a PS256 signing key, because PS256 is what an application that chose no
algorithm will now receive. DPoP and encryption are off in a fresh install, so most servers have work
to do before the switch moves.
Four clients stay outside the rules: the admin console, the administrators’ MCP client, each
project’s account page and SCIM provisioning connections. They are the server’s own, and each one
needs something the profile forbids. The MCP client runs on the operator’s machine with a loopback
redirect. The account page asks with response_type=none. A SCIM directory holds no DPoP key, and
its token reaches only the SCIM endpoint, which SL1 does not cover.
An RS256 in a stored record is usually a default nobody chose
The risky part of switching is the applications you already have. My first version of the switch would have refused nearly all of them.
The rule looked simple. Common Requirements allows only PS256, ES256 and EdDSA, so a client registered for RS256 is refused. The test suite agreed. Then I tried it on a real database, and nearly every application created in the console was refused.
The reason is how records are saved. When FoxAuth stores a client, it stores the validated record with
its defaults filled in. So a client whose owner never thought about algorithms still holds
id_token_signed_response_alg: "RS256". The test fixtures were written by hand and carry no defaults,
which is why the suite passed. From the record alone, an RS256 somebody chose and an RS256 the server
filled in look the same.
The fix treats a stored RS256 in those attributes as the default it almost always is. Under the profile, such a client receives PS256 signatures, and PS256 is written into the record the next time it is saved. Registering RS256 afresh, or changing an algorithm to RS256, is still refused.
That changes what the application has to do: it must pick the verification key from the key set by
kid and accept the algorithm the key states, not a fixed RS256. Most libraries do this. Some
hand-written verifiers do not. Before you switch, the readiness check lists every application that
will receive PS256, so you know whose verifier to ask about:
The readiness check is a card in the console under Settings, then Request security, and the same
answer is available to an agent through the settings_ipsie_profile_readiness_get tool. For each
application it names the rule and the attribute. http_redirect_uri means that redirect URI will be
refused, while the application’s https ones keep working. signing_algorithm means the application
will be refused. default_algorithm means it will keep working but receive PS256.
Turning the profile on rewrites nothing. A refused application keeps its record, so turning the profile off brings it back on the next request. That matters with a draft standard: you can back out the same afternoon.
Turn it on when a customer requires SL1, not because it sounds safer
The half of SL1 that conflicts with nothing is already on, so switching the profile on does not make an ordinary server noticeably safer. It makes the server stricter in four ways that some clients cannot follow. That trade is worth it in one situation: an enterprise customer requires SL1, and every application you serve can live with the rules.
Turn it on when:
- a customer’s contract or security review asks for IPSIE SL1, and
- every application you serve uses
httpsredirect URIs, can send DPoP proofs and verifies signatures by key set, and - you register applications yourself, in the console or with an initial access token.
Leave it off when you serve any of these:
- native or command line apps that receive the response on a loopback
httpaddress; - MCP clients that register themselves, or that you identify by a metadata document URL;
- partners whose verifiers accept only RS256;
- anything that relies on
response_type=none.
If you serve both kinds of customer, run them on separate instances, one with the profile on and one without. A FoxAuth project with an address of its own is its own issuer, but the switch is per instance, not per project.
Answer the questionnaire honestly, too. Three SL1 requirements are not met here yet. An application
can ask for a sign-in with a second factor, and in a project that does not require one, the request
ends in an error instead of asking the person for the factor. After a sign-in through an upstream
provider, the ID token does not carry that provider’s amr. And tokens that other issuers send to
this server, such as upstream ID tokens, are still accepted in RS256. The first is the next feature.
No certification has been applied for.
I looked for the same switch elsewhere. Auth0, Keycloak, Zitadel, authentik and Ory Hydra do not document being an SL1 OpenID Provider, as of the sources cited in the comparison pages. The profile is still a draft, so that may change.
To try it, start with the readiness card and read turn on the IPSIE SL1 profile, which lists every rule and every prerequisite. You will probably need a PS256 key first; rotate signing keys shows how to generate and promote one.