<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: siddarthpatelkama </title>
    <description>The latest articles on DEV Community by siddarthpatelkama  (@siddarthpatelkama).</description>
    <link>https://dev.to/siddarthpatelkama</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4003834%2F5b7c10be-984c-41f4-9e6e-5705458fce67.jpg</url>
      <title>DEV Community: siddarthpatelkama </title>
      <link>https://dev.to/siddarthpatelkama</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siddarthpatelkama"/>
    <language>en</language>
    <item>
      <title>The QR Code Wasn't Enough: How I Added Device-Bound Authentication</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Sun, 09 Aug 2026 05:34:45 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/the-qr-code-wasnt-enough-how-i-added-device-bound-authentication-41mo</link>
      <guid>https://dev.to/siddarthpatelkama/the-qr-code-wasnt-enough-how-i-added-device-bound-authentication-41mo</guid>
      <description>&lt;p&gt;&lt;em&gt;How I added another layer of verification to make account sharing and proxy attendance harder.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In my previous article, I explained how I used dynamic QR authentication to reduce one of the simplest forms of proxy attendance:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;screenshot and share the QR code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But that introduced another question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What if the student shares their account instead?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Consider this scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Classroom
Student A
   │
   └── Has valid account + trusted device

Hostel
Student B
   │
   └── Gets Student A's credentials
   │
   └── Logs in from another phone
   │
   └── Attempts attendance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The QR code can be valid.&lt;/p&gt;

&lt;p&gt;The credentials can be valid.&lt;/p&gt;

&lt;p&gt;The user can be authenticated.&lt;/p&gt;

&lt;p&gt;And the attendance request can still be illegitimate.&lt;/p&gt;

&lt;p&gt;So I needed another verification layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Isn't the Same as Device Trust
&lt;/h2&gt;

&lt;p&gt;Traditional authentication primarily answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Who is this user?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this attendance system, I also wanted to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is this request coming from the device associated with this account?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That led me to implement device-bound authentication.&lt;/p&gt;

&lt;p&gt;The idea is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Account
     │
     ▼
Trusted Device
     │
     ▼
Attendance Request
     │
     ▼
Backend Verification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The device becomes another signal that the backend can use when deciding whether an attendance request should continue.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Device Registration Flow
&lt;/h2&gt;

&lt;p&gt;At a high level, the flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First Login
    │
    ▼
Generate / Retrieve Device Identifier
    │
    ▼
Associate Device With Account
    │
    ▼
Store Device Association
    │
    ▼
Future Requests
    │
    ▼
Backend Compares Device Identity
    │
    ├── Match ────────► Continue
    │
    └── Mismatch ─────► Reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that the final decision happens on the backend.&lt;/p&gt;

&lt;p&gt;The client shouldn't be able to simply say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Trust me, this is my device."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The backend needs to validate the request against the device association it already knows about.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 1-to-1 Device Lock
&lt;/h2&gt;

&lt;p&gt;In my implementation, the relevant student flow uses a strict one-to-one device association.&lt;/p&gt;

&lt;p&gt;The repository uses the persistent identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uba_emergency_device_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to maintain that device association.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student A
    │
    └── Device A ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student A
    │
    ├── Device A ✅
    │
    └── Device B ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important distinction is that having valid credentials doesn't automatically make a new device trusted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Device Verification Fits
&lt;/h2&gt;

&lt;p&gt;Device binding doesn't replace dynamic QR authentication.&lt;/p&gt;

&lt;p&gt;It adds another layer to it.&lt;/p&gt;

&lt;p&gt;The attendance request becomes something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attendance Request
        │
        ▼
User Authentication
        │
        ▼
Dynamic QR Validation
        │
        ▼
Device Verification
        │
        ▼
Backend Validation
        │
        ▼
Attendance Recorded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer addresses a different problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic QR
&lt;/h3&gt;

&lt;p&gt;Makes previously captured QR codes much less useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Establishes the student's account identity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Device verification
&lt;/h3&gt;

&lt;p&gt;Makes casual account sharing across devices harder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend validation
&lt;/h3&gt;

&lt;p&gt;Keeps the final decision on the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Not Just Trust the Client?
&lt;/h2&gt;

&lt;p&gt;This is an important principle I learned while building the system:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The client should provide information. The backend should decide whether that information can be trusted.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A device identifier coming from the browser isn't magically secure.&lt;/p&gt;

&lt;p&gt;A determined attacker may be able to manipulate client-side identifiers depending on the environment.&lt;/p&gt;

&lt;p&gt;That's why I don't treat device binding as an unbreakable security mechanism.&lt;/p&gt;

&lt;p&gt;It's another signal.&lt;/p&gt;

&lt;p&gt;The goal is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Make proxy attendance impossible."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Make casual proxy attendance significantly harder without making legitimate attendance painful."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Recovery Problem
&lt;/h2&gt;

&lt;p&gt;Device binding immediately creates another problem.&lt;/p&gt;

&lt;p&gt;What happens when the legitimate user changes devices?&lt;/p&gt;

&lt;p&gt;Phones get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lost&lt;/li&gt;
&lt;li&gt;Replaced&lt;/li&gt;
&lt;li&gt;Reset&lt;/li&gt;
&lt;li&gt;Repaired&lt;/li&gt;
&lt;li&gt;Reconfigured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browser storage can also disappear.&lt;/p&gt;

&lt;p&gt;A security mechanism without recovery can become worse than the problem it was designed to solve.&lt;/p&gt;

&lt;p&gt;So a real implementation has to consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trusted Device
      │
      ├── Device lost
      ├── New phone
      ├── Storage reset
      └── Device replacement
              │
              ▼
        Recovery Process
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the areas where authentication becomes a system-design problem rather than just an &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security vs Usability
&lt;/h2&gt;

&lt;p&gt;Every additional security layer introduces friction.&lt;/p&gt;

&lt;p&gt;More checks can mean:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;but also:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More ways to accidentally block legitimate users.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this project, I think about the trade-off as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Security
   ×
Usability
   ×
Recoverability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three matter.&lt;/p&gt;

&lt;p&gt;A system that blocks every suspicious request but also locks legitimate students out isn't a good attendance system.&lt;/p&gt;

&lt;p&gt;Likewise, a system that is extremely convenient but accepts every request isn't doing much to prevent proxy attendance.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Doesn't Protect Against
&lt;/h2&gt;

&lt;p&gt;It's important not to oversell device binding.&lt;/p&gt;

&lt;p&gt;It can help reduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic account sharing&lt;/li&gt;
&lt;li&gt;Casual multi-device access&lt;/li&gt;
&lt;li&gt;Credential-based proxy attendance&lt;/li&gt;
&lt;li&gt;Repeated use of one student's account from another device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it doesn't automatically prevent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sophisticated attackers&lt;/li&gt;
&lt;li&gt;Compromised devices&lt;/li&gt;
&lt;li&gt;Manipulation of client-side identifiers&lt;/li&gt;
&lt;li&gt;Someone physically using the registered device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security is about the threat model.&lt;/p&gt;

&lt;p&gt;For my project, I'm primarily trying to prevent &lt;strong&gt;ordinary proxy attendance&lt;/strong&gt;, not defend against a nation-state with a lab full of reverse engineers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;When I started this project, I thought proxy attendance was mainly a QR-code problem.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;It became an identity problem.&lt;/p&gt;

&lt;p&gt;The QR tells the system about the &lt;strong&gt;attendance session&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Authentication tells it about the &lt;strong&gt;student&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Device verification provides another signal about the &lt;strong&gt;device making the request&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Backend validation brings those signals together.&lt;/p&gt;

&lt;p&gt;That led me to a broader lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security rarely comes from one perfect mechanism. It comes from multiple layers that compensate for each other's weaknesses.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Device-bound authentication solved another part of the proxy-attendance problem.&lt;/p&gt;

&lt;p&gt;But it also introduced more engineering questions.&lt;/p&gt;

&lt;p&gt;How should these validations interact with the database?&lt;/p&gt;

&lt;p&gt;How do you minimize Firestore reads while performing multiple checks?&lt;/p&gt;

&lt;p&gt;How should suspicious device changes be logged?&lt;/p&gt;

&lt;p&gt;And how do you design the backend so that adding security doesn't make every attendance request unnecessarily expensive?&lt;/p&gt;

&lt;p&gt;Those are the problems I'm exploring as I continue building the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I started with a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I stop someone from sharing a QR code?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then it became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What stops them from sharing an account?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And eventually:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How do I know the request is coming from a trusted device?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's been one of the most interesting parts of building this project.&lt;/p&gt;

&lt;p&gt;The architecture wasn't designed perfectly on day one.&lt;/p&gt;

&lt;p&gt;It evolved as each new problem exposed another weakness.&lt;/p&gt;

&lt;p&gt;And honestly, that's probably the most realistic way to build software.&lt;/p&gt;




&lt;h3&gt;
  
  
  Project
&lt;/h3&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/siddarthpatelkama/UBA-veltech-attendance-system" rel="noopener noreferrer"&gt;https://github.com/siddarthpatelkama/UBA-veltech-attendance-system&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💼 &lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/siddarthpatelkama" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>architecture</category>
    </item>
    <item>
      <title>I Didn't Plan to Build an Offline-First Attendance System. Firestore Forced Me To.</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Fri, 24 Jul 2026 19:39:11 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/i-didnt-plan-to-build-an-offline-first-attendance-system-firestore-forced-me-to-259a</link>
      <guid>https://dev.to/siddarthpatelkama/i-didnt-plan-to-build-an-offline-first-attendance-system-firestore-forced-me-to-259a</guid>
      <description>&lt;p&gt;How accidentally exhausting Firestore's free-tier limit transformed a rushed MVP into a more resilient anti-proxy attendance system.&lt;/p&gt;

&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;When I started building an attendance system for my university's UBA (Unnat Bharat Abhiyan) club, I had one clear instruction from our coordinator:&lt;/p&gt;

&lt;p&gt;"Build an MVP as soon as possible."&lt;/p&gt;

&lt;p&gt;Like most student developers, I focused on getting the features working first.&lt;/p&gt;

&lt;p&gt;The application already had everything I needed for the MVP:&lt;/p&gt;

&lt;p&gt;Dynamic QR-based attendance&lt;br&gt;
Firebase Authentication&lt;br&gt;
Firestore database&lt;br&gt;
Anti-proxy validation&lt;br&gt;
Admin dashboard&lt;br&gt;
Meeting management&lt;/p&gt;

&lt;p&gt;My daily workflow looked something like this:&lt;/p&gt;

&lt;p&gt;Code&lt;br&gt;
   ↓&lt;br&gt;
Run&lt;br&gt;
   ↓&lt;br&gt;
Test&lt;br&gt;
   ↓&lt;br&gt;
Scan QR&lt;br&gt;
   ↓&lt;br&gt;
Debug&lt;br&gt;
   ↓&lt;br&gt;
Repeat&lt;/p&gt;

&lt;p&gt;Everything seemed to be progressing smoothly.&lt;/p&gt;

&lt;p&gt;What I wasn't paying attention to was something happening quietly in the background.&lt;/p&gt;

&lt;p&gt;Every refresh.&lt;/p&gt;

&lt;p&gt;Every API call.&lt;/p&gt;

&lt;p&gt;Every test.&lt;/p&gt;

&lt;p&gt;Every debugging session.&lt;/p&gt;

&lt;p&gt;All of them were consuming Firestore document reads.&lt;/p&gt;

&lt;p&gt;The Problem I Never Expected&lt;/p&gt;

&lt;p&gt;One day, while testing the application, things suddenly stopped working.&lt;/p&gt;

&lt;p&gt;Attendance requests started failing.&lt;/p&gt;

&lt;p&gt;Firestore wasn't responding.&lt;/p&gt;

&lt;p&gt;Like most developers, my first thought was:&lt;/p&gt;

&lt;p&gt;"I definitely broke something."&lt;/p&gt;

&lt;p&gt;I checked my recent commits.&lt;/p&gt;

&lt;p&gt;I reviewed the API routes.&lt;/p&gt;

&lt;p&gt;I verified Firebase Authentication.&lt;/p&gt;

&lt;p&gt;I even looked through my Firestore Security Rules.&lt;/p&gt;

&lt;p&gt;Everything looked perfectly fine.&lt;/p&gt;

&lt;p&gt;Then I opened the Firebase Console.&lt;/p&gt;

&lt;p&gt;That's when I saw it.&lt;/p&gt;

&lt;p&gt;Firestore Reads: 50,000 / 50,000&lt;/p&gt;

&lt;p&gt;I had exhausted the entire free-tier quota.&lt;/p&gt;

&lt;p&gt;Not because thousands of students were using my application.&lt;/p&gt;

&lt;p&gt;Not because it had gone viral.&lt;/p&gt;

&lt;p&gt;Simply because I had spent days developing and debugging it.&lt;/p&gt;

&lt;p&gt;Ironically, I had become my own biggest user.&lt;/p&gt;

&lt;p&gt;The Easy Fix Wasn't the Right Fix&lt;/p&gt;

&lt;p&gt;Technically, I could have waited for the daily quota to reset.&lt;/p&gt;

&lt;p&gt;Problem solved.&lt;/p&gt;

&lt;p&gt;But this wasn't just another side project.&lt;/p&gt;

&lt;p&gt;It was an attendance system intended to be used during actual UBA meetings.&lt;/p&gt;

&lt;p&gt;If Firestore became unavailable during a meeting, attendance couldn't simply stop.&lt;/p&gt;

&lt;p&gt;So instead of asking,&lt;/p&gt;

&lt;p&gt;"How do I reduce Firestore reads?"&lt;/p&gt;

&lt;p&gt;I asked a different question.&lt;/p&gt;

&lt;p&gt;"How do I make the system continue working even when Firestore isn't available?"&lt;/p&gt;

&lt;p&gt;That single question completely changed the architecture of the project.&lt;/p&gt;

&lt;p&gt;Offline-First Was Never the Plan&lt;/p&gt;

&lt;p&gt;Before this happened, offline support wasn't even on my roadmap.&lt;/p&gt;

&lt;p&gt;Every attendance request depended on Firestore.&lt;/p&gt;

&lt;p&gt;Every scan required the backend.&lt;/p&gt;

&lt;p&gt;Which meant one dependency controlled whether the entire application worked.&lt;/p&gt;

&lt;p&gt;I didn't like that.&lt;/p&gt;

&lt;p&gt;So I redesigned the workflow.&lt;/p&gt;

&lt;p&gt;Instead of immediately writing attendance to Firestore, the application could now continue functioning locally whenever the backend wasn't available.&lt;/p&gt;

&lt;p&gt;Attendance records would be stored securely on the device and synchronised automatically once connectivity or database access returned.&lt;/p&gt;

&lt;p&gt;The project unexpectedly became offline-first.&lt;/p&gt;

&lt;p&gt;But There Was One Catch...&lt;/p&gt;

&lt;p&gt;Building offline support for a normal CRUD application is relatively straightforward.&lt;/p&gt;

&lt;p&gt;Building it for an anti-proxy attendance system isn't.&lt;/p&gt;

&lt;p&gt;Offline mode couldn't become an easy way to bypass the security model.&lt;/p&gt;

&lt;p&gt;The system still had to guarantee:&lt;/p&gt;

&lt;p&gt;Attendance integrity&lt;br&gt;
Duplicate prevention&lt;br&gt;
Secure local storage&lt;br&gt;
Reliable synchronisation&lt;br&gt;
Consistent validation after syncing&lt;/p&gt;

&lt;p&gt;The goal wasn't simply to make the application work offline.&lt;/p&gt;

&lt;p&gt;It was to ensure the same anti-proxy workflow remained intact whether the application was online or offline.&lt;/p&gt;

&lt;p&gt;That made the redesign much more interesting than simply adding a cache.&lt;/p&gt;

&lt;p&gt;Solving the Symptom vs Solving the Cause&lt;/p&gt;

&lt;p&gt;The offline architecture solved the immediate problem.&lt;/p&gt;

&lt;p&gt;But after everything was working again, I realised something.&lt;/p&gt;

&lt;p&gt;Running out of Firestore reads wasn't the real problem.&lt;/p&gt;

&lt;p&gt;My application was.&lt;/p&gt;

&lt;p&gt;I had built the MVP quickly.&lt;/p&gt;

&lt;p&gt;I hadn't built it efficiently.&lt;/p&gt;

&lt;p&gt;That led me to review every interaction between the application and Firestore.&lt;/p&gt;

&lt;p&gt;Questions I had never asked before suddenly became important.&lt;/p&gt;

&lt;p&gt;Do I really need this document read?&lt;br&gt;
Why am I fetching the same document multiple times?&lt;br&gt;
Can this data be cached?&lt;br&gt;
Can I combine multiple reads into one request?&lt;br&gt;
Is every database write actually necessary?&lt;/p&gt;

&lt;p&gt;Those questions ended up improving the architecture far more than any new feature.&lt;/p&gt;

&lt;p&gt;Optimising Firestore Usage&lt;/p&gt;

&lt;p&gt;Once the offline system was stable, I shifted my attention to optimisation.&lt;/p&gt;

&lt;p&gt;I reduced unnecessary Firestore reads.&lt;/p&gt;

&lt;p&gt;I optimised database queries.&lt;/p&gt;

&lt;p&gt;I removed redundant writes wherever possible.&lt;/p&gt;

&lt;p&gt;I introduced Render's free cache to store frequently accessed data, reducing repeated requests for information that rarely changed.&lt;/p&gt;

&lt;p&gt;These weren't flashy improvements.&lt;/p&gt;

&lt;p&gt;Users would never notice them directly.&lt;/p&gt;

&lt;p&gt;But together, they made the application significantly more efficient and much less dependent on Firestore's free-tier limits.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;When I started this project, I thought software engineering was mostly about building features.&lt;/p&gt;

&lt;p&gt;This experience taught me something different.&lt;/p&gt;

&lt;p&gt;Sometimes the most important engineering decisions don't happen while adding new features.&lt;/p&gt;

&lt;p&gt;They happen when your assumptions break.&lt;/p&gt;

&lt;p&gt;Running out of Firestore reads forced me to think beyond "making it work."&lt;/p&gt;

&lt;p&gt;It pushed me to think about resilience.&lt;/p&gt;

&lt;p&gt;Reliability.&lt;/p&gt;

&lt;p&gt;Efficiency.&lt;/p&gt;

&lt;p&gt;And designing systems that continue working even when one of their dependencies doesn't.&lt;/p&gt;

&lt;p&gt;Looking back, hitting Firestore's limit was frustrating.&lt;/p&gt;

&lt;p&gt;Today, I'm actually grateful it happened.&lt;/p&gt;

&lt;p&gt;Without that unexpected limitation, I probably would never have redesigned the application into something far more resilient than the MVP I originally planned.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;This project began as a simple anti-proxy attendance system for my university.&lt;/p&gt;

&lt;p&gt;It unexpectedly became a lesson in building software that can survive constraints.&lt;/p&gt;

&lt;p&gt;Sometimes architecture evolves because of careful planning.&lt;/p&gt;

&lt;p&gt;Sometimes it evolves because reality forces you to rethink everything.&lt;/p&gt;

&lt;p&gt;For me, it took exactly 50,000 Firestore document reads to learn that lesson.&lt;/p&gt;

&lt;p&gt;And honestly, I wouldn't build it any other way now.&lt;/p&gt;

&lt;p&gt;Thanks for Reading! 🚀&lt;/p&gt;

&lt;p&gt;Have you ever hit a cloud free-tier limit, API quota, or infrastructure constraint that completely changed the way you designed your application?&lt;/p&gt;

&lt;p&gt;I'd love to hear your experience in the comments. Some of the best engineering lessons come from the problems we never planned for.&lt;/p&gt;

</description>
      <category>database</category>
      <category>firebase</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>How I Designed an Anti-Proxy Attendance System Using Dynamic QR Authentication</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Mon, 29 Jun 2026 12:45:23 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/how-i-designed-an-anti-proxy-attendance-system-using-dynamic-qr-authentication-4h8i</link>
      <guid>https://dev.to/siddarthpatelkama/how-i-designed-an-anti-proxy-attendance-system-using-dynamic-qr-authentication-4h8i</guid>
      <description>&lt;p&gt;&lt;em&gt;"Anyone can generate a QR code. Designing one that actually prevents misuse is the hard part."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When people hear "QR-based attendance," the first question is usually:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can't someone just take a photo of the QR code and send it to their friend?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is: &lt;strong&gt;yes&lt;/strong&gt;, if the system is designed poorly.&lt;/p&gt;

&lt;p&gt;That question became one of the biggest engineering challenges while building my attendance platform.&lt;/p&gt;

&lt;p&gt;Instead of thinking about QR codes as images, I started thinking about them as &lt;strong&gt;temporary authentication tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Most QR attendance systems work like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Teacher generates a QR code.&lt;/li&gt;
&lt;li&gt;Students scan it.&lt;/li&gt;
&lt;li&gt;Attendance is marked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Unfortunately, it's also vulnerable.&lt;/p&gt;

&lt;p&gt;A student can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Photograph the QR code.&lt;/li&gt;
&lt;li&gt;Send it through WhatsApp.&lt;/li&gt;
&lt;li&gt;Another student scans it from somewhere else.&lt;/li&gt;
&lt;li&gt;Attendance gets recorded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The QR code itself becomes the vulnerability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rethinking the QR Code
&lt;/h2&gt;

&lt;p&gt;Instead of creating a QR code that simply points to an attendance page, I designed it to behave more like a short-lived authentication token.&lt;/p&gt;

&lt;p&gt;Every QR code is generated dynamically and remains valid only for a limited period (in my implementation, I set this to 11 seconds).&lt;/p&gt;

&lt;p&gt;Once it expires, the system rejects it.&lt;/p&gt;

&lt;p&gt;This dramatically reduces the opportunity for someone to reuse an older QR code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication Beyond the QR
&lt;/h2&gt;

&lt;p&gt;Scanning the QR code alone isn't enough.&lt;/p&gt;

&lt;p&gt;The backend performs additional validation before marking attendance.&lt;/p&gt;

&lt;p&gt;Rather than trusting the client, the server becomes the source of truth.&lt;/p&gt;

&lt;p&gt;This reduces opportunities for client-side manipulation and keeps the attendance workflow under server control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing for the Real World
&lt;/h2&gt;

&lt;p&gt;One challenge I encountered was balancing &lt;strong&gt;security&lt;/strong&gt; with &lt;strong&gt;usability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the QR expires too quickly, students may struggle to scan it in time.&lt;/p&gt;

&lt;p&gt;If it stays active for too long, it becomes easier to misuse.&lt;/p&gt;

&lt;p&gt;Finding the right balance required thinking about actual classroom conditions rather than only technical implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;One lesson stood out throughout this project:&lt;/p&gt;

&lt;p&gt;Security isn't usually one big feature.&lt;/p&gt;

&lt;p&gt;It's the result of multiple smaller decisions working together.&lt;/p&gt;

&lt;p&gt;A dynamic QR code helps.&lt;/p&gt;

&lt;p&gt;Server-side validation helps.&lt;/p&gt;

&lt;p&gt;Short-lived sessions help.&lt;/p&gt;

&lt;p&gt;Each layer reduces risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This article only covers one piece of the system.&lt;/p&gt;

&lt;p&gt;In future posts, I'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend architecture&lt;/li&gt;
&lt;li&gt;Firestore data modeling&lt;/li&gt;
&lt;li&gt;Authentication flow&lt;/li&gt;
&lt;li&gt;Scaling the platform&lt;/li&gt;
&lt;li&gt;Engineering decisions behind the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building software has taught me that writing code is only part of engineering.&lt;/p&gt;

&lt;p&gt;Designing systems that continue to work under real-world conditions is where the interesting problems begin. In this project, I used libraries like &lt;code&gt;qrcode&lt;/code&gt; on the backend to generate dynamic QR codes and &lt;code&gt;html5-qrcode&lt;/code&gt; on the frontend to scan and verify them efficiently, while SHA-256 hashing was used on the backend to securely generate and validate the QR token payloads.&lt;/p&gt;




&lt;p&gt;⭐ GitHub: &lt;a href="https://github.com/siddarthpatelkama" rel="noopener noreferrer"&gt;https://github.com/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💼 LinkedIn: &lt;a href="https://www.linkedin.com/in/siddarthpatelkama" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>showdev</category>
      <category>softwareengineering</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Building an Anti-Proxy -Offline-First Attendance Platform with Next.js, Express &amp; Firebase</title>
      <dc:creator>siddarthpatelkama </dc:creator>
      <pubDate>Fri, 26 Jun 2026 12:10:14 +0000</pubDate>
      <link>https://dev.to/siddarthpatelkama/building-an-anti-proxy-offline-first-attendance-platform-with-nextjs-express-firebase-15io</link>
      <guid>https://dev.to/siddarthpatelkama/building-an-anti-proxy-offline-first-attendance-platform-with-nextjs-express-firebase-15io</guid>
      <description>&lt;p&gt;Traditional attendance systems seem simple until you look at how they work in practice. Manual roll calls consume valuable class time, paper attendance sheets are inefficient, and many QR-based systems can still be exploited through proxy attendance.&lt;/p&gt;

&lt;p&gt;I wanted to explore whether a better solution was possible.&lt;/p&gt;

&lt;p&gt;This project is my attempt at designing and building an &lt;strong&gt;offline-first anti-proxy attendance platform&lt;/strong&gt; from scratch as a solo developer. The goal wasn't just to digitize attendance, but to create a system that remains reliable in real-world environments while prioritizing security, scalability, and a smooth user experience.&lt;/p&gt;

&lt;p&gt;The platform is built using &lt;strong&gt;Next.js 16, React 19, TypeScript, Express.js, Firebase Authentication, Firestore, Firebase Admin SDK, Tailwind CSS, Capacitor, Progressive Web Apps (PWA), Sentry, and dynamic QR-based authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than focusing only on features, this article explores the engineering decisions behind the project: why I chose an offline-first architecture, how dynamic QR validation improves security, the reasoning behind the technology stack, the challenges I faced during development, and the lessons I learned while building a production-style application that is already being used in my college club.&lt;/p&gt;

&lt;p&gt;The project is still evolving, and I continue to improve it whenever I have time. My aim is to keep documenting that journey by sharing not only what works, but also the mistakes, trade-offs, and architectural decisions that shaped the system.&lt;/p&gt;

&lt;p&gt;If you're interested in &lt;strong&gt;full-stack development, system design, Next.js, Firebase, TypeScript, or building software that solves practical problems&lt;/strong&gt;, I hope you'll find something valuable in this article.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/siddarthpatelkama" rel="noopener noreferrer"&gt;https://github.com/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💼 &lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/siddarthpatelkama" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/siddarthpatelkama&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm always open to feedback, suggestions, and discussions with fellow developers. If you have ideas for improving the project or questions about the implementation, I'd love to hear from you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>computerscience</category>
      <category>development</category>
      <category>vibecoding</category>
    </item>
  </channel>
</rss>
