@carisls/sso-core
v1.2.0
Published
A core component used by other components
Readme
@carisls/sso-core
A core SSO (Single Sign-On) component library for JWT token validation, user mapping, and authorization. This package provides essential utilities for handling OIDC/OAuth2 authentication flows, token validation, and Express.js middleware integration.
Note: This package is typically not installed directly. It is automatically installed as a dependency of other SSO packages:
@carisls/sso-standard@carisls/sso-resource-api
Features
- 🔐 JWT token validation with JWKS support
- 🔑 Public key fetching and caching
- 👤 User token mapping and transformation
- 🛡️ Express.js authorization middleware
- 🔒 Encryption/decryption utilities
- 🌐 Multi-issuer support
- 📦 Supports both ESM and CommonJS
Installation
This package is usually installed automatically as a dependency. If you need to install it directly:
npm install @carisls/sso-coreUsage
ESM (ECMAScript Modules)
import {
authorize,
certFetcherModule,
encryptorModule,
HttpError,
issuerSelectorModule,
postForm,
tokenDecoderModule,
tokenValidatorModule,
userMapper
} from '@carisls/sso-core';CommonJS
const {
authorize,
certFetcherModule,
encryptorModule,
HttpError,
issuerSelectorModule,
postForm,
tokenDecoderModule,
tokenValidatorModule,
userMapper
} = require('@carisls/sso-core');API Reference
authorize(role, exceptions, redirectToLogin)
Express.js middleware for authorization filtering based on user roles.
Parameters:
role(string | string[] | undefined): Role(s) to filter by. If undefined, only checks for authenticated user.exceptions(string[] | undefined): Array of URL paths to skip authorization.redirectToLogin(boolean, default:false): Iftrue, redirects to login page instead of returning 401.
Returns: Express middleware function (req, res, next) => void
Example:
import express from 'express';
import { authorize } from '@carisls/sso-core';
const app = express();
// Require any authenticated user
app.use(authorize());
// Require specific role
app.use('/admin', authorize('admin'));
// Require one of multiple roles
app.use('/dashboard', authorize(['admin', 'user']));
// With exceptions and redirect
app.use(authorize('user', ['/public', '/health'], true));certFetcherModule(jwksUri, publicKeyCache)
Creates a function to fetch and cache public keys from a JWKS (JSON Web Key Set) endpoint.
Parameters:
jwksUri(string): URL to the JWKS endpoint (e.g.,https://example.com/.well-known/jwks.json)publicKeyCache(number, default:300): Cache expiration time in seconds.
Returns: Async function (kid) => Promise<string> that returns the PEM-formatted public key for the given key ID.
Example:
import { certFetcherModule } from '@carisls/sso-core';
const getCert = certFetcherModule('https://auth.example.com/.well-known/jwks.json', 600);
// Get public key for a specific key ID
const publicKey = await getCert('key-id-123');encryptorModule(password, passwordSalt, iterationCount)
Creates an encryption/decryption utility.
Parameters:
password(string): Encryption password.passwordSalt(string, default:'C5mp4Hl$X9wby#s5'): Salt for password derivation.iterationCount(number, default:123123): Number of iterations for key derivation.
Returns: Object with encrypt and decrypt methods.
Example:
import { encryptorModule } from '@carisls/sso-core';
const { encrypt, decrypt } = encryptorModule('my-secret-password');
const encrypted = encrypt('sensitive data');
const decrypted = decrypt(encrypted);issuerSelectorModule(providers, cache)
Creates a validator that supports multiple JWT issuers/providers.
Parameters:
providers(Array<{ ssoUrl?: string, iss?: string, publicKey?: string }>): Array of provider configurations.ssoUrl: OpenID Connect configuration URL (e.g.,https://auth.example.com)iss: Issuer identifier (used as fallback ifssoUrlfails)publicKey: Optional static public key (PEM format) if not using JWKS
cache(number): Public key cache duration in seconds.
Returns: Promise resolving to an object with:
validator(tokenEnc, nonce, decodeOnly): Async function to validate tokensproviders: Map of configured providers
Example:
import { issuerSelectorModule } from '@carisls/sso-core';
const { validator } = await issuerSelectorModule([
{ ssoUrl: 'https://auth1.example.com', iss: 'https://auth1.example.com' },
{ ssoUrl: 'https://auth2.example.com', iss: 'https://auth2.example.com' },
{ publicKey: '-----BEGIN PUBLIC KEY-----\n...', iss: 'static-issuer' }
], 600);
// Validate token
try {
const result = await validator(encryptedToken, nonce);
console.log('Token valid:', result.token);
} catch (error) {
console.error('Token validation failed:', error.message);
}
// Decode only (no validation)
const { token } = await validator(encryptedToken, null, true);postForm(url, data)
Sends a POST request with form-urlencoded data.
Parameters:
url(string): Target URL.data(object): Data object to be encoded as form data.
Returns: Promise resolving to the response data.
Example:
import { postForm } from '@carisls/sso-core';
const response = await postForm('https://auth.example.com/token', {
grant_type: 'authorization_code',
code: 'auth-code-123',
client_id: 'my-client',
client_secret: 'my-secret'
});tokenDecoderModule
Utility object for decoding JWT tokens without validation.
Methods:
header(encToken): Decodes and returns the JWT header.data(encToken): Decodes and returns the JWT payload.
Example:
import { tokenDecoderModule } from '@carisls/sso-core';
const token = 'eyJhbGciOiJSUzI1NiIs...';
const header = tokenDecoderModule.header(token);
const payload = tokenDecoderModule.data(token);
console.log('Algorithm:', header.alg);
console.log('User ID:', payload.sub);tokenValidatorModule(jwksUri, publicKeyCache, publicKey)
Creates a token validator function.
Parameters:
jwksUri(string | null): JWKS endpoint URL. Ifnull, usespublicKeyinstead.publicKeyCache(number): Cache duration in seconds (ignored ifpublicKeyis provided).publicKey(string | undefined): Optional static public key in PEM format.
Returns: Async function (tokenEnc, nonce) => Promise<{ token, publicKey }>
Example:
import { tokenValidatorModule } from '@carisls/sso-core';
// Using JWKS
const validateWithJWKS = tokenValidatorModule(
'https://auth.example.com/.well-known/jwks.json',
600
);
// Using static public key
const validateWithKey = tokenValidatorModule(
null,
0,
'-----BEGIN PUBLIC KEY-----\n...'
);
const result = await validateWithJWKS(encryptedToken, 'nonce-value');
console.log('Validated token:', result.token);
console.log('Public key used:', result.publicKey);userMapper(token, idToken)
Maps a JWT token to a standardized user object.
Parameters:
token(object): Decoded JWT access token.idToken(object | undefined): Optional decoded ID token.
Returns: User object with the following structure:
{
iss: string; // Issuer
id: string; // User ID (from 'sub')
sid?: string; // Session ID
sa: boolean; // Service account flag (true if no session)
azp?: string; // Authorized party
name?: { // Name object (only if session exists)
full: string;
familyName: string;
givenName: string;
};
email?: string; // Email (only if session exists)
roles: string[]; // User roles
}Example:
import { userMapper } from '@carisls/sso-core';
const decodedToken = {
iss: 'https://auth.example.com',
sub: 'user-123',
sid: 'session-456',
email: '[email protected]',
given_name: 'John',
family_name: 'Doe',
roles: ['user', 'admin']
};
const user = userMapper(decodedToken);
// {
// iss: 'https://auth.example.com',
// id: 'user-123',
// sid: 'session-456',
// sa: false,
// name: {
// full: 'John Doe',
// familyName: 'Doe',
// givenName: 'John'
// },
// email: '[email protected]',
// roles: ['user', 'admin']
// }Complete Example
import express from 'express';
import {
issuerSelectorModule,
userMapper,
authorize
} from '@carisls/sso-core';
const app = express();
// Configure SSO providers
const { validator } = await issuerSelectorModule([
{ ssoUrl: 'https://auth.example.com', iss: 'https://auth.example.com' }
], 600);
// Express middleware to validate tokens
app.use(async (req, res, next) => {
try {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return next({ status: 401, message: 'No token provided' });
}
const { token: decodedToken } = await validator(token);
req.user = userMapper(decodedToken);
next();
} catch (error) {
next({ status: 401, message: error.message });
}
});
// Protected route with role-based authorization
app.get('/admin', authorize('admin'), (req, res) => {
res.json({ message: 'Admin access granted', user: req.user });
});
app.listen(3000);License
MIT
Author
Mihovil Strujic [email protected]
