Ever tried explaining to a board why your "enterprise-ready" SaaS is stuck in a 6-month sales cycle because of a login button? It’s usually because big clients don't want more passwords—they want saml.
Building a service provider (SP) isn't just about the code; it’s about navigating the trust relationship between your app and their identity provider (idp). According to NetSuite, companies like theirs use saml 2.0 to let employees jump into complex systems without a separate password, which is basically the gold standard for security now.
Diagram 1 shows the high-level handshake where the user is redirected from your app to the IdP and back again.
I once saw a dev team at a retail firm lose a massive contract because their saml implementation couldn't handle "IdP-initiated" flows. Most devs build "SP-initiated" flows where the user clicks "Login" on your site. But in IdP-initiated flows, the user clicks an icon on their Okta or Azure dashboard and gets sent to your app without asking. It’s risky because there is no InResponseTo ID to verify, so you gotta be extra careful about validating the recipient and the timestamp.
Anyway, next we’ll look at the actual xml bits that make this work.
So, you’ve got the high-level flow down, but now we gotta look at the actual "guts" of the saml response. It’s mostly just a big pile of xml, but if you don't parse it right, you're basically leaving the door unlocked for any ai or script kiddie to walk right in.
First off, your app (the SP) sends an AuthnRequest. You need to make sure this is structured perfectly. At a minimum, you need an Issuer (your EntityID) and an AssertionConsumerServiceURL (where the idp sends the user back to).
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_123" Version="2.0" IssueInstant="2023-10-01T12:00:00Z"
AssertionConsumerServiceURL="https://yourapp.com/saml/callback">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">your-entity-id</saml:Issuer>
</samlp:AuthnRequest>
Once the idp does its thing, it sends back a SAMLResponse. This is where the real work happens.
Diagram 2 illustrates the internal XML structure, highlighting where the digital signature sits within the assertion.
Most devs use a library like passport-saml because writing xml signatures from scratch is a special kind of hell. Here is how you actually inject that public key into your middleware:
const samlStrategy = new SamlStrategy({
path: '/login/callback',
entryPoint: 'https://idp.com/saml2',
issuer: 'your-app-entity-id',
// THIS IS THE KEY: The IdP's public cert goes here to verify signatures
cert: 'MIIDdTCCAl2gAwIBAgIJAL7...',
}, (profile, done) => {
return done(null, profile);
});
// Extracting attributes after verification
app.post('/saml/callback', (req, res) => {
const profile = req.user;
const email = profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'];
if (!db.users.find(email)) {
db.users.create({ email, role: profile.role || 'viewer' });
}
res.redirect('/dashboard');
});
Before you can even run that code, you have to do a "Metadata Exchange." This is just an XML file you swap with the client that contains your public keys and endpoints. It’s the "pre-game" handshake. To make this easier, many people use tools to automate the exchange.
Man, wrestling with saml is a headache nobody needs. Once you've got the xml basics, the real nightmare is scaling it across fifty different clients who all use different idps.
That's where offloading the heavy lifting to an api makes sense. Instead of writing custom logic for every new b2b customer, you can use a platform like SSOJet to handle the mess.
Diagram 3 shows how an intermediary API simplifies the connection between multiple IdPs and your single application.
Honestly, i've seen teams save months of dev time just by not building this from scratch. It lets you focus on your actual product. Next, we'll wrap things up with some best practices for keeping the whole system secure.
Implementing saml isn't a "set it and forget it" kind of deal. If you don't stay on top of your metadata and certs, your users are gonna have a bad time when their session suddenly dies on a tuesday morning.
Diagram 4 outlines the lifecycle of a certificate and the automated rotation process.
As mentioned earlier when we looked at how SSOJet handles the mess, the goal is to get out of the xml business and back to building features. Honestly, unless you're a glutton for punishment, don't roll your own security logic for every b2b client. Keep it simple, keep it automated, and you'll actually get some sleep.
*** This is a Security Bloggers Network syndicated blog from SSOJet - Enterprise SSO & Identity Solutions authored by SSOJet - Enterprise SSO & Identity Solutions. Read the original post at: https://ssojet.com/blog/single-sign-on-sso-guide-openid-saml-oauth