This module provides a tiered authentication system for api-ape WebSocket connections. It supports OPAQUE-based password authentication where the server never learns raw passwords, with MFA (WebAuthn/TOTP) for elevated security and extensibility for enterprise SSO adapters.
Key capabilities:
- OPAQUE/PAKE authentication β Password-authenticated key exchange (server never sees raw password)
- MFA support β WebAuthn (FIDO2) and TOTP (RFC 6238) for Tier 2 elevation
- Passport.js compatible β MFA adapters work with existing Passport.js strategies
- Tiered security model β Guest β Basic β Elevated β High Security
- State machine enforcement β No-downgrade rule, timeout handling, rate limiting
- Authorization middleware β Per-endpoint tier and permission checks
- Adapter pattern β Pluggable authentication methods (OPAQUE, WebAuthn, TOTP, LDAP, SAML, OAuth2)
Contributing? See the module files for implementation details.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AuthFramework β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Adapter Registry β β
β β - OPAQUE (Tier 1) β β β
β β - WebAuthn (Tier 2 MFA) β β β
β β - TOTP (Tier 2 MFA) β β β
β β - LDAP, SAML, OAuth2 (Tier 1, planned) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Per-Socket State Machines β β
β β - Tracks auth state per clientId β β
β β - Enforces tier requirements β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Message Router β β
β β - Routes auth messages to appropriate adapter β β
β β - Handles opaque_*, webauthn_*, totp_*, mfa_* messages β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Tier | Name | Description |
|---|---|---|
| 0 | GUEST | Unauthenticated, public endpoints only |
| 1 | BASIC | Identity verified via OPAQUE/SRP or enterprise SSO |
| 2 | ELEVATED | Tier 1 + MFA (WebAuthn or TOTP) |
| 3 | HIGH_SECURITY | Full 2-of-3 scheme for client-side key reconstruction |
const { createAuthFramework } = require('api-ape/server/security/auth');
const { createAuthMiddleware } = require('api-ape/server/socket/authMiddleware');
const authFramework = createAuthFramework({
opaque: {
// Provide your user storage functions
getUser: async (username) => db.users.findOne({ username }),
saveUser: async (username, data) => db.users.insertOne({ username, ...data })
},
webauthn: {
rpId: 'example.com',
rpName: 'My App',
// Optional: provide credential storage
getCredentials: async (userId) => db.webauthn.find({ userId }),
saveCredential: async (userId, credential) => db.webauthn.insertOne({ userId, ...credential })
},
totp: {
issuer: 'My App',
// Optional: provide secret storage
getSecret: async (userId) => db.totp.findOne({ userId }),
saveSecret: async (userId, data) => db.totp.upsertOne({ userId }, data)
},
mfaMethods: ['webauthn', 'totp'],
onAuthSuccess: (clientId, principal) => {
console.log(`${clientId} authenticated as ${principal.userId}`);
},
onMFASuccess: (clientId, principal, method) => {
console.log(`${clientId} elevated via ${method}`);
}
});const authMiddleware = createAuthMiddleware({
requirements: {
'admin/*': { tier: 2 }, // Admin endpoints require MFA
'user/*': { tier: 1 }, // User endpoints require auth
'public/*': { tier: 0 } // Public endpoints allow guests
},
defaultTier: 0
});const { ape } = require('api-ape');
ape(server, {
where: 'api',
authFramework, // Enable authentication
authMiddleware // Enable authorization
});Client Server
|-- opaque_reg_start ----------->| { user, clientNonce, regRequest }
|<- opaque_reg_response ---------| { serverNonce, ts, regResponse }
|-- opaque_reg_finish ---------->| { regRecord }
|<- opaque_reg_ok ---------------| { msg: "registered" }
Client Server
|-- opaque_auth_start ---------->| { user, clientNonce }
|<- opaque_auth_1 ---------------| { serverNonce, ts, envelope, oprfResponse }
|-- opaque_auth_2 -------------->| { clientAuth }
|<- opaque_auth_ok --------------| { assignedPrincipal, serverProof, tier: 1 }
Client Server
|-- webauthn_reg_start --------->| { userId, userName }
|<- webauthn_reg_challenge ------| { challenge, rp, user, pubKeyCredParams }
|-- webauthn_reg_finish -------->| { challenge, attestation }
|<- webauthn_reg_ok -------------| { credentialId }
Client Server
|-- webauthn_auth_start -------->| { userId }
|<- webauthn_auth_challenge -----| { challenge, allowCredentials }
|-- webauthn_auth_finish ------->| { challenge, assertion }
|<- webauthn_auth_ok ------------| { tier: 2 }
Client Server
|-- totp_setup_start ----------->| { userId }
|<- totp_setup_challenge --------| { secret, otpauthUri }
|-- totp_setup_verify ---------->| { code }
|<- totp_setup_ok ---------------| { }
Client Server
|-- totp_verify ---------------->| { userId, code }
|<- totp_ok --------------------| { tier: 2 }
Client Server
|-- mfa_challenge -------------->| { }
|<- mfa_challenge ---------------| { methods: [{ method: "totp" }, { method: "webauthn", challenge: {...} }] }
|-- mfa_verify ----------------->| { method: "totp", code: "123456" }
|<- mfa_elevated ----------------| { method: "totp", tier: 2 }
Both WebAuthn and TOTP adapters are compatible with Passport.js:
const passport = require('passport');
const { WebAuthnStrategy, TOTPStrategy } = require('api-ape/server/security/auth');
// Use with Passport.js
passport.use('webauthn', new WebAuthnStrategy({
rpId: 'example.com',
rpName: 'My App'
}, (user, done) => {
// Custom verification logic
done(null, user);
}));
passport.use('totp', new TOTPStrategy({
issuer: 'My App'
}, (user, done) => {
// Custom verification logic
done(null, user);
}));After authentication, controllers have access to auth state via this:
// api/protected/data.js
module.exports = function(query) {
// Check authentication
if (!this.isAuthenticated) {
throw new Error('Authentication required');
}
// Access user info
console.log('User:', this.principal.userId);
console.log('Roles:', this.principal.roles);
console.log('Tier:', this.authTier);
// Check tier requirement
if (!this.requiresTier(2)) {
throw new Error('MFA required for this operation');
}
return { data: 'sensitive info' };
};| Property | Type | Description |
|---|---|---|
this.isAuthenticated |
boolean |
Whether socket is authenticated (Tier β₯ 1) |
this.authTier |
number |
Current tier (0-3) |
this.principal |
object|null |
User info: { userId, roles, permissions } |
this.authState |
object|null |
Full auth state object |
this.requiresTier(n) |
function |
Check if socket meets minimum tier |
Query auth state for any connected client:
const client = this.clients.get(targetClientId);
if (client.isAuthenticated) {
console.log('User:', client.authState.principal.userId);
console.log('Tier:', client.authTier);
}GUEST β AUTHENTICATING β AUTHENTICATED (Tier 1)
β
βββββββββββββββββΌββββββββββββββββ
β β β
MFA_PENDING KEY_RECOVERY (stay Tier 1)
β β
βΌ βΌ
ELEVATED (2) HIGH_SECURITY (3)
Rules:
- No downgrade after authentication
- Higher tiers require completing lower tiers first
- Auth timeout closes incomplete sessions
- Rate limiting and lockout after failed attempts
| Feature | Description |
|---|---|
| Password protection | OPAQUE ensures server never sees raw password |
| Replay prevention | Single-use nonces with 30s expiry |
| No-downgrade | Cannot return to lower tier after auth |
| Rate limiting | Lockout after configurable failed attempts |
| Session binding | Auth bound to clientId + nonces + timestamp |
| TOTP replay protection | Tracks used counters to prevent code reuse |
| WebAuthn counter | Validates and updates authenticator counter |
auth/
βββ index.js # Auth framework coordinator
βββ index.test.js # Integration tests (23 tests)
βββ state-machine.js # State transitions, tier management
βββ state-machine.test.js # State machine tests (31 tests)
βββ nonce-manager.js # Single-use nonce handling
βββ adapters/
β βββ opaque.js # OPAQUE/SRP implementation
β βββ opaque.test.js # OPAQUE tests (12 tests)
β βββ webauthn.js # WebAuthn/FIDO2 adapter (Passport.js compatible)
β βββ webauthn.test.js # WebAuthn tests (25 tests)
β βββ totp.js # TOTP RFC 6238 adapter (Passport.js compatible)
β βββ totp.test.js # TOTP tests (35 tests)
βββ handlers/
βββ auth-messages.js # Message routing
Total: 114 tests passing
../README.mdβ Security module overview../../socket/authMiddleware.jsβ Authorization middleware../../socket/receiveContext.jsβ Controller context with auth../../../todo/Security.mdβ Security architecture design../../../todo/implementation-checklist.mdβ Implementation progress