SocketCluster Authentication Module
sc-auth is a foundational authentication module specifically designed for the SocketCluster real-time framework. It facilitates JSON Web Token (JWT) based authentication, which is the default mechanism in SocketCluster. This package, currently at version 6.0.0 (released approximately eight years ago), handles the core logic for signing and verifying JWTs within a SocketCluster environment. While newer SocketCluster documentation often guides developers towards using `agServer.auth.signToken` or `jsonwebtoken` directly, `sc-auth` provides a structured `AuthEngine` for this purpose. Its primary role is to enable persistent user sessions, cross-browser tab authentication, and secure access control by signing arbitrary data objects with a secret key. Due to its age, developers should be aware that active development is minimal, and practices may have evolved in the broader SocketCluster ecosystem. Its release cadence is effectively dormant, with its last major update occurring many years ago.
Common errors
-
TokenExpiredError: jwt expired
cause The provided JWT has passed its expiry time, making it invalid for use.fixImplement client-side logic to detect expired tokens and automatically request a new one (e.g., using a refresh token mechanism) or prompt the user to re-authenticate. Ensure server-side logic handles `TokenExpiredError` gracefully. -
JsonWebTokenError: invalid signature
cause The JWT's signature does not match, indicating the token was tampered with or signed with a different secret key than the one used for verification.fixVerify that the same `authKey` (or signature key) is consistently used across all signing and verification points in your application. Ensure no part of the token was altered after signing. This often happens if an `authKey` is mismatched between different services or deployments. -
TypeError: AuthEngine is not a constructor
cause Attempting to import `AuthEngine` using an incorrect syntax (e.g., CommonJS `require` in an ESM context, or an incorrect named import).fixFor CommonJS, use `const AuthEngine = require('sc-auth').AuthEngine;`. If using ESM, `sc-auth` might not natively support it for direct named imports; consider `const { AuthEngine } = await import('sc-auth');` or wrapping it in a CommonJS file that exports an ESM-compatible module. -
Error: Missing credentials for token verification
cause The `authKey` (or a corresponding public/private key pair) was not provided to the `AuthEngine` constructor or the `verifyToken` method.fixEnsure the `AuthEngine` is initialized with the correct `authKey` (e.g., `new AuthEngine(process.env.AUTH_SIGNATURE_KEY)`). Double-check environment variable loading and configuration.
Warnings
- breaking Version 6.0.0 of `sc-auth` (and SocketCluster v15+) introduced a breaking change by transitioning from callback-based functions to Promise-based asynchronous operations. Code expecting callbacks will fail.
- gotcha The `sc-auth` package has not seen active development in approximately eight years (as of 2026), with its last published version (6.0.0) dating back to then. While functional, it may lack modern features, security updates, or compatibility fixes for newer Node.js versions or evolving JWT standards.
- gotcha The `authKey` (or signature key) used to sign and verify JWTs is critical for security. Hardcoding it, using a weak key, or exposing it publicly severely compromises the integrity of your authentication system.
- gotcha JWTs, by design, are signed but not encrypted. Sensitive user data should never be stored directly in the JWT payload, as it can be read by anyone with the token.
Install
-
npm install sc-auth -
yarn add sc-auth -
pnpm add sc-auth
Imports
- AuthEngine
import { AuthEngine } from 'sc-auth';const AuthEngine = require('sc-auth').AuthEngine; - createAuthEngine
const { createAuthEngine } = require('sc-auth'); - signToken
const { signToken } = require('sc-auth');
Quickstart
const http = require('http');
const socketClusterServer = require('socketcluster-server');
const { AuthEngine } = require('sc-auth');
const AUTH_KEY = process.env.AUTH_SIGNATURE_KEY ?? 'my-secret-auth-key'; // NEVER hardcode in production!
const TOKEN_EXPIRY_IN_SECONDS = 3600; // 1 hour
const httpServer = http.createServer();
const authEngine = new AuthEngine(AUTH_KEY, {
algorithm: 'HS256', // Default algorithm
expiresIn: TOKEN_EXPIRY_IN_SECONDS
});
const agServer = socketClusterServer.attach(httpServer, {
authKey: AUTH_KEY,
authEngine: authEngine // Inject sc-auth's engine
});
(async () => {
for await (let { socket } of agServer.listener('connection')) {
// Example of authenticating a socket after a login event
socket.on('login', async (credentials, respond) => {
if (credentials.username === 'user' && credentials.password === 'pass') {
const tokenData = { username: credentials.username, role: 'admin' };
try {
const token = await authEngine.signToken(tokenData);
socket.authenticate(token);
respond(); // Acknowledge successful login
} catch (error) {
respond(error); // Send error back to client
}
} else {
respond(new Error('Invalid credentials'));
}
});
// Example middleware to check authenticated status
agServer.setMiddleware(agServer.MIDDLEWARE_INBOUND, async (middlewareStream) => {
for await (let action of middlewareStream) {
if (action.type === action.PUBLISH_IN) {
if (!action.socket.authToken) {
action.block(new Error('Authentication required to publish.'));
continue;
}
// Further authorization checks based on action.socket.authToken.role
if (action.socket.authToken.role !== 'admin' && action.channel === 'adminChannel') {
action.block(new Error('Not authorized for this channel.'));
continue;
}
}
action.next();
}
});
}
})();
httpServer.listen(8000, () => {
console.log(`SocketCluster server listening on port 8000`);
});