Access Control Skill
Purpose
This skill enforces access control requirements as defined in the Hack23 ISMS Access Control Policy. It ensures that all systems implement proper authentication, authorization, and session management based on the principle of least privilege.
Rules
Principle of Least Privilege
MUST:
- Grant minimum permissions necessary to perform job functions
- Default to deny access (allowlist approach)
- Separate duties for critical functions (no single person has complete control)
- Regularly review and revoke unnecessary permissions
- Document permission requirements for each role
- Implement time-limited access for temporary needs
MUST NOT:
- Grant administrative privileges by default
- Share user accounts between multiple people
- Use shared service accounts without audit trails
- Grant permanent access when temporary access is sufficient
- Allow users to escalate their own privileges
Role-Based Access Control (RBAC)
MUST:
- Define roles based on job functions
- Assign permissions to roles, not individual users
- Document role definitions and their permissions
- Implement role hierarchy where appropriate
- Enforce separation of duties through role conflicts
- Review role assignments quarterly
Roles for Hack23 Projects:
- Public: Unauthenticated users (read-only access to public content)
- User: Authenticated regular users (basic functionality)
- Contributor: Code contributors (read/write to repositories)
- Maintainer: Project maintainers (merge, release, moderate)
- Administrator: System administrators (full control, user management)
- Security Officer: Security team (audit logs, security settings)
MUST NOT:
- Create user-specific permissions (use roles instead)
- Assign multiple conflicting roles to same user
- Allow role self-assignment without approval
- Create overly broad roles that violate least privilege
Authentication Requirements
MUST:
- Require authentication for all non-public resources
- Implement multi-factor authentication (MFA) for:
- Administrative accounts
- Access to Confidential or Restricted data
- Remote access to internal systems
- Privileged operations (deployments, configuration changes)
- Enforce strong password requirements:
- Minimum 12 characters
- At least one uppercase, lowercase, number, symbol
- No common passwords (check against known breached passwords)
- No reuse of last 5 passwords
- Implement account lockout after 5 failed attempts
- Require password change every 90 days for privileged accounts
- Terminate sessions after 30 minutes of inactivity
- Support passwordless authentication (FIDO2/WebAuthn) where possible
MUST NOT:
- Use basic authentication over unencrypted connections
- Store passwords in plaintext or reversible encryption
- Allow anonymous access to sensitive resources
- Use weak hashing algorithms (MD5, SHA-1) for passwords
- Implement custom authentication without security review
- Bypass authentication for "trusted" networks
Authorization Requirements
MUST:
- Verify authorization at every access point
- Check permissions server-side (never rely on client-side checks)
- Validate user permissions for each resource accessed
- Implement attribute-based access control (ABAC) for complex policies
- Deny access by default (fail closed)
- Log all authorization failures
- Enforce authorization consistently across all interfaces (web, API, CLI)
MUST NOT:
- Assume authentication implies authorization
- Use client-side authorization checks as primary control
- Grant access based solely on URL/endpoint knowledge
- Skip authorization checks for internal APIs
- Use security through obscurity (hidden URLs don't require authentication)
Session Management
MUST:
- Generate cryptographically random session IDs (minimum 128 bits entropy)
- Set HTTPOnly flag on session cookies (prevent JavaScript access)
- Set Secure flag on session cookies (HTTPS only)
- Set SameSite=Strict or Lax on session cookies (CSRF protection)
- Implement session timeout (30 minutes inactivity, 8 hours absolute)
- Regenerate session ID after authentication
- Invalidate sessions on logout
- Store session data securely (encrypted, integrity-protected)
- Implement concurrent session limits
- Provide users visibility into active sessions
MUST NOT:
- Include session IDs in URLs
- Use predictable session IDs
- Transmit session cookies over unencrypted connections
- Store sensitive data in client-side storage (localStorage, sessionStorage)
- Allow session fixation attacks
- Share sessions across different security contexts
API Authentication
MUST:
- Require authentication for all non-public API endpoints
- Use OAuth 2.0 or OpenID Connect for third-party integrations
- Implement API key rotation (at least annually)
- Rate limit API requests to prevent abuse
- Validate API tokens on every request
- Use short-lived access tokens (< 1 hour) with refresh tokens
- Scope API tokens to minimum necessary permissions
MUST NOT:
- Include API keys in URLs or logs
- Use long-lived API keys without rotation
- Share API keys across multiple applications
- Transmit API keys over unencrypted connections
- Store API keys in client-side code
Access Reviews
MUST:
- Review user access rights quarterly
- Remove access for terminated employees immediately
- Review privileged access monthly
- Document access review results
- Revoke unused accounts after 90 days of inactivity
- Audit access control changes
MUST NOT:
- Skip access reviews for "trusted" users
- Allow indefinite access without review
- Ignore orphaned accounts
- Delay access revocation for terminated users
Examples
Example 1: RBAC Implementation (Node.js/Express)
// Define roles and permissions
const ROLES = {
PUBLIC: 'public',
USER: 'user',
CONTRIBUTOR: 'contributor',
MAINTAINER: 'maintainer',
ADMIN: 'administrator'
};
const PERMISSIONS = {
READ_PUBLIC: 'read:public',
READ_PRIVATE: 'read:private',
WRITE_CODE: 'write:code',
MERGE_PR: 'merge:pr',
MANAGE_USERS: 'manage:users',
MANAGE_SECURITY: 'manage:security'
};
// Role to permissions mapping
const rolePermissions = {
[ROLES.PUBLIC]: [PERMISSIONS.READ_PUBLIC],
[ROLES.USER]: [PERMISSIONS.READ_PUBLIC, PERMISSIONS.READ_PRIVATE],
[ROLES.CONTRIBUTOR]: [PERMISSIONS.READ_PUBLIC, PERMISSIONS.READ_PRIVATE, PERMISSIONS.WRITE_CODE],
[ROLES.MAINTAINER]: [PERMISSIONS.READ_PUBLIC, PERMISSIONS.READ_PRIVATE, PERMISSIONS.WRITE_CODE, PERMISSIONS.MERGE_PR],
[ROLES.ADMIN]: Object.values(PERMISSIONS) // All permissions
};
// Middleware to check permission
function requirePermission(permission) {
return (req, res, next) => {
// Must be authenticated
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
// Get user's permissions from their role
const userPermissions = rolePermissions[req.user.role] || [];
// Check if user has required permission
if (!userPermissions.includes(permission)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage in routes
app.get('/api/public/data', requirePermission(PERMISSIONS.READ_PUBLIC), (req, res) => {
// Public data endpoint
});
app.post('/api/code/commit', requirePermission(PERMISSIONS.WRITE_CODE), (req, res) => {
// Commit code endpoint
});
app.post('/api/pr/merge', requirePermission(PERMISSIONS.MERGE_PR), (req, res) => {
// Merge PR endpoint
});
Example 2: Secure Session Management (Express)
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const redisClient = redis.createClient({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASSWORD
});
// GOOD: Secure session configuration
app.use(session({
store: new RedisStore({
client: redisClient,
prefix: 'sess:'
}),
secret: process.env.SESSION_SECRET, // Strong random secret from env
name: 'sid', // Don't use default name
resave: false,
saveUninitialized: false,
rolling: true, // Reset expiry on activity
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
maxAge: 1800000, // 30 minutes
sameSite: 'strict', // CSRF protection
domain: 'hack23.com' // Explicit domain
}
}));
// Session timeout middleware
app.use((req, res, next) => {
if (req.session) {
const now = Date.now();
const lastActivity = req.session.lastActivity || now;
const inactivityTimeout = 30 * 60 * 1000; // 30 minutes
if (now - lastActivity > inactivityTimeout) {
req.session.destroy();
return res.status(401).json({ error: 'Session expired due to inactivity' });
}
req.session.lastActivity = now;
}
next();
});
// Regenerate session ID after authentication
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await authenticateUser(username, password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Regenerate session to prevent fixation
req.session.regenerate((err) => {
if (err) {
return res.status(500).json({ error: 'Session error' });
}
req.session.userId = user.id;
req.session.role = user.role;
req.session.lastActivity = Date.now();
res.json({ success: true, user: { id: user.id, role: user.role } });
});
});
// Logout
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ error: 'Logout failed' });
}
res.clearCookie('sid');
res.json({ success: true });
});
});
Example 3: MFA Implementation (TOTP)
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');
// Enable MFA for user
app.post('/api/user/mfa/enable', requireAuth, async (req, res) => {
const user = req.user;
// Generate secret
const secret = speakeasy.generateSecret({
name: `Hack23 (${user.email})`,
issuer: 'Hack23'
});
// Generate QR code
const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);
// Store secret (encrypted) in database
await db.users.update(user.id, {
mfa_secret: encrypt(secret.base32),
mfa_enabled: false // Not enabled until verified
});
res.json({
secret: secret.base32,
qrCode: qrCodeUrl
});
});
// Verify and activate MFA
app.post('/api/user/mfa/verify', requireAuth, async (req, res) => {
const { token } = req.body;
const user = req.user;
const userData = await db.users.findById(user.id);
const secret = decrypt(userData.mfa_secret);
// Verify token
const verified = speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: token,
window: 1 // Allow 1 step before/after for clock skew
});
if (!verified) {
return res.status(400).json({ error: 'Invalid verification code' });
}
// Enable MFA
await db.users.update(user.id, {
mfa_enabled: true
});
res.json({ success: true });
});
// Login with MFA
app.post('/api/login', async (req, res) => {
const { username, password, mfa_token } = req.body;
// Verify username/password
const user = await authenticateUser(username, password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Check if MFA is enabled
if (user.mfa_enabled) {
if (!mfa_token) {
return res.status(401).json({
error: 'MFA token required',
mfa_required: true
});
}
// Verify MFA token
const secret = decrypt(user.mfa_secret);
const verified = speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: mfa_token,
window: 1
});
if (!verified) {
return res.status(401).json({ error: 'Invalid MFA token' });
}
}
// Create session
req.session.regenerate((err) => {
if (err) {
return res.status(500).json({ error: 'Session error' });
}
req.session.userId = user.id;
req.session.role = user.role;
res.json({ success: true, user: { id: user.id, role: user.role } });
});
});
Example 4: API Key Management
const crypto = require('crypto');
// Generate API key
function generateApiKey() {
// Generate 32 bytes (256 bits) of random data
const key = crypto.randomBytes(32).toString('base64');
return key;
}
// Hash API key for storage
function hashApiKey(apiKey) {
return crypto.createHash('sha256').update(apiKey).digest('hex');
}
// Create API key with scopes
app.post('/api/keys', requireAuth, async (req, res) => {
const { name, scopes, expiresInDays } = req.body;
// Generate key
const apiKey = generateApiKey();
const hashedKey = hashApiKey(apiKey);
// Calculate expiry
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + (expiresInDays || 365));
// Store hashed key
await db.apiKeys.create({
user_id: req.user.id,
name: name,
key_hash: hashedKey,
scopes: scopes,
expires_at: expiresAt,
created_at: new Date()
});
// Return plaintext key ONCE (user must save it)
res.json({
apiKey: apiKey,
name: name,
scopes: scopes,
expiresAt: expiresAt,
warning: 'Save this key now. It will not be shown again.'
});
});
// Validate API key middleware
async function validateApiKey(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'API key required' });
}
const apiKey = authHeader.substring(7);
const hashedKey = hashApiKey(apiKey);
// Lookup key
const keyRecord = await db.apiKeys.findByHash(hashedKey);
if (!keyRecord) {
return res.status(401).json({ error: 'Invalid API key' });
}
// Check expiry
if (new Date() > keyRecord.expires_at) {
return res.status(401).json({ error: 'API key expired' });
}
// Check if key is active
if (!keyRecord.active) {
return res.status(401).json({ error: 'API key revoked' });
}
// Update last used
await db.apiKeys.updateLastUsed(keyRecord.id);
// Attach user and scopes to request
req.apiKey = keyRecord;
req.user = await db.users.findById(keyRecord.user_id);
next();
}
// Usage
app.get('/api/data', validateApiKey, requireScope('read:data'), (req, res) => {
// API endpoint
});
Related ISMS Policies
Related Documentation
Compliance Mapping
ISO 27001:2022
- A.5.15 Access control
- A.5.16 Identity management
- A.5.17 Authentication information
- A.5.18 Access rights
NIST Cybersecurity Framework
- PR.AC-1: Identities and credentials are issued, managed, verified, revoked
- PR.AC-4: Access permissions and authorizations are managed
- PR.AC-6: Identities are proofed and bound to credentials
- PR.AC-7: Users, devices, and assets are authenticated
CIS Controls
- Control 5: Account Management
- Control 6: Access Control Management
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: access-control3description: Least privilege principle, RBAC, authentication, authorization, and session management following Hack23 Access Control Policy Use when this capability is needed.4---56# Access Control Skill78## Purpose910This skill enforces access control requirements as defined in the [Hack23 ISMS Access Control Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Access_Control_Policy.md). It ensures that all systems implement proper authentication, authorization, and session management based on the principle of least privilege.1112## Rules1314### Principle of Least Privilege1516**MUST:**17- Grant minimum permissions necessary to perform job functions18- Default to deny access (allowlist approach)19- Separate duties for critical functions (no single person has complete control)20- Regularly review and revoke unnecessary permissions21- Document permission requirements for each role22- Implement time-limited access for temporary needs2324**MUST NOT:**25- Grant administrative privileges by default26- Share user accounts between multiple people27- Use shared service accounts without audit trails28- Grant permanent access when temporary access is sufficient29- Allow users to escalate their own privileges3031### Role-Based Access Control (RBAC)3233**MUST:**34- Define roles based on job functions35- Assign permissions to roles, not individual users36- Document role definitions and their permissions37- Implement role hierarchy where appropriate38- Enforce separation of duties through role conflicts39- Review role assignments quarterly4041**Roles for Hack23 Projects:**42- **Public**: Unauthenticated users (read-only access to public content)43- **User**: Authenticated regular users (basic functionality)44- **Contributor**: Code contributors (read/write to repositories)45- **Maintainer**: Project maintainers (merge, release, moderate)46- **Administrator**: System administrators (full control, user management)47- **Security Officer**: Security team (audit logs, security settings)4849**MUST NOT:**50- Create user-specific permissions (use roles instead)51- Assign multiple conflicting roles to same user52- Allow role self-assignment without approval53- Create overly broad roles that violate least privilege5455### Authentication Requirements5657**MUST:**58- Require authentication for all non-public resources59- Implement multi-factor authentication (MFA) for:60 - Administrative accounts61 - Access to Confidential or Restricted data62 - Remote access to internal systems63 - Privileged operations (deployments, configuration changes)64- Enforce strong password requirements:65 - Minimum 12 characters66 - At least one uppercase, lowercase, number, symbol67 - No common passwords (check against known breached passwords)68 - No reuse of last 5 passwords69- Implement account lockout after 5 failed attempts70- Require password change every 90 days for privileged accounts71- Terminate sessions after 30 minutes of inactivity72- Support passwordless authentication (FIDO2/WebAuthn) where possible7374**MUST NOT:**75- Use basic authentication over unencrypted connections76- Store passwords in plaintext or reversible encryption77- Allow anonymous access to sensitive resources78- Use weak hashing algorithms (MD5, SHA-1) for passwords79- Implement custom authentication without security review80- Bypass authentication for "trusted" networks8182### Authorization Requirements8384**MUST:**85- Verify authorization at every access point86- Check permissions server-side (never rely on client-side checks)87- Validate user permissions for each resource accessed88- Implement attribute-based access control (ABAC) for complex policies89- Deny access by default (fail closed)90- Log all authorization failures91- Enforce authorization consistently across all interfaces (web, API, CLI)9293**MUST NOT:**94- Assume authentication implies authorization95- Use client-side authorization checks as primary control96- Grant access based solely on URL/endpoint knowledge97- Skip authorization checks for internal APIs98- Use security through obscurity (hidden URLs don't require authentication)99100### Session Management101102**MUST:**103- Generate cryptographically random session IDs (minimum 128 bits entropy)104- Set HTTPOnly flag on session cookies (prevent JavaScript access)105- Set Secure flag on session cookies (HTTPS only)106- Set SameSite=Strict or Lax on session cookies (CSRF protection)107- Implement session timeout (30 minutes inactivity, 8 hours absolute)108- Regenerate session ID after authentication109- Invalidate sessions on logout110- Store session data securely (encrypted, integrity-protected)111- Implement concurrent session limits112- Provide users visibility into active sessions113114**MUST NOT:**115- Include session IDs in URLs116- Use predictable session IDs117- Transmit session cookies over unencrypted connections118- Store sensitive data in client-side storage (localStorage, sessionStorage)119- Allow session fixation attacks120- Share sessions across different security contexts121122### API Authentication123124**MUST:**125- Require authentication for all non-public API endpoints126- Use OAuth 2.0 or OpenID Connect for third-party integrations127- Implement API key rotation (at least annually)128- Rate limit API requests to prevent abuse129- Validate API tokens on every request130- Use short-lived access tokens (< 1 hour) with refresh tokens131- Scope API tokens to minimum necessary permissions132133**MUST NOT:**134- Include API keys in URLs or logs135- Use long-lived API keys without rotation136- Share API keys across multiple applications137- Transmit API keys over unencrypted connections138- Store API keys in client-side code139140### Access Reviews141142**MUST:**143- Review user access rights quarterly144- Remove access for terminated employees immediately145- Review privileged access monthly146- Document access review results147- Revoke unused accounts after 90 days of inactivity148- Audit access control changes149150**MUST NOT:**151- Skip access reviews for "trusted" users152- Allow indefinite access without review153- Ignore orphaned accounts154- Delay access revocation for terminated users155156## Examples157158### Example 1: RBAC Implementation (Node.js/Express)159160```javascript161// Define roles and permissions162const ROLES = {163 PUBLIC: 'public',164 USER: 'user',165 CONTRIBUTOR: 'contributor',166 MAINTAINER: 'maintainer',167 ADMIN: 'administrator'168};169170const PERMISSIONS = {171 READ_PUBLIC: 'read:public',172 READ_PRIVATE: 'read:private',173 WRITE_CODE: 'write:code',174 MERGE_PR: 'merge:pr',175 MANAGE_USERS: 'manage:users',176 MANAGE_SECURITY: 'manage:security'177};178179// Role to permissions mapping180const rolePermissions = {181 [ROLES.PUBLIC]: [PERMISSIONS.READ_PUBLIC],182 [ROLES.USER]: [PERMISSIONS.READ_PUBLIC, PERMISSIONS.READ_PRIVATE],183 [ROLES.CONTRIBUTOR]: [PERMISSIONS.READ_PUBLIC, PERMISSIONS.READ_PRIVATE, PERMISSIONS.WRITE_CODE],184 [ROLES.MAINTAINER]: [PERMISSIONS.READ_PUBLIC, PERMISSIONS.READ_PRIVATE, PERMISSIONS.WRITE_CODE, PERMISSIONS.MERGE_PR],185 [ROLES.ADMIN]: Object.values(PERMISSIONS) // All permissions186};187188// Middleware to check permission189function requirePermission(permission) {190 return (req, res, next) => {191 // Must be authenticated192 if (!req.user) {193 return res.status(401).json({ error: 'Authentication required' });194 }195 196 // Get user's permissions from their role197 const userPermissions = rolePermissions[req.user.role] || [];198 199 // Check if user has required permission200 if (!userPermissions.includes(permission)) {201 return res.status(403).json({ error: 'Insufficient permissions' });202 }203 204 next();205 };206}207208// Usage in routes209app.get('/api/public/data', requirePermission(PERMISSIONS.READ_PUBLIC), (req, res) => {210 // Public data endpoint211});212213app.post('/api/code/commit', requirePermission(PERMISSIONS.WRITE_CODE), (req, res) => {214 // Commit code endpoint215});216217app.post('/api/pr/merge', requirePermission(PERMISSIONS.MERGE_PR), (req, res) => {218 // Merge PR endpoint219});220```221222### Example 2: Secure Session Management (Express)223224```javascript225const session = require('express-session');226const RedisStore = require('connect-redis')(session);227const redis = require('redis');228229const redisClient = redis.createClient({230 host: process.env.REDIS_HOST,231 port: process.env.REDIS_PORT,232 password: process.env.REDIS_PASSWORD233});234235// GOOD: Secure session configuration236app.use(session({237 store: new RedisStore({ 238 client: redisClient,239 prefix: 'sess:' 240 }),241 secret: process.env.SESSION_SECRET, // Strong random secret from env242 name: 'sid', // Don't use default name243 resave: false,244 saveUninitialized: false,245 rolling: true, // Reset expiry on activity246 cookie: {247 secure: true, // HTTPS only248 httpOnly: true, // No JavaScript access249 maxAge: 1800000, // 30 minutes250 sameSite: 'strict', // CSRF protection251 domain: 'hack23.com' // Explicit domain252 }253}));254255// Session timeout middleware256app.use((req, res, next) => {257 if (req.session) {258 const now = Date.now();259 const lastActivity = req.session.lastActivity || now;260 const inactivityTimeout = 30 * 60 * 1000; // 30 minutes261 262 if (now - lastActivity > inactivityTimeout) {263 req.session.destroy();264 return res.status(401).json({ error: 'Session expired due to inactivity' });265 }266 267 req.session.lastActivity = now;268 }269 next();270});271272// Regenerate session ID after authentication273app.post('/login', async (req, res) => {274 const { username, password } = req.body;275 276 const user = await authenticateUser(username, password);277 if (!user) {278 return res.status(401).json({ error: 'Invalid credentials' });279 }280 281 // Regenerate session to prevent fixation282 req.session.regenerate((err) => {283 if (err) {284 return res.status(500).json({ error: 'Session error' });285 }286 287 req.session.userId = user.id;288 req.session.role = user.role;289 req.session.lastActivity = Date.now();290 291 res.json({ success: true, user: { id: user.id, role: user.role } });292 });293});294295// Logout296app.post('/logout', (req, res) => {297 req.session.destroy((err) => {298 if (err) {299 return res.status(500).json({ error: 'Logout failed' });300 }301 res.clearCookie('sid');302 res.json({ success: true });303 });304});305```306307### Example 3: MFA Implementation (TOTP)308309```javascript310const speakeasy = require('speakeasy');311const QRCode = require('qrcode');312313// Enable MFA for user314app.post('/api/user/mfa/enable', requireAuth, async (req, res) => {315 const user = req.user;316 317 // Generate secret318 const secret = speakeasy.generateSecret({319 name: `Hack23 (${user.email})`,320 issuer: 'Hack23'321 });322 323 // Generate QR code324 const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);325 326 // Store secret (encrypted) in database327 await db.users.update(user.id, {328 mfa_secret: encrypt(secret.base32),329 mfa_enabled: false // Not enabled until verified330 });331 332 res.json({333 secret: secret.base32,334 qrCode: qrCodeUrl335 });336});337338// Verify and activate MFA339app.post('/api/user/mfa/verify', requireAuth, async (req, res) => {340 const { token } = req.body;341 const user = req.user;342 343 const userData = await db.users.findById(user.id);344 const secret = decrypt(userData.mfa_secret);345 346 // Verify token347 const verified = speakeasy.totp.verify({348 secret: secret,349 encoding: 'base32',350 token: token,351 window: 1 // Allow 1 step before/after for clock skew352 });353 354 if (!verified) {355 return res.status(400).json({ error: 'Invalid verification code' });356 }357 358 // Enable MFA359 await db.users.update(user.id, {360 mfa_enabled: true361 });362 363 res.json({ success: true });364});365366// Login with MFA367app.post('/api/login', async (req, res) => {368 const { username, password, mfa_token } = req.body;369 370 // Verify username/password371 const user = await authenticateUser(username, password);372 if (!user) {373 return res.status(401).json({ error: 'Invalid credentials' });374 }375 376 // Check if MFA is enabled377 if (user.mfa_enabled) {378 if (!mfa_token) {379 return res.status(401).json({ 380 error: 'MFA token required',381 mfa_required: true 382 });383 }384 385 // Verify MFA token386 const secret = decrypt(user.mfa_secret);387 const verified = speakeasy.totp.verify({388 secret: secret,389 encoding: 'base32',390 token: mfa_token,391 window: 1392 });393 394 if (!verified) {395 return res.status(401).json({ error: 'Invalid MFA token' });396 }397 }398 399 // Create session400 req.session.regenerate((err) => {401 if (err) {402 return res.status(500).json({ error: 'Session error' });403 }404 405 req.session.userId = user.id;406 req.session.role = user.role;407 408 res.json({ success: true, user: { id: user.id, role: user.role } });409 });410});411```412413### Example 4: API Key Management414415```javascript416const crypto = require('crypto');417418// Generate API key419function generateApiKey() {420 // Generate 32 bytes (256 bits) of random data421 const key = crypto.randomBytes(32).toString('base64');422 return key;423}424425// Hash API key for storage426function hashApiKey(apiKey) {427 return crypto.createHash('sha256').update(apiKey).digest('hex');428}429430// Create API key with scopes431app.post('/api/keys', requireAuth, async (req, res) => {432 const { name, scopes, expiresInDays } = req.body;433 434 // Generate key435 const apiKey = generateApiKey();436 const hashedKey = hashApiKey(apiKey);437 438 // Calculate expiry439 const expiresAt = new Date();440 expiresAt.setDate(expiresAt.getDate() + (expiresInDays || 365));441 442 // Store hashed key443 await db.apiKeys.create({444 user_id: req.user.id,445 name: name,446 key_hash: hashedKey,447 scopes: scopes,448 expires_at: expiresAt,449 created_at: new Date()450 });451 452 // Return plaintext key ONCE (user must save it)453 res.json({454 apiKey: apiKey,455 name: name,456 scopes: scopes,457 expiresAt: expiresAt,458 warning: 'Save this key now. It will not be shown again.'459 });460});461462// Validate API key middleware463async function validateApiKey(req, res, next) {464 const authHeader = req.headers.authorization;465 466 if (!authHeader || !authHeader.startsWith('Bearer ')) {467 return res.status(401).json({ error: 'API key required' });468 }469 470 const apiKey = authHeader.substring(7);471 const hashedKey = hashApiKey(apiKey);472 473 // Lookup key474 const keyRecord = await db.apiKeys.findByHash(hashedKey);475 476 if (!keyRecord) {477 return res.status(401).json({ error: 'Invalid API key' });478 }479 480 // Check expiry481 if (new Date() > keyRecord.expires_at) {482 return res.status(401).json({ error: 'API key expired' });483 }484 485 // Check if key is active486 if (!keyRecord.active) {487 return res.status(401).json({ error: 'API key revoked' });488 }489 490 // Update last used491 await db.apiKeys.updateLastUsed(keyRecord.id);492 493 // Attach user and scopes to request494 req.apiKey = keyRecord;495 req.user = await db.users.findById(keyRecord.user_id);496 497 next();498}499500// Usage501app.get('/api/data', validateApiKey, requireScope('read:data'), (req, res) => {502 // API endpoint503});504```505506## Related ISMS Policies507508- **[Access Control Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Access_Control_Policy.md)** - Primary policy for access control509- **[Information Security Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Information_Security_Policy.md)** - Overall security framework510- **[Acceptable Use Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Acceptable_Use_Policy.md)** - User responsibilities511512## Related Documentation513514- [SECURITY_ARCHITECTURE.md](../../../../SECURITY_ARCHITECTURE.md) - Security controls implementation515- [secure-development SKILL.md](../secure-development/SKILL.md) - Related secure coding practices516517## Compliance Mapping518519### ISO 27001:2022520- **A.5.15** Access control521- **A.5.16** Identity management522- **A.5.17** Authentication information523- **A.5.18** Access rights524525### NIST Cybersecurity Framework526- **PR.AC-1**: Identities and credentials are issued, managed, verified, revoked527- **PR.AC-4**: Access permissions and authorizations are managed528- **PR.AC-6**: Identities are proofed and bound to credentials529- **PR.AC-7**: Users, devices, and assets are authenticated530531### CIS Controls532- **Control 5**: Account Management533- **Control 6**: Access Control Management534535---536> Converted and distributed by [TomeVault](https://tomevault.io/claim/hack23) — claim your Tome and manage your conversions.537<!-- tomevault:4.0:skill_md:2026-04-12 -->