Local Bypass Mode for Development & Presentations
Document Overview
This document provides the comprehensive technical specification for implementing a secure local bypass mode in the BIO-QMS documentation viewer. The bypass mode enables zero-friction development and offline presentations while maintaining production security through multiple safety mechanisms.
Key Design Principles:
- Developer Productivity: Eliminate auth friction during local development
- Offline Presentations: Support demos without network connectivity
- Production Safety: Multiple layers prevent bypass in production deployments
- Audit Trail: All bypass activations logged for security monitoring
- Visual Indicators: Clear "DEV MODE" watermark when bypass active
Table of Contents
- Bypass Detection Logic
- Implementation Architecture
- Safety Mechanisms
- Development Mode Features
- Presentation Mode Integration
- Local Logging
- Configuration
- Testing with Bypass
- Security Audit Trail
- Documentation
- Implementation Guide
- Troubleshooting
1. Bypass Detection Logic
1.1 Detection Modes
The bypass system supports three detection modes, evaluated in order:
| Priority | Mode | Activation | Use Case |
|---|---|---|---|
| 1 | Environment Variable | VITE_AUTH_MODE=none | Primary dev mode, CI/CD testing |
| 2 | URL Parameter | ?bypass=local | Quick testing, presentations |
| 3 | Build-Time Flag | Vite dev server detection | Automatic dev mode |
1.2 Environment Variable Detection
Variable: VITE_AUTH_MODE=none
Behavior:
- Full bypass, no auth checks anywhere in application
- All auth provider methods return immediately
- No network requests to auth service
- All documents visible regardless of NDA status
Implementation:
// src/config/auth.config.ts
export type AuthMode = 'none' | 'gcp';
export interface AuthConfig {
mode: AuthMode;
bypassEnabled: boolean;
bypassSource: 'env' | 'url' | 'build' | null;
}
export function getAuthConfig(): AuthConfig {
// Priority 1: Environment variable
const envMode = import.meta.env.VITE_AUTH_MODE as AuthMode;
if (envMode === 'none') {
return {
mode: 'none',
bypassEnabled: true,
bypassSource: 'env'
};
}
// Priority 2: URL parameter (localhost only)
if (isLocalhostBypass()) {
return {
mode: 'none',
bypassEnabled: true,
bypassSource: 'url'
};
}
// Priority 3: Dev server detection
if (isDevServer()) {
return {
mode: 'none',
bypassEnabled: true,
bypassSource: 'build'
};
}
// Default: GCP auth required
return {
mode: 'gcp',
bypassEnabled: false,
bypassSource: null
};
}
1.3 URL Parameter Detection
Parameter: ?bypass=local
Security Constraints:
- Only activates on localhost, 127.0.0.1, or [::1]
- Production domains reject parameter with CSP violation
- Server-side validation (Cloud Run rejects parameter)
Hostname Verification:
// src/auth/bypass/hostnameValidator.ts
const ALLOWED_HOSTNAMES = [
'localhost',
'127.0.0.1',
'[::1]',
'0.0.0.0'
];
const PROD_DOMAINS = [
'docs.coditect.ai',
'bio-qms.docs.coditect.ai',
'coditect.ai'
];
export function isLocalhostBypass(): boolean {
const params = new URLSearchParams(window.location.search);
if (!params.has('bypass') || params.get('bypass') !== 'local') {
return false;
}
const hostname = window.location.hostname;
// Check if localhost
const isLocalhost = ALLOWED_HOSTNAMES.includes(hostname);
// Check if production domain
const isProd = PROD_DOMAINS.some(domain =>
hostname.includes(domain)
);
if (isProd && isLocalhost === false) {
console.error('SECURITY VIOLATION: Bypass parameter on production domain');
// Send security alert
sendSecurityAlert({
type: 'bypass_attempt_production',
hostname,
url: window.location.href,
timestamp: new Date().toISOString()
});
return false;
}
return isLocalhost;
}
1.4 Build-Time Detection
Method: Vite dev server vs production build detection
Implementation:
// src/auth/bypass/buildDetector.ts
export function isDevServer(): boolean {
// Vite sets this automatically
const isDev = import.meta.env.DEV;
const isProduction = import.meta.env.PROD;
// Check if running on Vite dev server (typically port 5173)
const isDevPort = window.location.port === '5173' || window.location.port === '3000';
// Dev mode if:
// 1. Vite DEV flag is true, OR
// 2. Running on typical dev port AND not production build
return isDev || (isDevPort && !isProduction);
}
export function getBuildInfo(): BuildInfo {
return {
env: import.meta.env.MODE,
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD,
timestamp: import.meta.env.VITE_BUILD_TIMESTAMP,
commit: import.meta.env.VITE_GIT_COMMIT,
version: import.meta.env.VITE_APP_VERSION || '1.0.0'
};
}
1.5 Detection Flow Diagram
┌─────────────────────┐
│ App Initialization │
└──────────┬──────────┘
│
┌──────▼──────┐
│ Check ENV │
│ AUTH_MODE? │
└──┬────────┬─┘
│ none │ gcp
│ │
▼ │
┌────┐ │
│Bypass│ │
│ ON │ │
└────┘ │
│
┌──────▼────────┐
│ Check URL │
│ ?bypass=local?│
└──┬────────┬───┘
│ Yes │ No
│ │
┌────▼────┐ │
│Localhost?│ │
└──┬───┬──┘ │
│Yes│No │
│ │ │
▼ ▼ │
┌────┐┌────┐│
│ON ││OFF ││
└────┘└────┘│
│
┌──────▼──────┐
│ Check Build │
│ Dev Server? │
└──┬────────┬─┘
│ Yes │ No
│ │
┌──▼──┐ ┌──▼──┐
│ ON │ │ GCP │
│ │ │ AUTH│
└─────┘ └─────┘
2. Implementation Architecture
2.1 Component Overview
┌──────────────────────────────────────────────────────┐
│ BIO-QMS Viewer App │
├──────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ AuthBypassProvider │ │
│ │ (Wraps AuthProvider, short-circuits checks) │ │
│ └────────────┬─────────────────────────────────┬─┘ │
│ │ │ │
│ ┌──────────▼──────────┐ ┌────────────▼───┐│
│ │ NoopTokenValidator │ │ MockUser ││
│ │ (validates all) │ │ Context ││
│ └─────────────────────┘ └────────────────┘│
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ DevModeIndicator │ │
│ │ (Prominent watermark banner) │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ LocalAccessLogger │ │
│ │ (Browser console + localStorage log) │ │
│ └────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
2.2 AuthBypassProvider
Purpose: Wraps the standard AuthProvider and short-circuits all auth checks when bypass is active.
Implementation:
// src/auth/bypass/AuthBypassProvider.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { AuthProvider, User } from '../AuthProvider.interface';
import { getAuthConfig } from '../../config/auth.config';
import { MockUserProvider } from './MockUserProvider';
import { NoopTokenValidator } from './NoopTokenValidator';
import { LocalAccessLogger } from './LocalAccessLogger';
interface BypassContextValue {
bypassActive: boolean;
bypassSource: 'env' | 'url' | 'build' | null;
mockUser: User;
}
const BypassContext = createContext<BypassContextValue | undefined>(undefined);
interface AuthBypassProviderProps {
authProvider: AuthProvider;
children: React.ReactNode;
}
export function AuthBypassProvider({ authProvider, children }: AuthBypassProviderProps) {
const config = getAuthConfig();
const [bypassActive] = useState(config.bypassEnabled);
const logger = new LocalAccessLogger();
useEffect(() => {
if (bypassActive) {
console.warn(
'%c⚠️ AUTH BYPASS ACTIVE — DO NOT USE IN PRODUCTION',
'background: #ff0000; color: #ffffff; font-size: 16px; font-weight: bold; padding: 10px;'
);
logger.logBypassActivation({
source: config.bypassSource!,
hostname: window.location.hostname,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
});
}
}, [bypassActive, config.bypassSource, logger]);
if (!bypassActive) {
// No bypass, use real auth provider
return <>{children}</>;
}
// Bypass active, wrap with mock provider
const mockUser: User = {
id: 'local-dev-user',
email: 'dev@localhost',
name: 'Local Development User',
orgId: 'local-org',
ndaStatus: 'accepted' // All documents accessible
};
const contextValue: BypassContextValue = {
bypassActive: true,
bypassSource: config.bypassSource!,
mockUser
};
return (
<BypassContext.Provider value={contextValue}>
<MockUserProvider user={mockUser}>
{children}
</MockUserProvider>
</BypassContext.Provider>
);
}
export function useBypass(): BypassContextValue {
const context = useContext(BypassContext);
if (!context) {
// Bypass not active
return {
bypassActive: false,
bypassSource: null,
mockUser: null as any
};
}
return context;
}
2.3 MockUserProvider
Purpose: Provides synthetic user data for components expecting authenticated context.
Implementation:
// src/auth/bypass/MockUserProvider.tsx
import React, { createContext, useContext } from 'react';
import { User } from '../AuthProvider.interface';
const MockUserContext = createContext<User | null>(null);
interface MockUserProviderProps {
user: User;
children: React.ReactNode;
}
export function MockUserProvider({ user, children }: MockUserProviderProps) {
return (
<MockUserContext.Provider value={user}>
{children}
</MockUserContext.Provider>
);
}
export function useMockUser(): User | null {
return useContext(MockUserContext);
}
// Extended mock user for testing different scenarios
export const MockUsers = {
developer: {
id: 'dev-user',
email: 'dev@localhost',
name: 'Local Developer',
orgId: 'local-org',
ndaStatus: 'accepted' as const
},
ndaPending: {
id: 'nda-pending-user',
email: 'pending@localhost',
name: 'NDA Pending User',
orgId: 'local-org',
ndaStatus: 'pending' as const
},
ndaDeclined: {
id: 'nda-declined-user',
email: 'declined@localhost',
name: 'NDA Declined User',
orgId: 'local-org',
ndaStatus: 'declined' as const
},
researcher: {
id: 'researcher-user',
email: 'researcher@localhost',
name: 'Research Scientist',
orgId: 'biotech-labs',
ndaStatus: 'accepted' as const
}
};
2.4 NoopTokenValidator
Purpose: Validates all tokens as true without making API calls.
Implementation:
// src/auth/bypass/NoopTokenValidator.ts
import { TokenValidator } from '../TokenValidator.interface';
export class NoopTokenValidator implements TokenValidator {
async validateToken(token: string): Promise<boolean> {
// Bypass mode: all tokens valid
return true;
}
async validateClaims(claims: Record<string, any>): Promise<boolean> {
// Bypass mode: all claims valid
return true;
}
async verifySignature(token: string): Promise<boolean> {
// Bypass mode: no signature verification
return true;
}
async checkExpiry(token: string): Promise<boolean> {
// Bypass mode: tokens never expire
return true;
}
isExpired(expiryTimestamp: number): boolean {
// Bypass mode: nothing is expired
return false;
}
}
2.5 Feature Flag Integration
Purpose: Enable granular bypass controls for specific features.
Implementation:
// src/config/featureFlags.ts
export interface FeatureFlags {
bypassAuth: boolean;
bypassNdaGating: boolean;
bypassRateLimiting: boolean;
bypassAnalytics: boolean;
showDevIndicators: boolean;
}
export function getFeatureFlags(): FeatureFlags {
const config = getAuthConfig();
const bypassActive = config.bypassEnabled;
return {
// Auth bypass
bypassAuth: bypassActive,
// NDA gating (only bypass if auth bypass active)
bypassNdaGating: bypassActive,
// Rate limiting (always bypass in dev)
bypassRateLimiting: isDevServer() || bypassActive,
// Analytics (bypass in dev only, not in presentations)
bypassAnalytics: isDevServer(),
// Dev indicators (show watermark)
showDevIndicators: bypassActive
};
}
// Hook for components
export function useFeatureFlag(flag: keyof FeatureFlags): boolean {
const flags = getFeatureFlags();
return flags[flag];
}
3. Safety Mechanisms
3.1 Production Bypass Prevention
Strategy: Multiple layers ensure bypass cannot activate in production.
Layer 1: Build-Time Validation
Script: scripts/validate-production-auth.sh
#!/bin/bash
# scripts/validate-production-auth.sh
set -e
ENV_FILE="${1:-.env.production}"
echo "🔒 Validating production auth configuration..."
if [ ! -f "$ENV_FILE" ]; then
echo "❌ Environment file not found: $ENV_FILE"
exit 1
fi
source "$ENV_FILE"
# Check AUTH_MODE
if [ "$VITE_AUTH_MODE" = "none" ]; then
echo "❌ CRITICAL: Production build MUST use AUTH_MODE=gcp, found: none"
exit 1
fi
if [ -z "$VITE_AUTH_MODE" ]; then
echo "❌ CRITICAL: VITE_AUTH_MODE is not set"
exit 1
fi
if [ "$VITE_AUTH_MODE" != "gcp" ]; then
echo "❌ CRITICAL: Invalid AUTH_MODE for production: $VITE_AUTH_MODE"
exit 1
fi
echo "✅ Production auth mode validated: gcp"
echo "✅ Production build is SAFE to deploy"
CI/CD Integration:
# .github/workflows/deploy.yml
jobs:
deploy-production:
runs-on: ubuntu-latest
steps:
- name: Validate Auth Configuration
run: |
./scripts/validate-production-auth.sh .env.production
if [ $? -ne 0 ]; then
echo "::error::Production auth validation failed"
exit 1
fi
- name: Build Production
run: npm run build --mode production
- name: Deploy to Cloud Run
run: gcloud run deploy bio-qms-viewer --image=$IMAGE
Layer 2: Vite Build Plugin
Purpose: Strip bypass code from production bundles.
// vite-plugins/stripBypassCode.ts
import { Plugin } from 'vite';
export function stripBypassCode(): Plugin {
return {
name: 'strip-bypass-code',
transform(code, id) {
// Only strip in production builds
if (process.env.NODE_ENV !== 'production') {
return null;
}
// Remove bypass detection code
let transformedCode = code;
// Replace bypass check with constant false
transformedCode = transformedCode.replace(
/isLocalhostBypass\(\)/g,
'false'
);
// Replace bypass parameter check
transformedCode = transformedCode.replace(
/params\.get\(['"]bypass['"]\)/g,
'null'
);
// Remove dev server detection
transformedCode = transformedCode.replace(
/isDevServer\(\)/g,
'false'
);
return {
code: transformedCode,
map: null
};
}
};
}
Vite Config:
// vite.config.ts
import { defineConfig } from 'vite';
import { stripBypassCode } from './vite-plugins/stripBypassCode';
export default defineConfig({
plugins: [
react(),
tailwindcss(),
// Strip bypass code in production
process.env.NODE_ENV === 'production' && stripBypassCode()
].filter(Boolean)
});
Layer 3: Server-Side Rejection
Purpose: Cloud Run rejects bypass parameter at edge.
Configuration: app.yaml
# Cloud Run configuration
env_variables:
VITE_AUTH_MODE: gcp
# CSP header rejects bypass parameter
handlers:
- url: /.*
script: auto
secure: always
redirect_http_response_code: 301
# Request filtering
before_request:
- validate_no_bypass_param
Middleware:
// server/middleware/validateBypass.ts
export function validateNoBypassParam(req, res, next) {
const bypassParam = req.query.bypass;
if (bypassParam) {
// Log security violation
console.error('Security violation: bypass parameter detected in production', {
ip: req.ip,
url: req.url,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString()
});
// Send alert to monitoring
sendSecurityAlert({
type: 'bypass_attempt',
details: {
ip: req.ip,
url: req.url
}
});
// Reject request
res.status(403).json({
error: 'Forbidden',
message: 'Bypass parameter not allowed in production'
});
return;
}
next();
}
Layer 4: CSP Header
Purpose: Content Security Policy prevents parameter injection.
Content-Security-Policy:
default-src 'self';
script-src 'self';
connect-src 'self' https://auth.coditect.ai;
form-action 'none';
Violation Reporting:
// CSP violation handler
window.addEventListener('securitypolicyviolation', (event) => {
console.error('CSP Violation:', event);
sendSecurityAlert({
type: 'csp_violation',
details: {
violatedDirective: event.violatedDirective,
blockedURI: event.blockedURI,
sourceFile: event.sourceFile,
lineNumber: event.lineNumber
}
});
});
3.2 Hostname Verification
Purpose: Bypass only activates on verified localhost addresses.
Implementation:
// src/auth/bypass/hostnameValidator.ts
export function isValidLocalhost(): boolean {
const hostname = window.location.hostname;
const validHostnames = [
'localhost',
'127.0.0.1',
'[::1]',
'0.0.0.0',
// Vite default dev server
'localhost:5173',
'127.0.0.1:5173'
];
return validHostnames.some(valid =>
hostname === valid || hostname.startsWith(valid)
);
}
export function validateHostnameBeforeBypass(): boolean {
if (!isValidLocalhost()) {
console.error('Bypass rejected: hostname validation failed', {
hostname: window.location.hostname,
href: window.location.href
});
return false;
}
// Additional check: not a production domain
const isProd = isProductionDomain(window.location.hostname);
if (isProd) {
console.error('Bypass rejected: production domain detected', {
hostname: window.location.hostname
});
return false;
}
return true;
}
function isProductionDomain(hostname: string): boolean {
const prodDomains = [
'coditect.ai',
'docs.coditect.ai',
'bio-qms.docs.coditect.ai'
];
return prodDomains.some(domain => hostname.includes(domain));
}
3.3 Visual Indicators
Purpose: Prominent "DEV MODE" banner when bypass active.
Implementation:
// src/components/DevModeIndicator.tsx
import React from 'react';
import { AlertTriangle } from 'lucide-react';
import { useBypass } from '../auth/bypass/AuthBypassProvider';
import { getBuildInfo } from '../auth/bypass/buildDetector';
export function DevModeIndicator() {
const { bypassActive, bypassSource } = useBypass();
if (!bypassActive) {
return null;
}
const buildInfo = getBuildInfo();
return (
<div className="dev-mode-banner">
<div className="dev-mode-banner-content">
<AlertTriangle size={24} className="dev-mode-icon" />
<div className="dev-mode-text">
<strong>DEVELOPMENT MODE — UNAUTHENTICATED</strong>
<span className="dev-mode-details">
Bypass: {bypassSource} | Build: {buildInfo.env} |
{buildInfo.commit && ` Commit: ${buildInfo.commit.slice(0, 7)}`}
</span>
</div>
<button
onClick={() => window.location.href = '/?bypass=off'}
className="dev-mode-disable"
>
Disable Bypass
</button>
</div>
</div>
);
}
Styles:
/* styles.css */
.dev-mode-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 9999;
background: linear-gradient(135deg, #ff6b6b 0%, #ff0000 100%);
border-bottom: 3px solid #cc0000;
padding: 12px 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.dev-mode-banner-content {
display: flex;
align-items: center;
gap: 16px;
max-width: 1400px;
margin: 0 auto;
color: white;
}
.dev-mode-icon {
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.1); }
}
.dev-mode-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.dev-mode-text strong {
font-size: 16px;
font-weight: 700;
letter-spacing: 0.5px;
}
.dev-mode-details {
font-size: 12px;
opacity: 0.9;
font-family: 'Monaco', 'Courier New', monospace;
}
.dev-mode-disable {
padding: 8px 16px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.4);
color: white;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
transition: all 0.2s;
}
.dev-mode-disable:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-1px);
}
/* Watermark in bottom-right */
.dev-mode-watermark {
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(255, 0, 0, 0.8);
color: white;
padding: 6px 12px;
border-radius: 4px;
font-size: 11px;
font-weight: 700;
z-index: 9998;
pointer-events: none;
font-family: 'Monaco', 'Courier New', monospace;
}
3.4 Console Warnings
Purpose: Log prominent warnings in browser console.
Implementation:
// src/auth/bypass/consoleWarnings.ts
export function displayConsoleWarning() {
const styles = {
error: 'background: #ff0000; color: #ffffff; font-size: 20px; font-weight: bold; padding: 12px 20px; border-radius: 4px;',
warn: 'background: #ff6b00; color: #ffffff; font-size: 14px; padding: 8px 12px;',
info: 'color: #666; font-size: 12px;'
};
console.log(
'%c⚠️ AUTH BYPASS ACTIVE',
styles.error
);
console.log(
'%cDO NOT USE IN PRODUCTION',
styles.warn
);
console.log(
'%cAll authentication checks are disabled. This mode is for local development and presentations only.',
styles.info
);
console.table({
'Bypass Source': getAuthConfig().bypassSource,
'Hostname': window.location.hostname,
'Build Environment': import.meta.env.MODE,
'Timestamp': new Date().toISOString()
});
// Repeat warning every 5 minutes
setInterval(() => {
console.warn('⚠️ AUTH BYPASS STILL ACTIVE');
}, 5 * 60 * 1000);
}
4. Development Mode Features
4.1 Hot Reload Without Re-authentication
Benefit: Vite HMR (Hot Module Replacement) works seamlessly without auth interruptions.
Implementation:
// src/main.tsx
if (import.meta.hot) {
import.meta.hot.accept(() => {
// Hot reload: preserve auth state
console.log('HMR: Module reloaded, auth state preserved');
});
}
4.2 All Documents Visible
Behavior: NDA gating disabled, all documents accessible regardless of audience scope.
Implementation:
// src/components/DocumentFilter.tsx
export function shouldShowDocument(doc: Document, user: User | null): boolean {
const { bypassActive } = useBypass();
// Bypass mode: show all documents
if (bypassActive) {
return true;
}
// Normal mode: check NDA status
if (doc.ndaRequired && user?.ndaStatus !== 'accepted') {
return false;
}
// Check org scope
if (doc.orgScope && doc.orgScope !== user?.orgId) {
return false;
}
return true;
}
4.3 Mock User Switcher
Purpose: Test different user roles/permissions without re-authentication.
Implementation:
// src/components/MockUserSwitcher.tsx
import React, { useState } from 'react';
import { User, ChevronDown } from 'lucide-react';
import { MockUsers } from '../auth/bypass/MockUserProvider';
import { useBypass } from '../auth/bypass/AuthBypassProvider';
export function MockUserSwitcher() {
const { bypassActive } = useBypass();
const [currentUser, setCurrentUser] = useState<string>('developer');
const [isOpen, setIsOpen] = useState(false);
if (!bypassActive) {
return null;
}
const users = Object.entries(MockUsers);
const selectedUser = MockUsers[currentUser as keyof typeof MockUsers];
return (
<div className="mock-user-switcher">
<button
onClick={() => setIsOpen(!isOpen)}
className="mock-user-button"
>
<User size={16} />
<span>{selectedUser.name}</span>
<ChevronDown size={14} />
</button>
{isOpen && (
<div className="mock-user-dropdown">
{users.map(([key, user]) => (
<button
key={key}
onClick={() => {
setCurrentUser(key);
setIsOpen(false);
// Update mock user in context
localStorage.setItem('mock_user_key', key);
window.location.reload();
}}
className={`mock-user-option ${currentUser === key ? 'active' : ''}`}
>
<div className="mock-user-info">
<strong>{user.name}</strong>
<span className="mock-user-email">{user.email}</span>
<span className={`mock-user-nda nda-${user.ndaStatus}`}>
NDA: {user.ndaStatus}
</span>
</div>
</button>
))}
</div>
)}
</div>
);
}
4.4 Network Tab Indicators
Purpose: Show "(bypassed)" indicator for auth requests in DevTools.
Implementation:
// src/auth/bypass/networkIndicator.ts
export function interceptAuthRequests() {
const { bypassActive } = useBypass();
if (!bypassActive) {
return;
}
// Intercept fetch for auth endpoints
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const [url, options] = args;
// Check if auth endpoint
if (typeof url === 'string' && url.includes('auth.coditect.ai')) {
console.log(
'%c[BYPASSED]',
'background: #ff6b00; color: white; padding: 2px 6px; border-radius: 2px;',
url
);
// Return mock response
return Promise.resolve(new Response(
JSON.stringify({ bypassed: true }),
{
status: 200,
headers: { 'Content-Type': 'application/json' }
}
));
}
return originalFetch(...args);
};
}
4.5 Performance Profiling
Benefit: Profile without auth overhead for accurate measurements.
Usage:
// Example: Profile document loading
import { useBypass } from '../auth/bypass/AuthBypassProvider';
export function useDocumentLoad(docId: string) {
const { bypassActive } = useBypass();
useEffect(() => {
const startTime = performance.now();
loadDocument(docId).then(() => {
const loadTime = performance.now() - startTime;
console.log(`Document loaded in ${loadTime.toFixed(2)}ms`, {
bypassed: bypassActive,
authOverhead: bypassActive ? '0ms' : 'included'
});
});
}, [docId, bypassActive]);
}
5. Presentation Mode Integration
5.1 Seamless Integration
Design: Presentation mode (A.3) works seamlessly with bypass.
Implementation:
// src/components/PresentationMode.tsx
export function PresentationMode({ doc }: PresentationModeProps) {
const { bypassActive } = useBypass();
const [isPresentationMode, setIsPresentationMode] = useState(false);
// Presentation mode benefits from bypass:
// - No auth popups during screen sharing
// - Clean URLs without tokens
// - No network latency from auth checks
useEffect(() => {
if (isPresentationMode && bypassActive) {
console.log('Presentation mode + bypass active: optimal for demos');
}
}, [isPresentationMode, bypassActive]);
return (
<div className={isPresentationMode ? 'presentation-mode' : ''}>
{/* Presentation UI */}
</div>
);
}
5.2 No Auth Popups
Behavior: Screen sharing never interrupted by auth prompts.
Rationale:
- Bypass mode = no auth required
- No token expiry checks during presentation
- No network requests to auth service
5.3 Clean URLs
Benefit: URLs don't contain JWT tokens or auth parameters.
Example:
✅ With Bypass: https://localhost:5173/#/compliance/fda-21-cfr-part-11
❌ With Auth: https://bio-qms.docs.coditect.ai/#/compliance/fda-21-cfr-part-11?token=eyJhbG...
5.4 Audience Scope Override
Purpose: Demo all documents regardless of NDA status.
Implementation:
// src/filters/audienceFilter.ts
export function getVisibleDocuments(
documents: Document[],
user: User | null
): Document[] {
const { bypassActive } = useBypass();
if (bypassActive) {
// Bypass: show all documents
return documents;
}
// Normal: filter by NDA status and org
return documents.filter(doc => {
if (doc.ndaRequired && user?.ndaStatus !== 'accepted') {
return false;
}
if (doc.orgScope && doc.orgScope !== user?.orgId) {
return false;
}
return true;
});
}
6. Local Logging
6.1 Browser Console Logging
Purpose: All access events logged to browser console in dev mode.
Implementation:
// src/logging/devLogger.ts
export class DevLogger {
private enabled: boolean;
constructor() {
this.enabled = useBypass().bypassActive;
}
logAccess(doc: Document) {
if (!this.enabled) return;
console.log(
'%c[ACCESS]',
'background: #00aa00; color: white; padding: 2px 6px; border-radius: 2px;',
{
document: doc.title,
path: doc.path,
ndaRequired: doc.ndaRequired,
timestamp: new Date().toISOString()
}
);
}
logBypassActivation(details: BypassActivationDetails) {
console.group('%c⚠️ BYPASS ACTIVATED', 'font-size: 14px; font-weight: bold;');
console.table(details);
console.groupEnd();
}
logSecurityCheck(check: SecurityCheck) {
console.log(
'%c[SECURITY]',
'background: #ff6b00; color: white; padding: 2px 6px;',
check
);
}
}
export const devLogger = new DevLogger();
6.2 localStorage Access Log
Purpose: Persistent access log for debugging across sessions.
Implementation:
// src/logging/localAccessLog.ts
interface AccessLogEntry {
timestamp: string;
document: string;
path: string;
bypassActive: boolean;
userAgent: string;
}
export class LocalAccessLog {
private storageKey = 'bio_qms_access_log';
private maxEntries = 1000;
logAccess(doc: Document) {
const { bypassActive } = useBypass();
const entry: AccessLogEntry = {
timestamp: new Date().toISOString(),
document: doc.title,
path: doc.path,
bypassActive,
userAgent: navigator.userAgent
};
// Append to log
const log = this.getLog();
log.push(entry);
// Trim if too large
if (log.length > this.maxEntries) {
log.shift();
}
// Save
localStorage.setItem(this.storageKey, JSON.stringify(log));
}
getLog(): AccessLogEntry[] {
try {
const stored = localStorage.getItem(this.storageKey);
return stored ? JSON.parse(stored) : [];
} catch {
return [];
}
}
clearLog() {
localStorage.removeItem(this.storageKey);
}
exportLog(): string {
const log = this.getLog();
return JSON.stringify(log, null, 2);
}
}
export const localAccessLog = new LocalAccessLog();
6.3 Export Local Log
Purpose: Export log for testing validation or debugging.
Implementation:
// src/components/AccessLogExporter.tsx
import React from 'react';
import { Download } from 'lucide-react';
import { localAccessLog } from '../logging/localAccessLog';
import { useBypass } from '../auth/bypass/AuthBypassProvider';
export function AccessLogExporter() {
const { bypassActive } = useBypass();
if (!bypassActive) {
return null;
}
const handleExport = () => {
const log = localAccessLog.exportLog();
const blob = new Blob([log], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `bio-qms-access-log-${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
};
const handleClear = () => {
if (confirm('Clear access log? This cannot be undone.')) {
localAccessLog.clearLog();
alert('Access log cleared');
}
};
const log = localAccessLog.getLog();
return (
<div className="access-log-exporter">
<button onClick={handleExport} className="btn-export">
<Download size={16} />
Export Log ({log.length} entries)
</button>
<button onClick={handleClear} className="btn-clear">
Clear Log
</button>
</div>
);
}
7. Configuration
7.1 .env.development
Purpose: Default local development configuration.
# .env.development
# Auth Mode: No authentication required
VITE_AUTH_MODE=none
# Build Info (for watermark)
VITE_BUILD_ENV=development
VITE_BUILD_TIMESTAMP=2026-02-16T10:00:00Z
VITE_GIT_COMMIT=local-dev
# Dev Server
VITE_DEV_SERVER_PORT=5173
VITE_DEV_SERVER_HOST=localhost
# Feature Flags
VITE_ENABLE_DEV_TOOLS=true
VITE_ENABLE_MOCK_USER_SWITCHER=true
VITE_ENABLE_ACCESS_LOG=true
# Logging
VITE_LOG_LEVEL=debug
VITE_LOG_BYPASS_EVENTS=true
Usage:
npm run dev # Automatically loads .env.development
7.2 .env.production
Purpose: Production configuration (bypass DISABLED).
# .env.production
# Auth Mode: GCP authentication REQUIRED
VITE_AUTH_MODE=gcp
# GCP Auth Configuration
VITE_AUTH_ISSUER=https://auth.coditect.ai
VITE_AUTH_AUDIENCE=bio-qms.docs.coditect.ai
VITE_AUTH_JWKS_URI=https://auth.coditect.ai/.well-known/jwks.json
VITE_AUTH_LOGIN_URL=https://auth.coditect.ai/login
# Build Info
VITE_BUILD_ENV=production
VITE_BUILD_TIMESTAMP=${BUILD_TIMESTAMP}
VITE_GIT_COMMIT=${GIT_COMMIT}
# Feature Flags (all dev features DISABLED)
VITE_ENABLE_DEV_TOOLS=false
VITE_ENABLE_MOCK_USER_SWITCHER=false
VITE_ENABLE_ACCESS_LOG=false
# Logging
VITE_LOG_LEVEL=error
VITE_LOG_BYPASS_EVENTS=false
Usage:
npm run build --mode production
7.3 .env.staging
Purpose: Staging environment (bypass DISABLED, test auth).
# .env.staging
# Auth Mode: GCP authentication with staging auth service
VITE_AUTH_MODE=gcp
# Staging Auth Configuration
VITE_AUTH_ISSUER=https://auth-staging.coditect.ai
VITE_AUTH_AUDIENCE=bio-qms-staging.docs.coditect.ai
VITE_AUTH_JWKS_URI=https://auth-staging.coditect.ai/.well-known/jwks.json
VITE_AUTH_LOGIN_URL=https://auth-staging.coditect.ai/login
# Build Info
VITE_BUILD_ENV=staging
VITE_BUILD_TIMESTAMP=${BUILD_TIMESTAMP}
VITE_GIT_COMMIT=${GIT_COMMIT}
# Feature Flags (limited dev features)
VITE_ENABLE_DEV_TOOLS=true
VITE_ENABLE_MOCK_USER_SWITCHER=false
VITE_ENABLE_ACCESS_LOG=true
# Logging
VITE_LOG_LEVEL=info
VITE_LOG_BYPASS_EVENTS=true
7.4 Vite Configuration
Purpose: Environment-specific plugin loading.
// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import { stripBypassCode } from './vite-plugins/stripBypassCode';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const isProduction = mode === 'production';
const authMode = env.VITE_AUTH_MODE;
// Validate production auth
if (isProduction && authMode !== 'gcp') {
throw new Error(
`CRITICAL: Production build requires AUTH_MODE=gcp, found: ${authMode}`
);
}
return {
plugins: [
react(),
tailwindcss(),
// Strip bypass code in production
isProduction && stripBypassCode()
].filter(Boolean),
define: {
// Inject build-time constants
'__BUILD_TIMESTAMP__': JSON.stringify(new Date().toISOString()),
'__GIT_COMMIT__': JSON.stringify(process.env.GIT_COMMIT || 'unknown'),
'__AUTH_MODE__': JSON.stringify(authMode)
},
server: {
port: 5173,
host: 'localhost',
open: true
},
build: {
sourcemap: !isProduction,
minify: isProduction ? 'terser' : false,
terserOptions: {
compress: {
// Remove bypass code
drop_console: isProduction,
drop_debugger: isProduction,
// Remove dead code
dead_code: true,
conditionals: true
}
}
}
};
});
8. Testing with Bypass
8.1 Unit Tests with Bypass Enabled
Strategy: All unit tests run with bypass to avoid auth mocking complexity.
Configuration:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: ['./tests/setup.ts'],
env: {
// Force bypass in tests
VITE_AUTH_MODE: 'none',
VITE_BUILD_ENV: 'test'
}
}
});
Setup File:
// tests/setup.ts
import { vi } from 'vitest';
// Mock auth config to return bypass mode
vi.mock('../src/config/auth.config', () => ({
getAuthConfig: () => ({
mode: 'none',
bypassEnabled: true,
bypassSource: 'env'
})
}));
// Mock window.location
Object.defineProperty(window, 'location', {
value: {
hostname: 'localhost',
href: 'http://localhost:5173',
search: ''
},
writable: true
});
8.2 Integration Tests (Auth-Enabled and Bypassed)
Strategy: Separate test suites for both modes.
Bypass Suite:
// tests/integration/bypass.test.ts
import { render, screen } from '@testing-library/react';
import { describe, it, expect, beforeEach } from 'vitest';
import { Viewer } from '../src/Viewer';
describe('Viewer with Bypass', () => {
beforeEach(() => {
// Set bypass mode
process.env.VITE_AUTH_MODE = 'none';
});
it('should show dev mode indicator', () => {
render(<Viewer />);
expect(screen.getByText(/DEVELOPMENT MODE/i)).toBeInTheDocument();
});
it('should show all documents regardless of NDA', () => {
render(<Viewer />);
// Documents requiring NDA should be visible
expect(screen.getByText(/Confidential Document/i)).toBeInTheDocument();
});
it('should use mock user', () => {
render(<Viewer />);
expect(screen.getByText(/dev@localhost/i)).toBeInTheDocument();
});
});
Auth Suite:
// tests/integration/auth.test.ts
import { render, screen, waitFor } from '@testing-library/react';
import { describe, it, expect, beforeEach } from 'vitest';
import { Viewer } from '../src/Viewer';
describe('Viewer with Auth', () => {
beforeEach(() => {
// Set auth mode
process.env.VITE_AUTH_MODE = 'gcp';
});
it('should redirect to login when unauthenticated', async () => {
render(<Viewer />);
await waitFor(() => {
expect(window.location.href).toContain('/login');
});
});
it('should hide NDA documents when user has not accepted', () => {
// Mock user with NDA pending
localStorage.setItem('bio_qms_auth_token', createMockJWT({
nda_status: 'pending'
}));
render(<Viewer />);
expect(screen.queryByText(/Confidential Document/i)).not.toBeInTheDocument();
});
});
8.3 E2E Tests with Playwright
Bypass Tests:
// tests/e2e/bypass.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Bypass Mode', () => {
test('should activate bypass with URL parameter', async ({ page }) => {
await page.goto('http://localhost:5173/?bypass=local');
// Check for dev mode indicator
await expect(page.locator('.dev-mode-banner')).toBeVisible();
await expect(page.locator('.dev-mode-banner')).toContainText('DEVELOPMENT MODE');
});
test('should show all documents in bypass mode', async ({ page }) => {
await page.goto('http://localhost:5173/?bypass=local');
// Wait for documents to load
await page.waitForSelector('.sidebar');
// Count visible documents
const docs = await page.locator('.document-item').count();
expect(docs).toBeGreaterThan(0);
// Check that NDA-required docs are visible
await expect(page.locator('[data-nda-required="true"]')).toBeVisible();
});
test('should allow mock user switching', async ({ page }) => {
await page.goto('http://localhost:5173/?bypass=local');
// Click mock user switcher
await page.click('.mock-user-button');
// Select different user
await page.click('text=NDA Pending User');
// Page should reload
await page.waitForLoadState('domcontentloaded');
// Check new user is active
await expect(page.locator('.mock-user-button')).toContainText('NDA Pending User');
});
});
8.4 Test Utilities
withBypass() Wrapper:
// tests/utils/withBypass.tsx
import React from 'react';
import { AuthBypassProvider } from '../../src/auth/bypass/AuthBypassProvider';
import { NullAuthProvider } from '../../src/auth/NullAuthProvider';
export function withBypass(component: React.ReactNode) {
const provider = new NullAuthProvider();
return (
<AuthBypassProvider authProvider={provider}>
{component}
</AuthBypassProvider>
);
}
// Usage:
render(withBypass(<Viewer />));
mockAuth() Helper:
// tests/utils/mockAuth.ts
export function mockAuth(options: {
mode: 'bypass' | 'auth';
user?: Partial<User>;
}) {
if (options.mode === 'bypass') {
vi.mock('../../src/config/auth.config', () => ({
getAuthConfig: () => ({
mode: 'none',
bypassEnabled: true,
bypassSource: 'env'
})
}));
} else {
vi.mock('../../src/config/auth.config', () => ({
getAuthConfig: () => ({
mode: 'gcp',
bypassEnabled: false,
bypassSource: null
})
}));
// Mock JWT token
if (options.user) {
localStorage.setItem('bio_qms_auth_token', createMockJWT(options.user));
}
}
}
// Usage:
mockAuth({ mode: 'bypass' });
9. Security Audit Trail
9.1 Log Bypass Activations
Purpose: Record all bypass activations for security review.
Implementation:
// src/logging/bypassAuditLog.ts
interface BypassAuditEntry {
timestamp: string;
source: 'env' | 'url' | 'build';
hostname: string;
url: string;
userAgent: string;
referrer: string;
buildInfo: {
env: string;
commit: string;
timestamp: string;
};
}
export class BypassAuditLog {
private storageKey = 'bio_qms_bypass_audit';
logActivation(config: AuthConfig) {
const entry: BypassAuditEntry = {
timestamp: new Date().toISOString(),
source: config.bypassSource!,
hostname: window.location.hostname,
url: window.location.href,
userAgent: navigator.userAgent,
referrer: document.referrer,
buildInfo: getBuildInfo()
};
// Append to log
const log = this.getLog();
log.push(entry);
// Persist
localStorage.setItem(this.storageKey, JSON.stringify(log));
// Also send to server (if available)
this.sendToServer(entry);
}
private async sendToServer(entry: BypassAuditEntry) {
try {
await fetch('/api/audit/bypass', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entry)
});
} catch (error) {
console.error('Failed to send bypass audit log:', error);
}
}
getLog(): BypassAuditEntry[] {
try {
const stored = localStorage.getItem(this.storageKey);
return stored ? JSON.parse(stored) : [];
} catch {
return [];
}
}
}
export const bypassAuditLog = new BypassAuditLog();
9.2 Production Monitoring
Strategy: Alert if bypass parameter detected in production URLs.
Implementation:
// Cloud Run monitoring alert
resource.type="cloud_run_revision"
severity="CRITICAL"
textPayload=~"bypass parameter detected"
// Alert policy:
// - Notify security team immediately
// - Trigger incident response
// - Log full request details
9.3 Penetration Test Scenarios
Test Case 1: Bypass Circumvention
// Try to activate bypass on production domain
// Expected: Rejected, security alert sent
test('cannot bypass on production domain', async ({ page }) => {
// Mock production domain
await page.goto('https://bio-qms.docs.coditect.ai/?bypass=local');
// Should NOT show dev mode indicator
await expect(page.locator('.dev-mode-banner')).not.toBeVisible();
// Should redirect to login
await expect(page.url()).toContain('/login');
});
Test Case 2: Parameter Injection
// Try to inject bypass parameter via various methods
// Expected: All rejected
test('cannot inject bypass via XSS', async ({ page }) => {
await page.goto('https://bio-qms.docs.coditect.ai/?bypass=<script>alert(1)</script>');
// Should be sanitized, no bypass
await expect(page.locator('.dev-mode-banner')).not.toBeVisible();
});
Test Case 3: Environment Variable Override
// Try to override AUTH_MODE via developer tools
// Expected: Impossible, set at build time
test('cannot override AUTH_MODE at runtime', async ({ page }) => {
await page.goto('https://bio-qms.docs.coditect.ai/');
// Try to modify import.meta.env (read-only)
await page.evaluate(() => {
try {
(import.meta.env as any).VITE_AUTH_MODE = 'none';
} catch (e) {
// Expected: readonly error
}
});
// Should still require auth
await expect(page.url()).toContain('/login');
});
10. Documentation
10.1 Developer Guide
Purpose: How to use bypass mode for local development.
Content:
# Using Bypass Mode for Local Development
## Quick Start
1. **Automatic bypass (recommended):**
```bash
npm run dev # Bypass automatically enabled
- Manual bypass:
# In .env.development:
VITE_AUTH_MODE=none
# Or via URL:
http://localhost:5173/?bypass=local
What Gets Bypassed
- ✅ Authentication required
- ✅ JWT token validation
- ✅ NDA gating
- ✅ Organization scope filtering
- ✅ Rate limiting
- ✅ Network requests to auth.coditect.ai
Visual Indicators
When bypass is active, you'll see:
- 🔴 Red banner at top: "DEVELOPMENT MODE — UNAUTHENTICATED"
- 📋 Console warning every 5 minutes
- 🏷️ Watermark in bottom-right corner
Mock User Switcher
Test different user scenarios:
- Click user dropdown in header
- Select mock user:
- Developer (NDA accepted)
- NDA Pending User
- NDA Declined User
- Research Scientist
- Page reloads with new mock user
Disabling Bypass
To test with real auth:
# Update .env.development:
VITE_AUTH_MODE=gcp
# Or use staging env:
npm run build --mode staging
npm run preview
Troubleshooting
Bypass not activating:
- Check
.env.developmenthasVITE_AUTH_MODE=none - Verify URL is
localhostor127.0.0.1 - Clear browser cache and reload
Still seeing login screen:
- Check for
.env.productionoverride - Verify no
AUTH_MODE=gcpin shell environment
### 10.2 Security Review Documentation
**Purpose:** Explain why bypass is safe in production.
**Content:**
```markdown
# Bypass Mode Security Review
## Safety Guarantees
### 1. Build-Time Validation
- CI/CD pipeline rejects production builds with `AUTH_MODE=none`
- Vite plugin strips bypass code from production bundles
- TypeScript compilation fails if bypass config invalid
### 2. Server-Side Rejection
- Cloud Run middleware rejects `?bypass=` parameter
- CSP header prevents parameter injection
- WAF rules block suspicious bypass attempts
### 3. Hostname Verification
- Bypass only activates on `localhost`, `127.0.0.1`, `[::1]`
- Production domains (`*.coditect.ai`) always rejected
- DNS rebinding attacks prevented via hostname whitelist
### 4. Code Stripping
- `stripBypassCode()` Vite plugin removes bypass functions
- Minification removes dead code branches
- Source maps exclude bypass implementation
## Attack Surface Analysis
| Attack Vector | Mitigation | Residual Risk |
|---------------|------------|---------------|
| Parameter injection | CSP + server-side validation | None |
| Environment override | Build-time constants (readonly) | None |
| DNS rebinding | Hostname whitelist | Low |
| Code inspection | Code stripping + minification | Low |
| XSS token theft | N/A (no tokens in bypass mode) | None |
## Audit Trail
All bypass activations logged:
- Timestamp
- Hostname
- User agent
- Build info (commit, env, timestamp)
Logs retained for 90 days.
## Compliance
- ✅ SOC 2: Bypass disabled in production (verified)
- ✅ HIPAA: Access controls enforced (bypass dev-only)
- ✅ FDA 21 CFR Part 11: Audit trail maintained
10.3 Troubleshooting Guide
Purpose: Common bypass-related issues and solutions.
Content:
# Bypass Mode Troubleshooting
## Issue: Bypass Not Activating
**Symptoms:**
- Dev mode banner not showing
- Still being redirected to login
- URL parameter `?bypass=local` ignored
**Solutions:**
1. **Check environment variable:**
```bash
cat .env.development | grep AUTH_MODE
# Should output: VITE_AUTH_MODE=none
-
Verify hostname:
console.log(window.location.hostname);
// Must be: localhost, 127.0.0.1, or [::1] -
Clear cache:
rm -rf node_modules/.vite
npm run dev -
Check for production override:
# .env.production should NOT be loaded
unset VITE_AUTH_MODE
npm run dev
Issue: Bypass Active in Production
Symptoms:
- Red dev banner visible on production domain
- Console warnings in production
CRITICAL: This should never happen. If it does:
-
Immediate action:
- Rollback deployment immediately
- Notify security team
- Disable production instance
-
Investigation:
- Check build logs for
AUTH_MODE=none - Verify CI/CD validation ran
- Inspect deployed bundle for bypass code
- Check build logs for
-
Prevention:
- Run
./scripts/validate-production-auth.sh - Review
.env.productionconfig - Update CI/CD pipeline validation
- Run
Issue: Mock User Switcher Not Working
Symptoms:
- Mock user dropdown not appearing
- Switching users doesn't change behavior
Solutions:
-
Verify bypass active:
import { useBypass } from './auth/bypass/AuthBypassProvider';
const { bypassActive } = useBypass();
console.log('Bypass active:', bypassActive); -
Check localStorage:
const mockUser = localStorage.getItem('mock_user_key');
console.log('Current mock user:', mockUser); -
Force reload:
localStorage.setItem('mock_user_key', 'developer');
window.location.reload();
Issue: Console Warnings Annoying
Symptom:
- Console warning every 5 minutes is disruptive
Solution:
Warnings are intentional to prevent accidental production deployment.
To reduce frequency:
// src/auth/bypass/consoleWarnings.ts
// Change interval from 5 minutes to 30 minutes
setInterval(() => {
console.warn('⚠️ AUTH BYPASS STILL ACTIVE');
}, 30 * 60 * 1000); // 30 minutes
Or disable entirely (NOT recommended):
// Comment out the interval
// setInterval(() => { ... }, ...);
---
## 11. Implementation Guide
### 11.1 Phase 1: Core Bypass Infrastructure
**Tasks:**
1. Create `src/auth/bypass/` directory
2. Implement `AuthBypassProvider.tsx`
3. Implement `MockUserProvider.tsx`
4. Implement `NoopTokenValidator.ts`
5. Update `src/config/auth.config.ts` with bypass detection
6. Write unit tests for bypass detection logic
**Acceptance Criteria:**
- [ ] `useBypass()` hook returns correct state
- [ ] `getAuthConfig()` detects all three bypass modes
- [ ] `MockUsers` object provides 4+ test users
- [ ] Unit tests pass with 80%+ coverage
### 11.2 Phase 2: Safety Mechanisms
**Tasks:**
1. Implement hostname validation (`hostnameValidator.ts`)
2. Create Vite plugin (`stripBypassCode.ts`)
3. Add build-time validation script (`validate-production-auth.sh`)
4. Create dev mode visual indicators (`DevModeIndicator.tsx`)
5. Add console warnings (`consoleWarnings.ts`)
6. Update CI/CD pipeline with validation step
**Acceptance Criteria:**
- [ ] Bypass rejected on production domains
- [ ] Vite plugin strips bypass code from production bundle
- [ ] CI/CD rejects production builds with `AUTH_MODE=none`
- [ ] Dev mode banner visible when bypass active
- [ ] Console warnings display correctly
### 11.3 Phase 3: Development Features
**Tasks:**
1. Implement mock user switcher (`MockUserSwitcher.tsx`)
2. Add network request interceptor (`networkIndicator.ts`)
3. Create access log exporter (`AccessLogExporter.tsx`)
4. Integrate with presentation mode (A.3)
5. Add feature flag controls (`featureFlags.ts`)
**Acceptance Criteria:**
- [ ] Mock user switcher functional
- [ ] Auth requests show "(bypassed)" in DevTools
- [ ] Access log exports as JSON
- [ ] Presentation mode works with bypass
- [ ] Feature flags toggle correctly
### 11.4 Phase 4: Logging & Audit
**Tasks:**
1. Implement dev logger (`devLogger.ts`)
2. Create local access log (`localAccessLog.ts`)
3. Build bypass audit log (`bypassAuditLog.ts`)
4. Add server-side logging endpoint
5. Configure production monitoring alerts
**Acceptance Criteria:**
- [ ] All access events logged to console
- [ ] Access log persists in localStorage
- [ ] Bypass activations logged with full context
- [ ] Server receives audit logs (if available)
- [ ] Production alerts trigger correctly
### 11.5 Phase 5: Testing & Documentation
**Tasks:**
1. Write unit tests for all bypass components
2. Create integration test suites (bypass + auth)
3. Write E2E tests with Playwright
4. Document developer guide
5. Document security review
6. Create troubleshooting guide
**Acceptance Criteria:**
- [ ] Unit test coverage >80%
- [ ] Integration tests cover both modes
- [ ] E2E tests pass in all scenarios
- [ ] Developer guide complete and accurate
- [ ] Security review approved
- [ ] Troubleshooting guide comprehensive
---
## 12. Troubleshooting
### 12.1 Common Issues
#### Issue: Environment Variable Not Loading
**Cause:** Vite only loads `.env` files matching the current mode.
**Solution:**
```bash
# Check which mode is active
npm run dev # Loads .env.development
npm run build # Loads .env.production
# Force a specific mode
npm run dev --mode staging # Loads .env.staging
Issue: Bypass Parameter Ignored
Cause: Hostname is not recognized as localhost.
Debug:
console.log('Hostname:', window.location.hostname);
console.log('Is localhost?', isValidLocalhost());
Solution:
Add your hostname to ALLOWED_HOSTNAMES in hostnameValidator.ts.
Issue: Vite Plugin Not Stripping Code
Cause: Plugin only runs in production mode.
Verify:
# Build production bundle
npm run build --mode production
# Inspect bundle
cat dist/assets/index-*.js | grep "isLocalhostBypass"
# Should return no results (code stripped)
Issue: CI/CD Validation Failing
Cause: .env.production not found or invalid.
Debug:
# Run validation locally
./scripts/validate-production-auth.sh .env.production
# Check file exists
ls -la .env.production
# Check contents
cat .env.production | grep VITE_AUTH_MODE
12.2 Debug Commands
Check Auth Config:
import { getAuthConfig } from './config/auth.config';
console.table(getAuthConfig());
Check Bypass State:
import { useBypass } from './auth/bypass/AuthBypassProvider';
const { bypassActive, bypassSource, mockUser } = useBypass();
console.log({ bypassActive, bypassSource, mockUser });
Check Feature Flags:
import { getFeatureFlags } from './config/featureFlags';
console.table(getFeatureFlags());
Inspect Access Log:
import { localAccessLog } from './logging/localAccessLog';
console.table(localAccessLog.getLog());
Document Status
Version: 1.0.0 Status: Active Last Updated: 2026-02-16 Next Review: 2026-03-16
Change Log:
| Date | Version | Changes | Author |
|---|---|---|---|
| 2026-02-16 | 1.0.0 | Initial comprehensive specification | frontend-react-typescript-expert |
Related Documents:
auth-mode-switching.md— Environment-based auth mode switching (GCP JWT)TRACK-A-PRESENTATION-PUBLISHING.md— Parent track (A.4.4 requirements)cloud-run-deployment.md— Production deployment configuration
Implementation Files:
| File | Description |
|---|---|
src/auth/bypass/AuthBypassProvider.tsx | Main bypass provider wrapper |
src/auth/bypass/MockUserProvider.tsx | Mock user context |
src/auth/bypass/NoopTokenValidator.ts | No-op token validator |
src/auth/bypass/hostnameValidator.ts | Hostname verification |
src/auth/bypass/buildDetector.ts | Dev server detection |
src/components/DevModeIndicator.tsx | Visual dev mode banner |
src/components/MockUserSwitcher.tsx | Mock user dropdown |
src/logging/devLogger.ts | Console logging |
src/logging/localAccessLog.ts | localStorage access log |
src/logging/bypassAuditLog.ts | Bypass audit trail |
vite-plugins/stripBypassCode.ts | Production code stripping |
scripts/validate-production-auth.sh | Build-time validation |
End of Document