SSO

Implementing Single Sign On: A Nodejs developer’s guide

Kuntal Banerjee
CONTENTS

If your Node.js app is selling into enterprises, there’s one thing you’ll hear again and again: “Does it support SSO?”

Single Sign On (SSO) is more than a security feature, it’s a deal-breaker for most enterprise buyers. But integrating SSO across different identity providers like Okta, Microsoft Entra ID, OneLogin, or Ping Identity? That’s where things get complicated.

In this guide, you’ll learn how to set up SSO in a Node.js + React app using Scalekit, a platform that handles the heavy lifting across protocols like SAML, OAuth, and OIDC. We’ll walk through the integration step-by-step, from setting up your first login flow to supporting multiple IdPs in production.

Let’s dive in.

The pain of SSO gone wrong

Stackly’s authentication struggles

Let’s talk about Stackly, a fictitious fast-growing SaaS company that recently started landing enterprise deals.

That’s when the SSO headaches began.

Every new customer brought a different Identity Provider: Okta, OneLogin, Entra ID, even a few on legacy Active Directory. Supporting each one meant writing custom SSO logic, debugging protocol quirks, and chasing token issues in production.

Enterprise customers bring their own identity providers

Things got messy fast:

  • Onboarding slowed to a crawl as engineers wired up one-off integrations.
  • Login sessions broke randomly thanks to inconsistent token handling.
  • Devs spent more time untangling auth issues than shipping new features.

SSO became a blocker, not a value add.

That’s when they switched to Scalekit. With a single integration, they could support multiple IdPs through one clean interface. No more juggling protocol details. No more rewriting flows. Just a working login button, for every customer.

Why enterprises care about SSO (And why it’s a headache)

For enterprises, SSO isn’t optional, it’s the expectation. It’s how IT teams keep user access secure without handing out passwords for every tool under the sun.

From the user's side? It’s seamless. One login, access to everything.
From your side as a developer? It’s… not that simple.

Supporting enterprise SSO means dealing with:

  • Protocol soup: SAML, OIDC, OAuth, each with their own quirks.
  • Vendor-specific configs: Okta expects one thing, Entra ID wants another.
  • Token handling: Expired tokens, misconfigured redirects, broken sessions.
  • Attribute mapping: Because no two IdPs send the same user format.

It’s all doable but not without cost. You end up building auth flows instead of product features.

That’s where Scalekit steps in. It standardises the auth experience across IdPs, so you don’t have to reinvent the wheel every time a new customer shows up with a different setup.

How Scalekit simplifies SSO for Node.js apps

Scalekit gives you a clean way to support SSO without juggling protocols or vendor-specific hacks.

It sits between your app and the Identity Provider, handles the authentication handshake, and gives you a user profile on successful login. That’s it.

Here’s what you get out of the box:

  • Multi-IdP support: Works with Okta, OneLogin, Entra ID, Ping Identity, and more.
  • Protocol abstraction: Doesn’t matter if it’s SAML or OIDC, the flow stays the same.
  • Unified API: One set of methods to initiate login, handle callbacks, and retrieve user info.
  • Quick setup: Drop in the SDK, configure your environment, and start testing.

It’s built to get you up and running fast - without sacrificing the flexibility you need when supporting enterprise customers.

“Scalekit takes the pain out of enterprise SSO. The documentation is thorough and easy to follow, and their support team stays proactive, helping us get things done without unnecessary delays. The pricing is transparent, which makes budgeting and forecasting much simpler compared to other solutions like Auth0 and WorkOS in the market.”
— Review on G2

Connecting your IdPs to Scalekit

Before you dive into code, you’ll need to connect your customer’s Identity Provider (IdP) to Scalekit. This is usually a one-time setup per enterprise client and once it’s done, the rest of the flow stays the same.

Here’s how it works for the most common IdPs:

Microsoft Entra ID

  1. Head to your Azure PortalAzure Active Directory > App registrations → click New Registration.
  2. Give your app a name and choose the protocol (OIDC or SAML).
  3. Under Authentication, add the redirect URI from Scalekit.
  4. Once created, copy the Application (Client) ID and Tenant ID.
  5. Grant admin consent for required scopes (for OIDC flows).

What you’ll need to plug into Scalekit:

  • For OIDC: Client ID, Client Secret, Issuer URL
  • For SAML: Metadata URL

OneLogin

  1. In the OneLogin Admin Console, go to Applications > Add App.
  2. Pick either OIDC Custom App or SAML Custom Connector depending on the protocol.
  3. Add your redirect URI from Scalekit.
  4. Grab the Client ID, Client Secret, and Metadata URL.

Add to Scalekit:

  • OIDC: Client ID, Secret, Issuer
  • SAML: Metadata URL
  • Make sure identifiers match (audience, issuer, etc.)

Ping Identity

  1. Open the Ping Admin Portal, navigate to Connections > Applications > Add Application.
  2. Choose SAML or OIDC.
  3. Set the redirect URI from Scalekit.
  4. Copy over the Client ID, Secret, or Metadata URL.
  5. Configure token expiration and session policies.

Same thing here:

  • Provide Scalekit the relevant credentials and URLs.
  • Ensure your redirect URI is whitelisted in Ping.
💡 Pro tip: You don’t need to set this up for every IdP from the start. Use Scalekit’s Test IdP while developing and plug in real ones once your flow is solid.

Setting up SSO in Node.js using Scalekit

Once your IdPs are hooked up in Scalekit, wiring the backend is refreshingly simple. All you need is the SDK, a couple of routes, and your SSO flow is good to go.

Step-by-step setup

1. Install the SDK

To install Scalekit SDK inside your backend project, run:

npm install @scalekit-sdk/node

This gives you access to helper methods like getAuthorizationUrl() and authenticateWithCode() that handle most of the heavy lifting.

2. Set environment variables

Scalekit will give you the credentials you need after setting up your environment in the dashboard. Create a .env file in your backend and add:

These will be used when initializing the SDK.

SCALEKIT_ENVIRONMENT_URL=your_env_url SCALEKIT_CLIENT_ID=your_client_id SCALEKIT_CLIENT_SECRET=your_secret

3. Kick off the login flow

Create a route in your backend to initiate the login flow. This is where your app redirects the user to begin the SSO process:

const scalekit = require("@scalekit/sdk"); app.get("/login", async (req, res) => {    const authUrl = scalekit.getAuthorizationUrl("https://your-app/callback");    res.redirect(authUrl); });

📝 Optional: You can pass in organizationId, connectionId, or loginHint depending on how you want to route users.

4. Handle the callback after authentication

Once a user logs in via their IdP, they’ll be sent back to your app with a code. Use it to get the user’s profile:

app.get("/callback", async (req, res) => {  const { code } = req.query;  if (!code) return res.status(400).json({ error: "Missing code" });  const userProfile = await scalekit.authenticateWithCode(code);  res.json(userProfile); // or set a session here });

🔐 This is where you’d typically drop in your JWT-based session logic, more on that in the next section.

5. Test with Scalekit’s IdP simulator

Once your login and callback routes are working, you can validate the entire flow using Scalekit’s Test IdP Simulator, no need to connect a real IdP just yet.

Scalekit IdP simulator

Just use a test domain like user@example.org, and Scalekit will simulate a login through its internal IdP. This is perfect for:

  • Verifying redirect + callback behavior
  • Debugging early-stage integration
  • Testing how your app handles successful login responses

This helps you iterate quickly without needing admin access to Okta, Entra ID, or OneLogin during development.

User profile information on Scalekit dashboard

Securing user sessions in Node.js with JWT authentication

After a user signs in via Scalekit, your app gets their verified identity. Now it’s your turn to keep them logged in. The cleanest way to do that in a Node.js app? JWT-based session handling.

Let’s walk through how to set that up.

How JWT-based session management works

  1. User logs in → The server generates a JWT session token.
  2. Token is sent to the client → Stored securely in an HTTP-only cookie or sent via headers.
  3. User accesses protected routes → The server verifies the JWT before allowing access.
  4. Token expiration & renewal → A refresh mechanism can be used to extend sessions securely.

Step 1: Generate a JWT token after authentication

Once you get the user profile from Scalekit, generate a signed token to maintain their session:

const jwt = require("jsonwebtoken"); function generateSessionToken(user) {    return jwt.sign(        { userId: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: "1h" } // Token validity period    ); }

After receiving the user profile from Scalekit, issue a token and store it in an HTTP-only cookie:

app.post("/login", async (req, res) => { const user = await authenticateUser(req.body); // Verify user credentials if(!user) return res.status(401).json({ error: "Invalid credentials" }); const sessionToken = generateSessionToken(user); res.cookie("session_token", sessionToken, { httpOnly: true, secure: true }); res.json({ message: "Authentication successful" }); });

Step 2: Verify the token on protected routes

Every request to protected endpoints should validate the token:

function authenticateSession(req, res, next) { const token = req.cookies.session_token; if (!token) return res.status(401).json({ error: "Unauthorized" });    try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next();    } catch (error) { res.status(403).json({ error: "Invalid or expired session token" });    } }

Now, any protected routes will require a valid session token before granting access:

app.get("/dashboard", authenticateSession, (req, res) => {    res.json({ message: "Welcome to your dashboard", user: req.user }); });

Step 3: Refreshing expired tokens (Optional)

Want to keep sessions going without forcing logins?

Use a refresh token flow:

app.post("/refresh-token", async (req, res) => {    const refreshToken = req.cookies.refresh_token; if (!refreshToken) return res.status(401).json({ error: "Unauthorized" }); try {        const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET); const newSessionToken = generateSessionToken(decoded); res.cookie("session_token", newSessionToken, { httpOnly: true, secure: true }); res.json({ message: "Session refreshed" }); } catch (error) {        res.status(403).json({ error: "Invalid refresh token" });    } });

Best practices for secure session management

  • Store JWT tokens in HTTP-only cookies, not local storage.
  • Set expiration times to automatically log out inactive users.
  • Rotate secret keys periodically for better security.
  • Use refresh tokens cautiously and store them securely.
  • Enable multifactor authentication (MFA) for additional security.

Debugging and best practices for secure Node.js SSO

Even with everything set up, SSO can sometimes throw curveballs. Here are a few things that commonly go wrong, and how to get past them quickly:

Common SSO issues and fixes

  • Token Expiration → Use short-lived access tokens and implement refresh logic
  • ACS URL Mismatch → Double-check that the callback/redirect URL is correctly registered in your IdP config
  • Login loop? → Verify state params, redirect URIs, and that session tokens are being set correctly

Best practices for Secure SSO

  • Always use HTTPS to prevent token interception
  • Rotate secrets periodically for improved security
  • Enable centralised logging to trace auth events across your app and IdP

Wrapping up

Adding Single Sign-On to a Node.js app doesn’t have to mean fighting with SAML flows or hard-coding IdP configs for every client. Scalekit gives you a cleaner path, one integration that works across multiple IdPs, with way less boilerplate.

You’ve seen how to connect IdPs, route users through a secure login flow, and manage sessions using JWTs. And whether your customer uses Okta, Microsoft Entra ID, or Ping Identity, it’s the same setup on your side.

That’s the real power here: consistency. No reinventing the wheel every time a new enterprise knocks on your door.

Ready to simplify SSO in your Node.js stack? Start building with Scalekit today.

FAQ

How does Scalekit's Test IdP Simulator help during development?

It lets you test your SSO flow without connecting to a real Identity Provider. Great for validating redirects, callback handling, and session logic before going live.

How does Scalekit simplify Node.js SSO compared to traditional methods?

No manual SAML, OAuth, or OIDC setup. Scalekit gives you a single API that works across IdPs, so you spend less time wiring things together and more time shipping features.

How does Scalekit handle multiple IdPs without needing complex configurations?

You configure the IdP once in the Scalekit dashboard. From there, Scalekit handles routing, connection, and protocol translation automatically, no per-client custom logic needed.

Can Scalekit handle both SSO and social logins in my app?

Yes. You can support enterprise logins (Okta, Entra ID, OneLogin) and social auth (Google, GitHub, etc.) in the same flow.

Can I migrate my existing Node.js SSO setup to Scalekit?

Yes. Just swap your current login flow with Scalekit’s SDK, test it using the IdP simulator, and connect your existing IdPs via the dashboard.

No items found.
Ship Enterprise Auth in days

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
3 FREE SSO/SCIM connections
Built-in multi-tenancy and organizations
SAML, OIDC based SSO
SCIM provisioning for users, groups
Unlimited users
Unlimited social logins