Security Architecture
Authentication, authorization, data protection, and security patterns for Smart Shelf.
Security Architecture
This section covers the comprehensive security architecture, authentication patterns, authorization mechanisms, and data protection strategies used in Smart Shelf.
Security Overview
Smart Shelf implements a defense-in-depth security strategy with multiple layers of protection:
Security Philosophy
- Security by Design: Security considerations integrated from the ground up
- Principle of Least Privilege: Users and systems have minimal access needed
- Zero-Trust Architecture: Verify every request regardless of source
- Defense in Depth: Multiple security layers for comprehensive protection
Security Layers
- Client-side validation (User Experience)
- Server-side validation (Security Enforcement)
- Database-level security (Data Protection)
- Infrastructure-level security (Network Protection)
Security Architecture Sections
Authentication
Complete authentication system implementation including JWT tokens, session management, and multi-factor authentication support.
Key Topics:
- Authentication flow and token management
- Server-side and client-side authentication
- Session handling and security
- Password policies and requirements
Data Protection
Comprehensive data protection strategies including encryption, secure storage, and privacy safeguards.
Key Topics:
- Encryption at rest and in transit
- Application-level field encryption
- Secure data handling practices
- Data backup and recovery security
Authorization & Access Control
Role-based access control (RBAC), permissions management, and authorization guards throughout the application.
Key Topics:
- Role hierarchy and permissions
- Component-level authorization guards
- API route protection
- Database row-level security policies
Security Monitoring & Auditing
Comprehensive monitoring, audit logging, and threat detection systems for proactive security management.
Key Topics:
- Audit logging and event tracking
- Security headers and CSRF protection
- Rate limiting and abuse prevention
- Threat detection and incident response
Compliance & Privacy
GDPR compliance, data privacy measures, and regulatory requirements implementation.
Key Topics:
- GDPR compliance framework
- Data subject rights implementation
- Privacy by design principles
- Data retention and disposal policies
Security Architecture Flow
graph TB
A[Client Request] --> B[Security Headers]
B --> C[Rate Limiting]
C --> D[Authentication]
D --> E[Authorization]
E --> F[Data Access]
F --> G[Audit Logging]
H[Data Protection] --> F
I[Monitoring] --> G
J[Compliance] --> H
style A fill:#e1f5fe
style G fill:#f3e5f5
style H fill:#fff3e0
style I fill:#e8f5e8
style J fill:#fce4ec
// JWT Payload Structure
interface JWTPayload {
// Standard claims
sub: string; // User ID
iss: string; // Issuer (Supabase)
aud: string; // Audience
exp: number; // Expiration timestamp
iat: number; // Issued at timestamp
// Custom claims
email: string; // User email
role: string; // User role
warehouse_id?: string; // Default warehouse
permissions: string[]; // User permissions
session_id: string; // Session identifier
}
Authentication Implementation
Server-Side Authentication
// lib/auth/server.ts
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
export async function getCurrentUser() {
const supabase = createServerComponentClient({ cookies });
try {
const { data: { session }, error } = await supabase.auth.getSession();
if (error || !session) {
return null;
}
// Get user profile with permissions
const { data: profile } = await supabase
.from('user_profiles')
.select('*, user_roles(role, permissions)')
.eq('id', session.user.id)
.single();
return {
id: session.user.id,
email: session.user.email,
...profile,
};
} catch (error) {
console.error('Authentication error:', error);
return null;
}
}
export async function requireAuth() {
const user = await getCurrentUser();
if (!user) {
redirect('/auth/login');
}
return user;
}
Client-Side Authentication
// lib/auth/client.ts
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { useRouter } from 'next/navigation';
export function useAuth() {
const supabase = createClientComponentClient();
const router = useRouter();
const signIn = async (email: string, password: string) => {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
throw new Error(error.message);
}
router.push('/dashboard');
return data;
};
const signOut = async () => {
await supabase.auth.signOut();
router.push('/auth/login');
};
const signUp = async (email: string, password: string, userData: any) => {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: userData,
},
});
if (error) {
throw new Error(error.message);
}
return data;
};
return {
signIn,
signOut,
signUp,
};
}
Authorization Architecture
Role-Based Access Control (RBAC)
// lib/auth/roles.ts
export enum UserRole {
ADMIN = 'admin',
MANAGER = 'manager',
STAFF = 'staff',
READONLY = 'readonly',
}
export const ROLE_HIERARCHY = {
[UserRole.ADMIN]: 4,
[UserRole.MANAGER]: 3,
[UserRole.STAFF]: 2,
[UserRole.READONLY]: 1,
};
export const PERMISSIONS = {
// Product Management
'products:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF, UserRole.READONLY],
'products:create': [UserRole.ADMIN, UserRole.MANAGER],
'products:update': [UserRole.ADMIN, UserRole.MANAGER],
'products:delete': [UserRole.ADMIN],
// Inventory Management
'inventory:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF, UserRole.READONLY],
'inventory:adjust': [UserRole.ADMIN, UserRole.MANAGER],
'inventory:transfer': [UserRole.ADMIN, UserRole.MANAGER],
'inventory:count': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF],
// Order Management
'orders:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF, UserRole.READONLY],
'orders:create': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF],
'orders:update': [UserRole.ADMIN, UserRole.MANAGER, UserRole.STAFF],
'orders:approve': [UserRole.ADMIN, UserRole.MANAGER],
'orders:cancel': [UserRole.ADMIN, UserRole.MANAGER],
// Financial
'financial:read': [UserRole.ADMIN, UserRole.MANAGER, UserRole.READONLY],
'financial:reports': [UserRole.ADMIN, UserRole.MANAGER],
// User Management
'users:read': [UserRole.ADMIN],
'users:create': [UserRole.ADMIN],
'users:update': [UserRole.ADMIN],
'users:delete': [UserRole.ADMIN],
// System Administration
'system:settings': [UserRole.ADMIN],
'system:backup': [UserRole.ADMIN],
'system:audit': [UserRole.ADMIN],
} as const;
export function hasPermission(userRole: UserRole, permission: string): boolean {
const allowedRoles = PERMISSIONS[permission as keyof typeof PERMISSIONS];
return allowedRoles?.includes(userRole) ?? false;
}
export function requirePermission(user: User, permission: string): void {
if (!hasPermission(user.role, permission)) {
throw new Error(`Access denied: ${permission} permission required`);
}
}
export function hasHigherRole(userRole: UserRole, targetRole: UserRole): boolean {
return ROLE_HIERARCHY[userRole] > ROLE_HIERARCHY[targetRole];
}
Authorization Guards
Component-Level Authorization
// components/auth/role-guard.tsx
interface RoleGuardProps {
allowedRoles: UserRole[];
children: React.ReactNode;
fallback?: React.ReactNode;
}
export function RoleGuard({ allowedRoles, children, fallback }: RoleGuardProps) {
const { user } = useCurrentUser();
if (!user || !allowedRoles.includes(user.role)) {
return fallback || <UnauthorizedMessage />;
}
return <>{children}</>;
}
// Usage
<RoleGuard allowedRoles={[UserRole.ADMIN, UserRole.MANAGER]}>
<AdminPanel />
</RoleGuard>
Permission-Based Guards
// components/auth/permission-guard.tsx
interface PermissionGuardProps {
permission: string;
children: React.ReactNode;
fallback?: React.ReactNode;
}
export function PermissionGuard({ permission, children, fallback }: PermissionGuardProps) {
const { user } = useCurrentUser();
if (!user || !hasPermission(user.role, permission)) {
return fallback || null;
}
return <>{children}</>;
}
// Usage
<PermissionGuard permission="products:create">
<CreateProductButton />
</PermissionGuard>
API Route Authorization
// lib/auth/api-auth.ts
export function withAuth(
handler: (req: NextRequest, user: User) => Promise<NextResponse>,
requiredPermission?: string
) {
return async function(req: NextRequest) {
try {
// Validate authentication
const user = await validateAuth(req);
// Check permission if required
if (requiredPermission) {
requirePermission(user, requiredPermission);
}
return handler(req, user);
} catch (error) {
if (error.message.includes('token')) {
return NextResponse.json(
{ success: false, error: { code: 'UNAUTHORIZED', message: 'Invalid token' } },
{ status: 401 }
);
}
if (error.message.includes('Access denied')) {
return NextResponse.json(
{ success: false, error: { code: 'FORBIDDEN', message: error.message } },
{ status: 403 }
);
}
throw error;
}
};
}
// Usage
export const GET = withAuth(async (req, user) => {
// Handler with authenticated user
}, 'products:read');
Database Security (Row Level Security)
RLS Policy Patterns
User-Based Data Isolation
-- Users can only access their own profile
CREATE POLICY "user_profile_isolation" ON user_profiles
FOR ALL USING (
auth.uid() = id
);
-- Users can only view profiles in their organization
CREATE POLICY "organization_profile_access" ON user_profiles
FOR SELECT USING (
EXISTS (
SELECT 1 FROM user_profiles up
WHERE up.id = auth.uid()
AND up.organization_id = user_profiles.organization_id
)
);
Role-Based Data Access
-- Admins can access all data
CREATE POLICY "admin_full_access" ON products
FOR ALL USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND role = 'admin'
)
);
-- Managers can access data in their warehouse
CREATE POLICY "manager_warehouse_access" ON inventory
FOR ALL USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid()
AND role IN ('admin', 'manager')
AND (warehouse_id = inventory.warehouse_id OR role = 'admin')
)
);
-- Staff can only read data in their warehouse
CREATE POLICY "staff_warehouse_read" ON inventory
FOR SELECT USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid()
AND warehouse_id = inventory.warehouse_id
)
);
Time-Based Access Control
-- Users can only modify recent records
CREATE POLICY "recent_modifications_only" ON stock_movements
FOR UPDATE USING (
created_at > NOW() - INTERVAL '24 hours'
AND created_by = auth.uid()
);
-- Prevent modification of finalized orders
CREATE POLICY "no_finalized_order_changes" ON sales_orders
FOR UPDATE USING (
status NOT IN ('shipped', 'delivered', 'cancelled')
);
Data Masking and Field-Level Security
-- Hide sensitive customer data from staff
CREATE POLICY "customer_data_privacy" ON customers
FOR SELECT USING (
CASE
WHEN EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND role IN ('admin', 'manager')
) THEN true
ELSE false
END
);
-- Create a view with masked data for staff
CREATE OR REPLACE VIEW customer_summary AS
SELECT
id,
name,
CASE
WHEN EXISTS (
SELECT 1 FROM user_profiles
WHERE id = auth.uid() AND role IN ('admin', 'manager')
) THEN email
ELSE REGEXP_REPLACE(email, '(.{2}).*(@.*)', '\1***\2')
END as email,
city,
country,
is_active
FROM customers;
Data Protection
Data Encryption
Encryption at Rest
- Database: Supabase provides AES-256 encryption for all data at rest
- File Storage: All uploaded files encrypted using industry-standard encryption
- Backups: Encrypted backups stored in multiple geographic locations
Encryption in Transit
- HTTPS/TLS: All client-server communication encrypted with TLS 1.3
- Database Connections: SSL/TLS encryption for all database connections
- API Communications: End-to-end encryption for all API calls
Application-Level Encryption
// lib/encryption/field-encryption.ts
import { createCipher, createDecipher } from 'crypto';
const ENCRYPTION_KEY = process.env.FIELD_ENCRYPTION_KEY!;
const ALGORITHM = 'aes-256-gcm';
export function encryptField(text: string): string {
try {
const iv = crypto.randomBytes(16);
const cipher = createCipher(ALGORITHM, ENCRYPTION_KEY);
cipher.setAAD(Buffer.from('additional-auth-data'));
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
} catch (error) {
throw new Error('Encryption failed');
}
}
export function decryptField(encryptedText: string): string {
try {
const [ivHex, authTagHex, encrypted] = encryptedText.split(':');
const iv = Buffer.from(ivHex, 'hex');
const authTag = Buffer.from(authTagHex, 'hex');
const decipher = createDecipher(ALGORITHM, ENCRYPTION_KEY);
decipher.setAAD(Buffer.from('additional-auth-data'));
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (error) {
throw new Error('Decryption failed');
}
}
// Usage in database models
export async function createCustomer(customerData: CustomerInput) {
const encryptedData = {
...customerData,
// Encrypt sensitive fields
email: encryptField(customerData.email),
phone: encryptField(customerData.phone),
taxId: customerData.taxId ? encryptField(customerData.taxId) : null,
};
return supabase.from('customers').insert(encryptedData);
}
Data Privacy and GDPR Compliance
Personal Data Handling
// lib/privacy/gdpr.ts
export interface PersonalDataField {
table: string;
field: string;
category: 'identity' | 'contact' | 'financial' | 'behavioral';
retention: number; // days
}
export const PERSONAL_DATA_FIELDS: PersonalDataField[] = [
{ table: 'user_profiles', field: 'full_name', category: 'identity', retention: 2555 }, // 7 years
{ table: 'user_profiles', field: 'email', category: 'contact', retention: 2555 },
{ table: 'customers', field: 'email', category: 'contact', retention: 2555 },
{ table: 'customers', field: 'phone', category: 'contact', retention: 2555 },
{ table: 'audit_logs', field: 'user_agent', category: 'behavioral', retention: 365 }, // 1 year
];
export async function anonymizeUserData(userId: string): Promise<void> {
const supabase = createServiceRoleClient();
try {
// Start transaction
await supabase.rpc('begin_transaction');
// Anonymize user profile
await supabase
.from('user_profiles')
.update({
full_name: 'Anonymous User',
email: `anonymous-${userId}@deleted.local`,
avatar_url: null,
is_active: false,
anonymized_at: new Date().toISOString(),
})
.eq('id', userId);
// Anonymize related customer data if exists
await supabase
.from('customers')
.update({
name: 'Anonymous Customer',
email: `anonymous-customer-${userId}@deleted.local`,
phone: null,
contact_person: 'Anonymous',
})
.eq('created_by', userId);
// Keep audit trail but remove identifying information
await supabase
.from('audit_logs')
.update({
user_agent: 'Anonymous',
ip_address: '0.0.0.0',
anonymized: true,
})
.eq('user_id', userId);
await supabase.rpc('commit_transaction');
} catch (error) {
await supabase.rpc('rollback_transaction');
throw new Error('Failed to anonymize user data');
}
}
export async function exportUserData(userId: string): Promise<any> {
const supabase = createServiceRoleClient();
// Export all personal data for GDPR data portability
const [profile, orders, activities] = await Promise.all([
supabase.from('user_profiles').select('*').eq('id', userId).single(),
supabase.from('sales_orders').select('*').eq('created_by', userId),
supabase.from('audit_logs').select('*').eq('user_id', userId).order('created_at', { ascending: false }).limit(1000),
]);
return {
personal_data: {
profile: profile.data,
orders: orders.data,
activities: activities.data,
},
export_date: new Date().toISOString(),
retention_policy: 'Data will be retained according to our privacy policy',
};
}
Security Monitoring and Auditing
Audit Logging
// lib/audit/logger.ts
export interface AuditEvent {
action: string;
resource: string;
resourceId?: string;
userId: string;
ipAddress?: string;
userAgent?: string;
details?: any;
timestamp: Date;
}
export async function logAuditEvent(event: AuditEvent): Promise<void> {
const supabase = createServiceRoleClient();
try {
await supabase.from('audit_logs').insert({
action: event.action,
resource: event.resource,
resource_id: event.resourceId,
user_id: event.userId,
ip_address: event.ipAddress,
user_agent: event.userAgent,
details: event.details,
created_at: event.timestamp.toISOString(),
});
} catch (error) {
// Log to external service if database is unavailable
console.error('Audit logging failed:', error);
}
}
// Middleware for automatic audit logging
export function withAuditLog(action: string, resource: string) {
return function(handler: (req: NextRequest, user: User) => Promise<NextResponse>) {
return async function(req: NextRequest) {
const user = await validateAuth(req);
try {
const response = await handler(req, user);
// Log successful action
await logAuditEvent({
action,
resource,
userId: user.id,
ipAddress: req.headers.get('x-forwarded-for') || req.ip,
userAgent: req.headers.get('user-agent'),
timestamp: new Date(),
});
return response;
} catch (error) {
// Log failed action
await logAuditEvent({
action: `${action}_failed`,
resource,
userId: user.id,
ipAddress: req.headers.get('x-forwarded-for') || req.ip,
userAgent: req.headers.get('user-agent'),
details: { error: error.message },
timestamp: new Date(),
});
throw error;
}
};
};
}
Security Headers and CSRF Protection
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Security Headers
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('X-XSS-Protection', '1; mode=block');
// Content Security Policy
const csp = [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https:",
"connect-src 'self' https://*.supabase.co wss://*.supabase.co",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
].join('; ');
response.headers.set('Content-Security-Policy', csp);
// CSRF Protection for state-changing requests
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(request.method)) {
const origin = request.headers.get('origin');
const host = request.headers.get('host');
if (!origin || !host || !origin.includes(host)) {
return NextResponse.json(
{ error: 'Invalid origin' },
{ status: 403 }
);
}
}
return response;
}
export const config = {
matcher: [
'/((?!api/auth|_next/static|_next/image|favicon.ico).*)',
],
};
Security Best Practices Checklist
Development Security
- All user inputs validated and sanitized
- SQL injection prevention (parameterized queries)
- XSS prevention (output encoding)
- CSRF protection implemented
- Secure session management
- Password complexity requirements
- Rate limiting on API endpoints
- Error messages don't leak sensitive information
Infrastructure Security
- HTTPS enforced for all communications
- Security headers implemented
- Database connections encrypted
- Regular security updates applied
- Environment variables secured
- Secrets management implemented
- Network access controls configured
- Regular security audits performed
Data Security
- Data encryption at rest and in transit
- Regular backups with encryption
- Data retention policies implemented
- GDPR compliance measures
- Access logging and monitoring
- Row-level security policies
- Field-level encryption for sensitive data
- Secure data disposal procedures
This security architecture provides comprehensive protection for Smart Shelf while maintaining usability and performance. Regular security reviews and updates ensure ongoing protection against evolving threats.