Single Sign-On (SSO) Security: Implementing SAML and OAuth2/OIDC

Single Sign-On (SSO) Security: Implementing SAML and OAuth2/OIDC

Whitespots Team ·
sso
saml
oauth2
oidc
authentication

Introduction

Single Sign-On (SSO) enables users to authenticate once and access multiple applications. This guide covers implementing secure SSO using SAML, OAuth 2.0, and OpenID Connect.

OAuth 2.0 / OpenID Connect Implementation

javascript
const passport = require('passport'); const { Strategy: OIDCStrategy } = require('passport-openidconnect'); passport.use('oidc', new OIDCStrategy({ issuer: 'https://accounts.example.com', authorizationURL: 'https://accounts.example.com/oauth2/authorize', tokenURL: 'https://accounts.example.com/oauth2/token', userInfoURL: 'https://accounts.example.com/oauth2/userinfo', clientID: process.env.OIDC_CLIENT_ID, clientSecret: process.env.OIDC_CLIENT_SECRET, callbackURL: 'https://app.example.com/auth/callback', scope: ['openid', 'profile', 'email'] }, async (issuer, profile, done) => { try { // Find or create user let user = await db.users.findOne({ email: profile.emails[0].value }); if (!user) { user = await db.users.create({ email: profile.emails[0].value, name: profile.displayName, ssoProvider: 'oidc', ssoId: profile.id }); } return done(null, user); } catch (error) { return done(error); } } )); // SSO login route app.get('/auth/sso', passport.authenticate('oidc') ); // SSO callback app.get('/auth/callback', passport.authenticate('oidc', { failureRedirect: '/login' }), (req, res) => { // Create session req.session.userId = req.user.id; res.redirect('/dashboard'); } );

SAML Implementation

javascript
const saml = require('passport-saml'); passport.use(new saml.Strategy({ entryPoint: 'https://idp.example.com/saml/sso', issuer: 'my-app', callbackUrl: 'https://app.example.com/auth/saml/callback', cert: fs.readFileSync('idp-cert.pem', 'utf-8'), identifierFormat: null }, async (profile, done) => { try { const user = await db.users.findOne({ email: profile.email }); if (!user) { return done(new Error('User not found')); } return done(null, user); } catch (error) { return done(error); } } )); // SAML endpoints app.get('/auth/saml', passport.authenticate('saml', { successRedirect: '/dashboard', failureRedirect: '/login' }) ); app.post('/auth/saml/callback', passport.authenticate('saml', { failureRedirect: '/login', failureFlash: true }), (req, res) => { res.redirect('/dashboard'); } );

Token Validation

javascript
const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); // JWKS client for token validation const client = jwksClient({ jwksUri: 'https://accounts.example.com/.well-known/jwks.json', cache: true, cacheMaxAge: 86400000 }); function getKey(header, callback) { client.getSigningKey(header.kid, (err, key) => { if (err) { return callback(err); } const signingKey = key.publicKey || key.rsaPublicKey; callback(null, signingKey); }); } // Validate ID token function validateIdToken(token) { return new Promise((resolve, reject) => { jwt.verify(token, getKey, { audience: process.env.OIDC_CLIENT_ID, issuer: 'https://accounts.example.com', algorithms: ['RS256'] }, (err, decoded) => { if (err) { return reject(err); } resolve(decoded); }); }); }

Session Management with SSO

javascript
// Create SSO session async function createSSOSession(user, idToken, accessToken) { const session = await db.sessions.create({ userId: user.id, idToken, accessToken, expiresAt: new Date(Date.now() + 3600 * 1000), ssoProvider: 'oidc' }); return session; } // Validate SSO session async function validateSSOSession(sessionId) { const session = await db.sessions.findById(sessionId); if (!session || session.expiresAt < new Date()) { return null; } // Optionally refresh token if needed if (shouldRefresh(session)) { await refreshSSOToken(session); } return session; } // SSO logout app.post('/auth/logout', async (req, res) => { const session = await db.sessions.findById(req.sessionId); // Destroy local session await db.sessions.delete(req.sessionId); // Redirect to SSO logout const logoutUrl = `https://accounts.example.com/logout?` + `post_logout_redirect_uri=${encodeURIComponent('https://app.example.com')}` + `&id_token_hint=${session.idToken}`; res.json({ logoutUrl }); });

SSO Security Best Practices

  • ✅ Validate all tokens cryptographically
  • ✅ Verify token issuer and audience
  • ✅ Use HTTPS for all SSO endpoints
  • ✅ Implement proper logout (SLO)
  • ✅ Validate redirect URIs
  • ✅ Use state parameter for CSRF protection
  • ✅ Store tokens securely
  • ✅ Implement token refresh
  • ✅ Monitor SSO authentication events
  • ✅ Regular security audits
  • ✅ Handle SSO failures gracefully

Conclusion

Secure SSO implementation requires proper token validation, session management, and security controls. Implement OAuth2/OIDC or SAML with comprehensive validation and monitoring for enterprise-grade SSO security.