A middleware implementing standard flow SSO
npm install @carisls/sso-standardA middleware implementing standard OIDC/OAuth2 authorization code flow SSO for Express.js applications. This package provides a complete, ready-to-use authentication solution with automatic token management, session handling, and user mapping.
- 🔐 Complete OIDC/OAuth2 authorization code flow implementation
- 🍪 Automatic cookie-based session management with encryption
- 🔄 Automatic token refresh handling
- 👤 User token mapping and request injection
- 🛡️ Built-in authorization middleware
- 🌐 Multi-provider support (Keycloak, Okta, and other OIDC providers)
- 🔑 JWKS-based public key validation with caching
- 📦 Supports both ESM and CommonJS
- 🎯 Customizable endpoints and paths
- 🔒 Secure cookie handling with encryption
``bash`
npm install @carisls/sso-standard
`javascript
import express from 'express';
import { router } from '@carisls/sso-standard';
const app = express();
app.use(router({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
// Simple format: comma-separated string
providers: 'https://auth.example.com',
// Or array format: providers: [{ ssoUrl: 'https://auth.example.com' }],
encPassword: 'your-encryption-password'
}));
app.listen(3000);
`
`javascript
const express = require('express');
const { router } = require('@carisls/sso-standard');
const app = express();
app.use(router({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
// Simple format: comma-separated string
providers: 'https://auth.example.com',
// Or array format: providers: [{ ssoUrl: 'https://auth.example.com' }],
encPassword: 'your-encryption-password'
}));
app.listen(3000);
`
Creates and configures an Express router with complete SSO authentication flow.
Parameters:
- options (Object): Configuration object with the following properties:clientId
- (string, required): SSO Client ID registered with your identity providerclientSecret
- (string, required): SSO Client Secretproviders
- (string | Array'https://auth1.example.com,https://auth2.example.com')ssoUrl
- An array of provider configuration objects:
- (string): Base URL for the provider (usually equal to iss). Used to discover OpenID Connect configurationiss
- (string, optional): Issuer identifier. If not provided, defaults to ssoUrlpublicKey
- (string, optional): Static public key in PEM format (if not using JWKS)encPassword
- (string, required): Password used for encrypting cookiesencPasswordSalt
- (string, optional): Salt for password derivation (default: 'C5mp4Hl$X9wby#s5')encIterationCount
- (number, optional): Iteration count for key derivation (default: 123123)publicKeyCache
- (number, optional): Public key caching expiration in seconds (default: 300)expOffset
- (number, optional): Force renewal of tokens earlier (in seconds) to handle clock skew issues (default: 0)groups
- (boolean, optional): Request groups scope from the provider (default: true)userMapper
- (function, optional): Custom function to map tokens to user object. Signature: (token, idToken) => userObjectpaths
- (Object, optional): Customize default endpoint pathslogin
- (string, default: '/login'): Login initiation endpointsso
- (string, default: '/sso'): SSO callback endpointafterLogin
- (string, default: '/'): Redirect after successful loginlogout
- (string, default: '/logout'): Logout initiation endpointafterLogout
- (string, default: '/'): Redirect after successful logout
Returns: Express Router instance with the following endpoints configured:
- GET /login (or custom path): Initiates SSO login flowGET /sso
- (or custom path): Handles SSO callback and token exchangeGET /logout
- (or custom path): Initiates logout flowreq.user
- Automatic token refresh middleware
- User injection middleware (adds to authenticated requests)
Example:
`javascript
import express from 'express';
import { router } from '@carisls/sso-standard';
const app = express();
app.use(router({
clientId: 'my-app-client',
clientSecret: 'my-secret-key',
// Simple format: comma-separated string
providers: 'https://auth.example.com',
// Or detailed format: array of objects
// providers: [
// {
// ssoUrl: 'https://auth.example.com',
// // iss defaults to ssoUrl if not provided
// }
// ],
encPassword: 'my-encryption-password-123',
publicKeyCache: 600,
expOffset: 60,
groups: true,
paths: {
login: '/auth/login',
sso: '/auth/callback',
afterLogin: '/dashboard',
logout: '/auth/logout',
afterLogout: '/'
},
userMapper: (token, idToken) => {
return {
id: token.sub,
email: token.email,
name: token.name,
roles: token.roles || []
};
}
}));
// Protected route - user is automatically available via req.user
app.get('/dashboard', (req, res) => {
res.json({
message: 'Welcome!',
user: req.user
});
});
app.listen(3000);
`
Express.js middleware for authorization filtering based on user roles. This middleware works in conjunction with the router to provide role-based access control.
Parameters:
- role (string | string[] | undefined, optional): Role(s) to filter by. If undefined, only checks for authenticated user.exceptions
- (string[] | undefined, optional): Array of URL paths to skip authorization checks.redirectToLogin
- (boolean, default: false): If true, redirects to login page instead of returning 401.
Returns: Express middleware function (req, res, next) => void
Example:
`javascript
import express from 'express';
import { router, authorize } from '@carisls/sso-standard';
const app = express();
// Setup SSO router
app.use(router({
clientId: 'my-client-id',
clientSecret: 'my-secret',
// Simple format: comma-separated string
providers: 'https://auth.example.com',
// Or array format: providers: [{ ssoUrl: 'https://auth.example.com' }],
encPassword: 'encryption-password'
}));
// Require any authenticated user
app.use(authorize());
// Require specific role
app.get('/admin', authorize('admin'), (req, res) => {
res.json({ message: 'Admin access granted', user: req.user });
});
// Require one of multiple roles
app.get('/dashboard', authorize(['admin', 'user']), (req, res) => {
res.json({ message: 'Dashboard access', user: req.user });
});
// With exceptions and redirect
app.use(authorize('user', ['/public', '/health'], true));
app.listen(3000);
`
`javascript
import express from 'express';
import { router, authorize } from '@carisls/sso-standard';
const app = express();
// Configure SSO middleware
app.use(router({
clientId: process.env.SSO_CLIENT_ID,
clientSecret: process.env.SSO_CLIENT_SECRET,
// Simple format: comma-separated string (iss defaults to ssoUrl)
providers: process.env.SSO_URL,
// Or array format with custom iss:
// providers: [
// {
// ssoUrl: process.env.SSO_URL,
// iss: process.env.SSO_ISSUER // optional, defaults to ssoUrl
// }
// ],
encPassword: process.env.ENCRYPTION_PASSWORD,
publicKeyCache: 600,
paths: {
login: '/login',
sso: '/sso',
afterLogin: '/dashboard',
logout: '/logout',
afterLogout: '/'
}
}));
// Public routes
app.get('/', (req, res) => {
res.send('Welcome! Login');
});
// Protected routes - require authentication
app.get('/dashboard', authorize(), (req, res) => {
res.json({
message: 'Dashboard',
user: req.user
});
});
// Admin routes - require admin role
app.get('/admin', authorize('admin'), (req, res) => {
res.json({
message: 'Admin panel',
user: req.user
});
});
// API routes with role-based access
app.get('/api/users', authorize(['admin', 'user-manager']), (req, res) => {
res.json({ users: [] });
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
`
1. Login Flow: When a user visits /login, they are redirected to the identity provider's authorization endpoint with the appropriate OAuth2 parameters.
2. Callback Handling: After authentication, the provider redirects back to /sso with an authorization code. The middleware exchanges this code for access, refresh, and ID tokens.
3. Token Storage: Tokens are encrypted and stored in HTTP-only cookies:
- x-session: Encrypted access tokenx-session-sso
- : Encrypted refresh tokenx-session-id
- : Encrypted ID token (used for logout)
4. User Injection: On each request, the middleware decrypts tokens, validates them, and injects a req.user object containing user information.
5. Token Refresh: The middleware automatically refreshes access tokens when they expire using the stored refresh token.
6. Logout: Visiting /logout clears all cookies and redirects to the identity provider's logout endpoint.
This package works with any OIDC-compliant identity provider, including:
- Keycloak
- Okta
- Auth0
- Azure AD
- Google Identity Platform
- Any other OIDC/OAuth2 provider
By default, the req.user object follows this structure (can be customized with userMapper):
`typescript`
{
iss: string; // Issuer
id: string; // User ID (from 'sub')
sid?: string; // Session ID
sa: boolean; // Service account flag
azp?: string; // Authorized party
name?: { // Name object (if available)
full: string;
familyName: string;
givenName: string;
};
email?: string; // Email (if available)
roles: string[]; // User roles
}
For production use, store sensitive values in environment variables:
`bash`
SSO_CLIENT_ID=your-client-id
SSO_CLIENT_SECRET=your-client-secret
SSO_URL=https://auth.example.com
SSO_ISSUER=https://auth.example.com
ENCRYPTION_PASSWORD=your-strong-encryption-password
- Cookie Encryption: All tokens are encrypted before being stored in cookies
- HTTP-Only Cookies: Prevents XSS attacks by making cookies inaccessible to JavaScript
- Secure Cookies: Automatically enabled when using HTTPS
- Token Validation: All tokens are validated using JWKS before use
- Automatic Refresh: Tokens are refreshed before expiration to prevent service interruption
- @carisls/sso-core: Core SSO utilities (token validation, encryption, etc.)express
- : Web frameworkcookie-parser
- : Cookie parsing middlewaredebug`: Debug logging utility
-
MIT
Mihovil Strujic