Building Trust Through Production Logging & Authentication Hardening
In security-critical systems like smart lock management, trust is built through meticulous attention to operational visibility and authentication robustness. Recent improvements to our UnlockOS SDK demonstrate key patterns for hardening production systems while maintaining developer experience.
Environment-Aware Logging Strategy
Production systems require different logging approaches than development environments. Implementing environment-specific log levels prevents information leakage while maintaining operational visibility:
interface LogConfig {
level: 'debug' | 'info' | 'warn' | 'error';
sensitiveFields: string[];
maxLogSize: number;
}
class SecureLogger {
private config: LogConfig;
constructor(environment: 'development' | 'staging' | 'production') {
this.config = {
level: environment === 'production' ? 'warn' : 'debug',
sensitiveFields: ['password', 'token', 'key', 'secret'],
maxLogSize: environment === 'production' ? 1000 : 5000
};
}
log(level: string, message: string, data?: any) {
if (this.shouldLog(level)) {
const sanitized = this.sanitizeData(data);
console[level](message, sanitized);
}
}
private sanitizeData(data: any): any {
if (!data) return data;
const sanitized = { ...data };
this.config.sensitiveFields.forEach(field => {
if (sanitized[field]) {
sanitized[field] = '[REDACTED]';
}
});
return sanitized;
}
}OAuth State Management & Re-authentication Prevention
Authentication flows in smart lock systems must be bulletproof. A critical improvement involved preventing unintended re-authentication after OAuth callbacks:
class AuthenticationManager {
private oauthState: Map<string, {
timestamp: number;
used: boolean;
redirectUrl: string;
}> = new Map();
async handleOAuthCallback(code: string, state: string): Promise<void> {
const stateData = this.oauthState.get(state);
// Prevent replay attacks and duplicate processing
if (!stateData || stateData.used ||
Date.now() - stateData.timestamp > 300000) { // 5 min expiry
throw new Error('Invalid or expired OAuth state');
}
// Mark state as used immediately
stateData.used = true;
this.oauthState.set(state, stateData);
try {
await this.exchangeCodeForToken(code);
// Prevent main app from triggering re-authentication
this.clearAuthenticationTriggers();
} catch (error) {
this.logAuthenticationFailure(error, { state, timestamp: Date.now() });
throw error;
}
}
private clearAuthenticationTriggers(): void {
// Clear any pending authentication requests
sessionStorage.removeItem('pending_auth');
localStorage.removeItem('auth_redirect');
}
}Feature Removal as Security Hardening
Removing features can be as important as adding them. The removal of "guest mode" demonstrates how reducing attack surface improves security posture:
// Before: Complex access control with guest mode
interface AccessControl {
userType: 'admin' | 'host' | 'guest';
permissions: Permission[];
guestLimitations?: GuestLimitation[];
}
// After: Simplified, more secure access control
interface AccessControl {
userType: 'admin' | 'host';
permissions: Permission[];
authenticationRequired: true;
}
class SecurityAudit {
static validateAccess(user: AuthenticatedUser, resource: Resource): boolean {
// No guest mode = no unauthenticated access paths
if (!user.isAuthenticated) {
this.logUnauthorizedAccess(user.id, resource.id);
return false;
}
return this.checkPermissions(user.permissions, resource.requiredPermissions);
}
}Row-Level Security (RLS) Policy Hardening
Database security through Row-Level Security policies ensures data isolation even if application-level checks fail:
-- Secure role permission access
CREATE POLICY "role_permission_details_select" ON role_permission_details
FOR SELECT USING (
EXISTS (
SELECT 1 FROM user_organization_roles uor
WHERE uor.user_id = auth.uid()
AND uor.organization_id = role_permission_details.organization_id
AND uor.role IN ('admin', 'manager')
)
);
-- Audit trail for policy violations
CREATE OR REPLACE FUNCTION log_rls_violation()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO security_audit_log (user_id, table_name, action, timestamp)
VALUES (auth.uid(), TG_TABLE_NAME, TG_OP, NOW());
RETURN NULL;
END;
$$ LANGUAGE plpgsql;Production-Ready Error Handling
Robust error handling prevents information leakage while maintaining operational visibility:
class ProductionErrorHandler {
static handlePaymentError(error: unknown, context: PaymentContext): PaymentResult {
const errorId = this.generateErrorId();
// Log detailed error internally
logger.error('Payment processing failed', {
errorId,
error: error instanceof Error ? error.message : 'Unknown error',
stack: error instanceof Error ? error.stack : undefined,
context: this.sanitizeContext(context)
});
// Return sanitized error to client
return {
success: false,
errorId,
message: this.getPublicErrorMessage(error),
retryable: this.isRetryable(error)
};
}
private static getPublicErrorMessage(error: unknown): string {
// Never expose internal error details
if (error instanceof ValidationError) {
return error.publicMessage;
}
return 'An error occurred. Please contact support with error ID.';
}
}Capacity Management & System Limits
Implementing capacity limits prevents system abuse and ensures fair resource allocation:
interface CapacityConfig {
maxOccupancy: number;
warningThreshold: number; // 80% of max
blockNewCheckins: boolean;
}
class OccupancyManager {
async validateCheckIn(facilityId: string): Promise<CheckInValidation> {
const current = await this.getCurrentOccupancy(facilityId);
const config = await this.getCapacityConfig(facilityId);
const congestionRate = (current / config.maxOccupancy) * 100;
if (config.blockNewCheckins && current >= config.maxOccupancy) {
this.logCapacityViolation(facilityId, current, config.maxOccupancy);
return {
allowed: false,
reason: 'CAPACITY_EXCEEDED',
congestionRate
};
}
return {
allowed: true,
congestionRate,
warning: congestionRate > 80
};
}
}Summary
Building trust in security-critical systems requires:
- Layered Logging: Environment-specific log levels with sensitive data sanitization
- Authentication Hardening: Proper state management and replay attack prevention
- Attack Surface Reduction: Removing unnecessary features like guest modes
- Database Security: Row-level security policies as defense in depth
- Production Error Handling: Internal visibility without information leakage
- System Limits: Capacity management to prevent abuse
These patterns ensure that security is not just a feature, but a fundamental characteristic of the system architecture.