Enterprise SSO is usually explained as "log in once and access every application." That describes the user experience, but it hides the architecture so completely that teams start believing every application is sharing one giant session. They are not. Each application has its own session. What is shared is trust in the same identity provider, full stop.
This difference matters because SSO failures are rarely about the login screen. They happen when an application trusts the wrong issuer, accepts an assertion intended for somebody else, maps groups directly into permanent privilege, or assumes that signing out from one place has magically ended every session everywhere.
SSO is federation. One system proves that authentication happened, another system decides whether to accept that proof, and then the application creates local state of its own.
The identity provider does not log you into the application
Take a normal employee opening a software-as-a-service application. The application does not have the employee's password, and it should not ask for it. Instead, it redirects the browser to an identity provider, usually called the IdP. The IdP authenticates the user using whatever controls the enterprise requires, perhaps password, phishing-resistant MFA, device posture, or an existing IdP session.
After authentication, the IdP sends a signed response back through the browser. With SAML, that response contains a SAML assertion. With OpenID Connect, the application receives an authorization response, exchanges the authorization code at the token endpoint, and obtains an ID token.
The browser is carrying the response, but the browser is not what makes it trustworthy. The signature, issuer, audience, time bounds, request correlation, and protocol validation make it trustworthy.

Once the application validates the response, it maps the external identity to a local account and creates its own session cookie. On the next request, the application normally reads that local cookie. It does not redirect to the IdP every time only. This is why the application can remain signed in even when the IdP page is not open, and why logout becomes more complicated than login.
SAML and OIDC carry similar trust in different shapes
SAML 2.0 is still common in enterprise software. It uses signed XML assertions and browser POST or redirect bindings. The application is the Service Provider, or SP. The IdP sends an assertion containing a subject, an audience restriction, validity times, and attributes.
OpenID Connect is an identity layer built on OAuth 2.0. The application is an OIDC client, and the IdP is an OpenID Provider. Modern applications normally use the authorization code flow with PKCE. The ID token is usually a JWT, and the client can use the access token for a separate API when the design requires it.
They solve a similar SSO problem, but they are not interchangeable payload formats. SAML has assertion consumer service URLs, entity IDs, XML signatures, and metadata. OIDC has redirect URIs, client IDs, discovery metadata, JSON Web Keys, authorization codes, and ID-token validation. Wrapping both behind one "SSO callback" abstraction can be useful, but the validation rules must stay protocol specific.
The application should load trusted configuration from an administrative onboarding flow. It should not accept an arbitrary issuer or metadata URL from a login request. Otherwise the caller can choose which identity provider the application trusts, which is basically allowing the user to bring their own authority.
Validation is the real SSO implementation
Redirecting a browser is easy. Correctly deciding whether the returned identity proof belongs to this request and this application is the security work.
For OIDC, validate at least the issuer, audience, signature algorithm, expiry, and nonce. Correlate the state value with the browser session to defend the authorization response flow. Use an exact registered redirect URI. If the token contains multiple audiences, apply the protocol's authorized-party rules rather than picking the first value that looks familiar.
const tokenSet = await oidcClient.callback(
"https://app.example.com/auth/callback",
callbackParams,
{
state: loginAttempt.state,
nonce: loginAttempt.nonce,
code_verifier: loginAttempt.codeVerifier,
},
);
const claims = tokenSet.claims();
if (claims.iss !== configuredIssuer) {
throw new Error("unexpected identity provider");
}
const account = await accounts.resolveFederatedIdentity({
issuer: claims.iss,
subject: claims.sub,
});The stable external identifier is the pair of issuer and subject, not email address by itself. Email can change, be reused, or collide across identity providers. Linking accounts by an unverified email is convenient right until one tenant's identity becomes another tenant's local account.
For SAML, validate the XML signature using the configured IdP certificate, the response destination, audience restriction, assertion validity, and request correlation such as InResponseTo when the flow expects it. Protect against XML signature wrapping by using a maintained SAML library and consuming only the element that the library verified. Searching the XML tree manually for a convenient NameID defeats the point of the signature.
The OIDC Core specification and SAML 2.0 Profiles define these checks because each one closes a different substitution or replay path. They are not optional decoration around the signature.
Account mapping is where tenants get crossed
After validation, the application has an external identity. It still has to answer a local question: which account and tenant does this identity belong to.
Enterprise applications often discover the tenant from the verified issuer or an explicit SSO connection selected before redirect. That connection maps one enterprise IdP to one application tenant. The returned email or domain may help onboarding, but it should not silently override the configured tenant boundary.
Just-in-time provisioning can create a local user after the first successful SSO response. SCIM can provision users and groups before login. These are lifecycle choices, not authentication shortcuts. Whether the account was created just in time or through SCIM, the application still needs a durable mapping and a clear disabled state.
Group and role claims need careful treatment also. An IdP group proves that the IdP placed the user in that group. It does not automatically mean the application should grant administrator access. Map external groups through tenant-owned policy, keep defaults narrow, and decide what happens when a claim disappears. Authorization remains an application responsibility.

This separation is useful during incidents. You can see whether the IdP authenticated the wrong person, the federation mapping selected the wrong local account, or the application granted the wrong permission. If everything is called "SSO," those three failures become one vague ticket.
SSO login is easier than SSO logout
Login works well because the browser follows a chain and every participant can create new state. Logout asks multiple independent systems to destroy state they own, sometimes when one of them is unavailable.
Signing out of the application should always end the application's local session. Redirecting to an IdP logout endpoint may end the IdP session, but it does not guarantee every other application session is gone. SAML Single Logout and OIDC logout specifications provide coordination mechanisms, but deployments vary and front-channel browser flows can fail.
For high-risk environments, do not rely on coordinated logout as the only revocation strategy. Keep local session lifetimes appropriate, consume deprovisioning events, and re-check sensitive authorization. If an employee is disabled at the IdP, an application session with a month-long lifetime should not remain trusted simply because no logout message arrived.
AI agents should not borrow the SSO session
An AI agent acting for an employee needs the human identity context, but the agent is not the human. Passing the browser session cookie into an agent process collapses two actors into one credential and makes the audit trail useless basically.
Use SSO to authenticate the human and establish the application's local session. When the user delegates a task, issue or exchange a separate short-lived credential for the agent. That credential should identify the workload, preserve the delegated subject, target one audience, and carry narrower scope. RFC 8693 token exchange is one useful pattern for this on-behalf-of relationship.
The distinction becomes important when the user logs out, the task is cancelled, or the agent delegates to a tool. The application can revoke the agent task without pretending the browser and the agent are one session. The resource API can log both who authorized the work and which workload actually performed it.
SSO answers how the human entered the trust system. It does not give every autonomous process permission to impersonate that human afterward.
The takeaway
Enterprise SSO is a chain of explicit trust decisions. The IdP authenticates the user and signs a protocol response. The application validates that response, maps the external identity into the correct tenant, and creates a local session. Then the application applies its own authorization policy.
SAML and OIDC make the exchange interoperable. They do not remove the need for strict validation, lifecycle management, or local access control. Once you see SSO as federation instead of a shared login, the architecture becomes much easier to reason about, and much harder to accidentally trust too broadly.
Akash Devdhar is a Senior Software Engineer specializing in enterprise identity, authentication, authorization, and AI infrastructure. He writes about building secure AI systems using OAuth, OIDC, RBAC, and modern identity architectures.