GUC Swap

Full-stack group and tutorial swapping platform built specifically for German University in Cairo (GUC) students, featuring a graph-based multi-way cycle detection engine, immutable identity-bound permanent ban enforcement, device-bound session security, Transport Payload Obfuscation, and a 3-tier email failover chain.

SecurityHonoTypeScriptPostgreSQLRedisReact
Visit Website
GitHub
Aug 31, 2026
GUC Swap

Graph-Based Multi-Way Swap Cycle Detection

THE PROBLEM

Group and tutorial swapping is not always a simple A↔B exchange. Student A might want Group 5, Student B might want Group 8, and Student C might want Group 2, forming a cycle where A gives to B, B gives to C, and C gives to A. Finding these multi-way swap cycles in a pool of hundreds of students is a graph theory problem that requires efficient cycle detection without exponential blowup.

WHAT I DID

Implemented a DFS-based cycle detection algorithm that models the swap pool as a directed graph. Each student is a node, and a directed edge exists from student i to student j if student i's desired group/tutorial matches student j's current group/tutorial (or if either side has "any" as their preference). The algorithm starts from the current user (node 0) and performs depth-limited DFS (max depth 5) to find cycles that return to node 0 through 2+ intermediate students. Results are grouped by the spot the user would receive, organized by cycle depth (depth_3, depth_4, etc.), and capped at 15 cycles per depth to prevent output explosion. The algorithm includes an iteration counter (MAX_ITER = 10,000) to prevent infinite loops on large graphs.

WHY THIS APPROACH

The graph-based approach correctly models the swap problem as cycle detection in a directed graph, which is the mathematically precise formulation. DFS with depth limiting provides a good balance between finding long chains and keeping computation bounded. The "any" preference handling (matching any group/tutorial) is integrated into the edge creation, so students with flexible preferences naturally become well-connected nodes in the graph. The MAX_ITER guard prevents CPU exhaustion on Workers, where execution time is bounded. The alternative of BFS would find shortest cycles first but would use more memory. Brute-force enumeration of all permutations would be O(n!) and infeasible for pools of 100+ students.

THE IMPACT

Multi-way swap cycles of up to 5 students are discovered efficiently. Results are organized by the swap the user would receive and by cycle length, making it easy to evaluate options. The algorithm handles "any" preferences, empty preferences, and large student pools without timeout. CPU usage is bounded by the iteration counter.

Multi-Dimensional Permanent Ban & Identity Binding System

THE PROBLEM

A platform built for the GUC student community must be able to permanently exclude abusive actors. On typical platforms, users bypass bans by deleting their accounts and re-registering under different names or phone numbers. The system required an uncircumventable identity architecture where bans are strictly permanent and backed by verified proof of misuse.

WHAT I DID

Architected an immutable identity mapping and multi-vector permanent ban system: • Immutable 10-Digit Identity Binding: Every user is permanently assigned a unique 10-digit identifier bound directly to their verified institutional email in an isolated identity mapping table. Even if a user deletes their account and re-registers with a different name, new phone number, or altered details, the system deterministically re-assigns them the exact same 10-digit ID. • Multi-Dimensional Ban Checks: Enforces bans across email, phone number, and WhatsApp number simultaneously. Bans are applied against the identity record based on user report tickets accompanied by verified evidence of platform misuse. • Multi-Checkpoint Enforcement: Ban verification executes at registration (pre-creation), login (post-verification), and password reset.

WHY THIS APPROACH

GUC students are issued only one official GUC email address by the university, meaning there are no secondary email alternatives. Binding the permanent 10-digit ID to the institutional email ensures that deleting and recreating an account will always yield the exact same identity footprint, once banned based on verified abuse reports, there is strictly no way back into the platform. Checking email, phone, and WhatsApp simultaneously closes all alternative bypass vectors.

THE IMPACT

Account deletion and re-registration bypasses are completely eliminated through immutable 10-digit identity mapping. Banned abusive actors are permanently excluded across email, phone, and WhatsApp with zero path to return.

Device-Bound Session Validation via SHA-256 Fingerprinting

THE PROBLEM

Session hijacking is a critical threat for any authentication system. If an attacker obtains a valid session cookie (via XSS, network interception, or social engineering), they can impersonate the user from any device. Standard cookie-based sessions provide no mechanism to detect that the session is being used from a different browser or machine than the one that originally authenticated.

WHAT I DID

Implemented a device fingerprinting system that binds each session to a specific device. On first visit, the server generates a secure random secret key and stores it as a persistent, httpOnly, secure, SameSite=Lax cookie. The fingerprint is computed as secure hash of device properties and the secure cookie. This fingerprint is stored in the user profile table database row alongside the session ID. On every authenticated request (dashboard, profile, settings, account deletion), the server recomputes the fingerprint from the incoming request headers and the device fingerprint cookie, and compares it to the stored fingerprint. If they do not match, meaning the request is coming from a different browser/device, the session is immediately invalidated: cookies are deleted and a 401 is returned.

WHY THIS APPROACH

The device fingerprint cookie is the binding factor. Without it, an attacker who steals the session ID cannot produce a matching fingerprint because the device fingerprint cookie is httpOnly (not accessible via JavaScript) and tied to the original device. The three-component fingerprint (UA + Lang + secure device cookie) ensures that changing any one factor, using a different browser, different language settings, or a different device, breaks the fingerprint. The alternative of IP-based binding would fail for mobile users whose IP changes frequently, and would break on NAT/CGNAT networks common in Egyptian ISPs. Device fingerprinting via this secure device cookie provides security without usability friction.

THE IMPACT

Stolen session tokens are useless from a different device. Session hijacking attacks are neutralized at the application layer. Failed fingerprint checks result in immediate session termination with no user-visible error, the attacker simply gets a 401.

Transport Payload Obfuscation on All API Payloads

THE PROBLEM

While HTTPS already secures data in transit perfectly well, plaintext JSON is still trivially visible to anyone opening browser DevTools or casual network inspection tools. I wanted to add a friction layer against casual debugging and automated scraping, making it slightly more annoying to reverse-engineer the API without claiming to add actual cryptographic security.

WHAT I DID

Implemented a symmetric payload obfuscation layer that wraps all API request and response bodies. The frontend monkey-patches the global window.fetch to intercept requests to api.gucswap.com, outgoing JSON bodies are obfuscated with a shared key and sent as application/octet-stream. The backend middleware mirrors this: it deobfuscates incoming octet-stream payloads before route handlers process them, and obfuscates all JSON responses back to octet-stream before sending.

WHY THIS APPROACH

Symmetric obfuscation provides a lightweight friction layer that adds minimal CPU overhead, critical on Workers where every millisecond of CPU time counts. It is explicitly not a security feature, but rather a deterrent against casual inspection in DevTools or automated scripts. A heavy encryption standard like AES-GCM would have been disproportionate and slow for a feature that solely exists to add debugging friction. This approach achieves the goal of making payloads opaque with zero additional dependencies and near-zero latency.

THE IMPACT

Browser DevTools network tab shows binary data instead of JSON, successfully adding a friction layer against casual debugging and scraping. Payloads remain opaque to casual inspection while maintaining near-zero performance overhead. Zero additional dependencies.

Dual-Layer Rate Limiting with Redis and In-Memory Fallback

THE PROBLEM

API endpoints handling authentication (login, register, password reset) are prime targets for brute-force attacks, credential stuffing, and denial-of-service. Without rate limiting, an attacker can attempt thousands of login passwords per minute, or flood the registration endpoint with fake accounts. Rate limiting must work even if Redis is temporarily unavailable.

WHAT I DID

Implemented a flexible rate limiting system supporting multi-window configurations (e.g., "rate limiting via minute and hour and day") parsed from human-readable strings. Each route has its own configurable limit, login allows rate limiting via minute and hour and day, register allows per minute with a shared hourly and daily window across register and forget-password, dashboard allows per minute and per hour limits. When Redis is available, limits use Redis INCR with EXPIRE for distributed counting. When Redis is unavailable, the system falls back to an in-memory Map with automatic TTL-based expiry and a sweep mechanism that cleans up when the store exceeds a threshold limit. IP identification uses CF-Connecting-IP (Cloudflare header) with X-Real-IP as fallback.

WHY THIS APPROACH

Multi-window rate limiting (per-minute AND per-hour AND per-day) prevents both short-term brute-force bursts and long-term credential stuffing campaigns. The string-based configuration ("rate limiting via minute and hour and day") makes it trivial to tune limits per route without code changes. The Redis/in-memory dual-layer ensures rate limiting never becomes a single point of failure, if Redis goes down, the in-memory fallback continues protecting endpoints. The shared window between register and forget-password ("shared:register_forget") prevents an attacker from distributing abuse across both endpoints.

THE IMPACT

Login brute-force attempts are limited to configurable requests per minute, hour, and day per IP. Registration and password reset share a combined limit of hourly and daily limits. Rate limiting is always operational even during Redis outages. Per-route configuration allows security-tight limits on auth endpoints while being more permissive on read-only endpoints.

Comprehensive Security Header Suite

THE PROBLEM

Web applications are vulnerable to clickjacking, MIME sniffing, cross-origin attacks, and information disclosure if proper security headers are not set. Each header addresses a specific class of attack, and missing any one of them creates an exploitable gap.

WHAT I DID

Applied a comprehensive set of security headers to every API response via a Hono middleware: • HSTS (max-age=31536000; includeSubDomains; preload) enforces HTTPS. • X-Content-Type-Options: nosniff prevents MIME type sniffing. • X-Frame-Options: DENY prevents clickjacking. • X-XSS-Protection: 1; mode=block enables the legacy XSS filter. • Cross-Origin-Opener-Policy: same-origin prevents cross-origin window references. • Cross-Origin-Resource-Policy: same-origin prevents cross-origin resource loading. • Origin-Agent-Cluster: ?1 requests process isolation. • Content-Security-Policy is configured specifically for HTML and API responses (default-src none; frame-ancestors none). • Referrer-Policy: strict-origin-when-cross-origin limits referrer leakage. • Permissions-Policy disables geolocation, microphone, and camera. • X-Powered-By, Server, and X-AspNet-Version headers are explicitly removed.

WHY THIS APPROACH

Each header addresses a specific attack class: HSTS prevents SSL stripping attacks. nosniff prevents browsers from interpreting uploaded files as executable. DENY framing prevents clickjacking via iframes. COOP/CORP prevent cross-origin side-channel attacks (Spectre variants). CSP restricts which resources the browser can load, mitigating XSS impact even if an injection occurs. Removing server identification headers prevents fingerprinting. The different CSP policies for HTML vs API responses reflect the different trust models, HTML pages need font and script sources, while API responses should have no resource loading at all.

THE IMPACT

Every response carries a full complement of modern security headers. Clickjacking, MIME sniffing, cross-origin attacks, and information disclosure are all mitigated. Server technology is not revealed to attackers. CSP provides a last line of defense against XSS even if input validation fails.

Multi-Layer Input Validation and Sanitization

THE PROBLEM

User input is the primary attack surface for injection attacks (XSS, SQL injection), data corruption, and application errors. A platform collecting GUC student names, institutional emails, phone numbers, and academic data must validate every field against strict schemas while handling edge cases like zero-width characters, leet-speak bypasses, and HTML injection attempts.

WHAT I DID

Implemented a comprehensive validation and sanitization pipeline: (1) Strict field allowlisting via checkStrict - every request body is checked against a defined set of allowed keys; unexpected fields cause immediate rejection. (2) Zero-width character detection - any string containing Unicode zero-width characters (U+200B-U+200D, U+FEFF) is rejected, preventing invisible character injection. (3) Type and length validation - every field has explicit min/max length constraints and type checks. (4) GUC email regex enforcement - only @student.guc.edu.eg emails are accepted for registration. (5) Phone number validation against a comprehensive regex covering 60+ country codes. (6) Name format validation requiring 2+ words of 2+ characters each, with profanity checking. (7) HTML stripping via a custom stripAllTags function that removes scripts, styles, and all HTML tags, plus htmlUnescape for decoding entities. (8) The xss library provides an additional sanitization layer. (9) Password complexity: 12+ chars, uppercase, lowercase, digit, special character.

WHY THIS APPROACH

Defense in depth is the principle - no single validation layer is trusted alone. The strict field allowlisting prevents mass assignment attacks. Zero-width character detection prevents a class of attacks where malicious content is hidden in visually empty strings. The multi-step sanitization (strip tags → html unescape → profanity check → format validation) ensures that each layer handles what it's best at, rather than relying on a single regex to do everything. Parameterized database queries provide SQL injection protection at the database layer, but the application-level validation prevents corrupted data from ever reaching the database.

THE IMPACT

Every input field is validated against strict schemas before processing. XSS payloads are stripped at multiple layers. Zero-width character injection is blocked. Only valid GUC emails and properly formatted phone numbers are accepted. Profanity in names is detected via both regex and leet-speak-aware wordlist matching.

Leet-Speak-Aware Profanity Filtering with N-Gram Matching

THE PROBLEM

A campus community platform built for GUC students requires clean, professional user profiles. Simple profanity filters are trivially bypassed by substituting characters (e.g., "a" → "@", "i" → "1", "e" → "3") or inserting spaces between letters. The filter must catch both standard profanity and creative bypass attempts without blocking legitimate names or content.

WHAT I DID

Built a profanity detection system with four layers: (1) A 5,000+ word wordlist covering English, Slang, and Franko profanities, with all terms pre-hashed in client-side bundles so no raw offensive words exist in frontend code. (2) Leet-speak normalization that maps special characters to their letter equivalents (@→a, 4→a, 8→b, 3→e, etc.) before matching, defeating character substitution attacks. (3) Repeated character collapse, sequences of 3+ identical characters are reduced to 2 (e.g., "stuuupid" → "stupid"), defeating character repetition bypasses. (4) N-gram matching checks both individual tokens and multi-word combinations (up to the longest word in the wordlist) against the normalized token set, catching phrases that span multiple words.

WHY THIS APPROACH

The combination of leet normalization, character collapse, and n-gram matching makes the filter robust against common bypass techniques. Franko words are particularly important for detecting transliterated terms. Pre-hashing wordlist terms in the frontend ensures the client code stays clean without storing plaintext offensive words in production bundles. The wordlist is checked against both raw and fully-collapsed forms of each token, while the n-gram approach catches multi-word terms.

THE IMPACT

English, Slang, and Franko profanities in user names and content are detected despite leet-speak, character repetition, and multi-word bypass attempts, while keeping client-side code completely free of raw offensive words. The filter operates at registration time and profile update time, keeping the platform professional.

SHA-256 Hashed Token Management with Automatic Invalidation

THE PROBLEM

Verification tokens, 2FA tokens, and password reset tokens are high-value targets. If stored in plaintext in a database or cache, a compromise of that storage immediately exposes all active tokens. Tokens must also be single-use, reusing a verification or reset token should fail. Additionally, issuing a new token for the same purpose (e.g., requesting another verification email) should invalidate the previous token.

WHAT I DID

All tokens are cryptographically secure URL-safe random strings generated via crypto.getRandomValues categorized by their specific authentication purpose. The raw token is sent to the user via email; only the secure hash is stored in Redis with a 10-minute (600-second) TTL. Token storage uses a dual-key scheme: token:{token_hash} stores the payload, and token_mapping:{identifier} maps to the current hash. When a new token is issued for the same type and email, the old token_map entry is looked up, the old token:{token_hash} is deleted, and the new token is stored, ensuring only one active token per type per email. Token consumption uses Redis GETDEL (atomic get-and-delete), preventing replay. The deleteAllTokensForEmail function clears all token types for a user on password change, invalidating any pending verification, 2FA, or reset tokens.

WHY THIS APPROACH

Hashing tokens before storage means that even if Redis is compromised, the attacker cannot forge valid tokens, they would need to reverse a secure hash. The 10-minute TTL ensures tokens expire quickly, limiting the window for interception. GETDEL provides atomicity, a token cannot be consumed twice even under concurrent requests. The dual-key scheme (token + token_map) enables efficient invalidation when a new token is issued, without scanning all keys. The alternative of storing tokens in the database would add latency and database load for a high-churn, short-lived data type that Redis handles optimally.

THE IMPACT

Tokens are never stored in plaintext. Single-use enforcement prevents replay attacks. New token issuance automatically invalidates previous tokens. 10-minute TTL limits exposure window. Password change invalidates all pending tokens across all types.

Redis-Cached Session Management with SHA-256 Token Hashing

THE PROBLEM

Session management must balance security (preventing token theft and replay) with performance (avoiding a database query on every authenticated request). Storing session IDs in plaintext in the database creates a risk if the database is compromised, an attacker could use any session token directly. Validating against the database on every request adds latency and database load.

WHAT I DID

Sessions are managed through a three-layer architecture: (1) The session ID is a secure random token stored in an httpOnly, secure, SameSite=Lax cookie with a 24-hour TTL. (2) The raw token is never stored, only its secure hash is persisted in the user session column and used as the Redis cache key (session:{session_hash}). (3) Redis caches the full user profile data for 24 hours (86400 seconds), so authenticated requests resolve from cache in ~5ms instead of querying the primary database. On logout or password change, both the Redis key and the database session ID column are cleared. Session invalidation also occurs on device fingerprint mismatch.

WHY THIS APPROACH

Hashing the session token before storage means that even if the database is compromised, the attacker cannot forge valid sessions, they would need to reverse a secure hash of a 256-bit random value. Redis caching eliminates the need for a database round-trip on every request while the 24-hour TTL ensures eventual expiration. The alternative of JWT-based stateless sessions would prevent server-side revocation (critical for logout, password change, and device fingerprint checks). The stateful approach was chosen specifically because the application needs the ability to invalidate sessions proactively.

THE IMPACT

Session tokens are never stored in plaintext. Database compromise does not lead to session hijacking. Authenticated requests resolve from Redis with sub-5ms latency. Server-side session revocation works instantly for logout, password change, and device mismatch. 24-hour session lifetime with immediate invalidation capability.

PBKDF2 Password Hashing with SHA-256 Pre-Hash Layer

THE PROBLEM

Password storage is a fundamental security concern. Storing plaintext passwords exposes every user to mass credential theft if the database is compromised. Simple hashing (MD5, SHA-256 without salt) is vulnerable to rainbow table attacks.

WHAT I DID

Implemented a two-layer password hashing scheme: first, a secure pre-hash using the Web Crypto API's subtle.digest, then PBKDF2 with a high iteration count, a secure hashing algorithm, and 64-byte output. The salt is a strong, unique secure salt variable stored in Worker secrets. The pre-hash step normalizes the input before PBKDF2, and the PBKDF2 step provides the computational hardness that makes brute-force attacks impractical. Password complexity is enforced at registration: minimum 12 characters, at least one uppercase, one lowercase, one digit, and one special character.

WHY THIS APPROACH

The secure pre-hash prevents potential length-extension issues and ensures the PBKDF2 input is a fixed-size, high-entropy buffer. A high iteration count of secure key derivation with a 64-byte output provides strong computational hardness. PBKDF2 is natively supported, well-studied, and NIST-approved.

THE IMPACT

Passwords are stored as secure hashes derived from secure key derivation with 100K iterations. Brute-force attacks against leaked hashes are computationally infeasible. Password complexity requirements are enforced server-side, not just client-side.

Email-Based Two-Factor Authentication with Magic Links

THE PROBLEM

Password-only authentication is vulnerable to credential stuffing, phishing, and password reuse attacks. A second factor is needed, but hardware tokens and authenticator apps add friction for a GUC student population. The 2FA mechanism must be both secure and accessible.

WHAT I DID

Implemented email-based 2FA using magic links. When a user with 2FA enabled attempts to login, after password verification the system generates a secure random token, stores its secure hash in Redis with a 10-minute TTL, and sends a verification link to the user's email. The login API returns a special status indicator, and the frontend navigates to the 2FA verification page. Clicking the link (GET /login/2fa-verify/:token) validates the token against Redis (atomic GETDEL), and if valid, issues a full session. The token is single-use and expires in 10 minutes.

WHY THIS APPROACH

Email-based 2FA is the most accessible second factor for a GUC student population, every student has an email account, and no additional app installation is required. The magic link approach (rather than a 6-digit code) eliminates the need for the user to copy/paste codes, reduces entry errors, and completely avoids the brute-force risks inherent to short numeric codes. The 10-minute TTL and single-use enforcement (GETDEL) provide strong security guarantees. The alternative of TOTP (Google Authenticator) would require students to install and configure an additional app, reducing adoption. SMS-based 2FA would require phone number collection and is vulnerable to SIM swapping.

THE IMPACT

Users with 2FA enabled receive a magic link via email after password verification. The link is single-use, time-limited (10 minutes), and hashed before storage. Account takeover requires both the password and access to the email account. The 2FA status is checked on every login attempt, not just the first.

Cloudflare Turnstile CAPTCHA Integration

THE PROBLEM

Automated bots can abuse registration, login, password reset, and contact forms even with rate limiting in place. Traditional CAPTCHAs (reCAPTCHA) degrade user experience with visual puzzles and are blocked in some regions. The platform needs bot mitigation that is non-interactive for legitimate users but effective against automated abuse.

WHAT I DID

Integrated Cloudflare Turnstile as a CAPTCHA guard on all state-changing endpoints: registration, login, forget-password, contact, support, and report. The Turnstile widget renders on the frontend (with a responsive wrapper that scales the 300px widget to fit smaller containers), produces a token sent in the request body, and the backend validates it against Cloudflare's siteverify API. If the token is missing or invalid, the request is rejected with a client error status.

WHY THIS APPROACH

Turnstile is non-interactive for legitimate users (managed/challenge mode) while providing robust bot detection via Cloudflare's network intelligence. Unlike reCAPTCHA v2, it does not present visual puzzles. Unlike hCaptcha, it leverages Cloudflare's existing infrastructure which is already in the request path. Passing the token directly in the request body keeps submission and authentication payloads clean, self-contained, and decoupled.

THE IMPACT

All state-changing endpoints are protected against automated abuse. Legitimate users experience zero friction - Turnstile operates non-interactively. Bot-driven registration spam, credential stuffing, and form flooding are mitigated at the edge before reaching application logic.

3-Tier Email Provider Failover Chain

THE PROBLEM

Email delivery is critical for account verification, 2FA magic links, and password resets. If the email provider goes down or rate-limits the application, users cannot verify their accounts or complete logins. Single-provider architectures create a single point of failure for the entire authentication flow.

WHAT I DID

Implemented a 3-tier email delivery chain: (1) Primary Email API - for fast, high-deliverability transactional messaging. (2) Secondary Backup Email API - an automated fallback service if the primary experiences outages or rate limits. (3) Direct SMTP via Sockets (tertiary) - a direct socket connection using the GUC Swap API, implementing STARTTLS, AUTH PLAIN, AUTH LOGIN, and AUTH CRAM-MD5 authentication methods natively on the backend. The email delivery core tries each provider in sequence - if the primary API fails (HTTP error, timeout, network issue), it cascades to the secondary provider, and if that fails, it connects directly to a fallback SMTP server via a raw TCP socket. Contact, support, and user report emails use a separate isolated dispatch path sent via an internal network for operational separation.

WHY THIS APPROACH

Email is the backbone of the authentication system - verification, 2FA, and password reset all depend on delivery. A 3-tier chain ensures that even if two providers fail simultaneously, the SMTP fallback provides a direct path to recipient mail servers. Implementing the full SMTP protocol (EHLO, STARTTLS, AUTH, MAIL FROM, RCPT TO, DATA) directly over sockets ensures the GUC Swap API can dispatch email independently without relying on third-party SaaS APIs. The retry-at-application-level approach across distinct infrastructure tiers is far more reliable than retrying a failing provider.

THE IMPACT

Email delivery resilience across three independent tiers. Account verification and 2FA work reliably even during partial provider outages. The native SMTP socket fallback eliminates complete dependency on external transactional email APIs. Contact, support, and report emails are routed through a separate isolated channel sent via an internal network.

GUC Academic Structure Validation

THE PROBLEM

A platform specific to the German University in Cairo must enforce that users register with valid faculty, major, semester, and group combinations. Invalid combinations (e.g., a "Dentistry" major in "Engineering" faculty, or semester 9 in a 8-semester faculty) would corrupt the matching algorithm and create invalid swap candidates.

WHAT I DID

Encoded the complete GUC academic structure as a constants object mapping 6 faculties to their valid majors: Engineering (15 majors including CSEN, MET, Networks, etc.), Pharmacy & Biotechnology (4 majors), Management Technology (3 majors), Applied Sciences & Arts (3 majors), Dentistry (1 major), and Law & Legal Studies (1 major). Registration validation checks that the selected faculty exists in the structure and that the selected major is a valid child of that faculty. Semester validation enforces that 10-semester faculties (Dentistry, Pharmacy & Biotechnology, Engineering) allow semesters 1-10, while 8-semester faculties allow only 1-8. The same validation runs on profile updates to prevent users from editing their way into invalid states.

WHY THIS APPROACH

Server-side validation of the GUC structure ensures that the matching algorithm only processes valid combinations. Client-side validation in catalogs.ts provides the same data for immediate feedback, but the server-side validation is authoritative. The nested structure (faculty → majors) prevents invalid cross-faculty combinations. The semester limits prevent students in shorter programs from selecting semesters that don't exist in their curriculum. The alternative of free-text input would allow any combination, corrupting the swap matching.

THE IMPACT

Only valid GUC faculty-major-semester combinations are accepted at registration and profile update. The swap matching algorithm operates on clean, validated data. Invalid combinations are caught with specific error messages at the validation layer.

Structured Error Handling and Information Disclosure Prevention

THE PROBLEM

Unhandled exceptions can leak stack traces, database errors, or internal service details to clients. Rate limit errors must be distinguishable from other errors by the frontend for proper UI handling. Validation errors must return structured data without revealing internal state.

WHAT I DID

Implemented a centralized error handler that catches unhandled exceptions and maps them to exceptional HTTP status codes for every state (appropriate 4xx client error codes and generic 5xx server error codes without stack traces or internal details). Handlers differentiate between invalid request schemas, rate limiting, missing endpoints, and method mismatches. To optimize backend efficiency, strict preliminary validations reject malformed requests early before reaching the database to prevent unnecessary queries and save CPU exhaustion, paired with an intentional calibration delay on early failures to adjust execution time and avoid timing attacks between requests that hit the database and those that do not.

WHY THIS APPROACH

The centralized error handler ensures that no raw exception ever reaches the client. Each error category is mapped to structured 4xx/5xx status codes, allowing the frontend to handle each scenario specifically while keeping internal architecture details completely concealed. Performing validation prior to database interaction prevents resource starvation and CPU exhaustion. The added calibration delay equalizes response latency between early validation rejections and full database queries to avoid timing attacks and user enumeration based on response latency.

THE IMPACT

No stack traces, database errors, or internal details are ever sent to clients. Granular 4xx status codes enable precise client handling while generic 5xx responses protect internal topology. Response times are balanced to avoid timing attacks between database-hitting requests and fast validation exits.

React 19 Frontend Architecture with Lazy Loading and Route Guards

THE PROBLEM

A feature-rich SPA with 20+ pages (landing, auth flows, dashboard, profile, settings, demo mode, legal pages) can suffer from slow initial load times if all components are bundled together. Additionally, protected routes must redirect unauthenticated users to login, and authenticated users away from login/register pages, without flickering or layout shifts.

WHAT I DID

Built the frontend with React 19, TypeScript, Vite, and React Router DOM v7. All page components are lazy-loaded via React.lazy() with a consistent RouteFallback spinner, reducing the initial bundle to the landing page and shared providers. RouteGuards provides two guard components: ProtectedRoute (redirects to /login with the current path in state if unauthenticated) and PublicRoute (redirects to /dashboard if authenticated). Both guards show a full-screen loading spinner during the initial auth check, preventing layout shifts. The AuthContext uses a bootstrap promise pattern to prevent multiple concurrent session checks. The App component wraps everything in HelmetProvider (SEO), ThemeProvider (dark/light mode via next-themes), MotionConfig (respects prefers-reduced-motion), ReactLenis (smooth scrolling), and a backdrop gradient for visual polish.

WHY THIS APPROACH

Lazy loading reduces the initial JavaScript payload from ~20 pages worth of code to just the landing page and shared components. Route guards prevent unauthorized access without requiring each page to implement its own auth check, the guard wraps the route tree and handles redirection centrally. The bootstrap promise pattern in AuthContext prevents the "flash of unauthenticated content" that occurs when multiple components independently trigger session checks on mount. The demo route (public, no auth required) allows showcasing the platform without creating an account, which is valuable for marketing and portfolio presentation.

THE IMPACT

Initial page load contains only the landing page code. Authenticated/unauthenticated routing is handled centrally with no per-page boilerplate. The auth check runs once on mount, not per-component. Demo mode showcases the platform without requiring authentication. Dark/light theme, smooth scrolling, and reduced-motion support are applied globally.

Frontend Auth Session Hygiene and Wizard State Cleanup

THE PROBLEM

Multi-step authentication flows (register → verify, login → 2FA, forget password → reset) must balance UX resilience with state hygiene. Storing wizard state solely in component memory causes accidental page reloads or refreshes to reset the user's progress back to step 1 (creating a frustrating user experience). Conversely, unbounded sessionStorage persistence risks leaking stale form data, emails, and step counters across flows.

WHAT I DID

Implemented the AuthSessionManager component that preserves active wizard progress against accidental reloads while enforcing strict session hygiene. It maintains a whitelist of paths where auth wizard state is allowed (/login, /register, /forget-password, and certain footer pages with a fromAuth flag). On every route change, if the current path leaves the auth flow, all auth-related wizard state in sessionStorage is cleared (form data, step counters, temporary email states, and cooldown timers). When navigating within auth pages, irrelevant keys are cleared per-page (e.g., navigating to /login clears register-specific keys). The AuthContext provides a bootstrap promise pattern that prevents concurrent session check requests during initial page load.

WHY THIS APPROACH

Persisting wizard state in sessionStorage prevents accidental page reloads or temporary detours from wiping the user's progress, avoiding a disruptive UX. Meanwhile, deterministic per-route cleanup addresses the security risk of stale auth state persisting after the user leaves the flow. The fromAuth flag and footer detour tracking handle edge cases where a user navigates to a legal page (privacy, terms) during an auth flow and then returns without losing their place.

THE IMPACT

Accidental page refreshes during multi-step auth wizards no longer reset user progress, ensuring a smooth user experience. Wizard state is automatically purged the moment the user navigates away from auth flows. Multi-step auth flows work seamlessly even when users navigate to footer pages and back.

Frontend Profile Caching with Invalidation

THE PROBLEM

Multiple components on the dashboard, profile, and settings pages need access to the user's profile data. Fetching the profile from the API on every component mount would create unnecessary network requests and increase page load time, especially on slow connections.

WHAT I DID

Implemented a client-side profile cache using a module-level variable (profileCache) and a deduplication promise (profilePromise). The fetchCachedProfile function returns the cached data immediately if available, or shares a single in-flight fetch promise if a request is already pending. This ensures that even if 5 components call fetchCachedProfile simultaneously, only one HTTP request is made. The cache is invalidated (set to null) on logout, on unauthorized responses (via handleUnauthorized), and on login, ensuring the next fetch retrieves fresh data. The cache lives for the duration of the page session (module scope), providing a natural expiration when the user refreshes the page.

WHY THIS APPROACH

The promise-sharing pattern eliminates duplicate requests without requiring a state management library (Redux, Zustand). The module-level scope provides natural session lifetime, the cache clears when the page refreshes. Invalidation on logout and unauthorized ensures stale data is never served after a session ends. The alternative of using React Context for profile data would require wrapping components in providers and would not naturally handle the deduplication case. The alternative of React Query / SWR would add a dependency for a simple use case.

THE IMPACT

Multiple components share a single profile fetch. Duplicate API calls are eliminated. Cache invalidation on auth state changes ensures data freshness. Zero additional dependencies for profile data management.