← The Envert Journal
architectureNovember 4, 2026·15 min read

How to Add Authentication to a Custom Web App (And Get It Right)

Learn how to add authentication to a custom web app without introducing security vulnerabilities or ballooning tech debt. Compare Clerk, Supabase, and custom auth.

Modern software studio workstation showing authentication architecture diagrams and code on screens in dim cinematic lighting.

Authentication sounds deceptive. When non-technical founders or junior engineers talk about user login, they describe it as a single form with two input fields and a button: enter an email, enter a password, check the database, issue a cookie. In a weekend tutorial, that takes forty-five minutes to build.

In a production application, that barebones implementation is an operational landmine.

Learning how to add authentication to a custom web app the right way means engineering for edge cases, attack vectors, data compliance, and user onboarding friction. If you roll your own authentication poorly, you risk credential stuffing attacks, session hijacking, corrupted tenant isolation, and catastrophic data breaches. If you pick the wrong vendor, you can easily end up locked into an infrastructure tier that runs $2,000 to $5,000 per month before your SaaS reaches product-market fit.

This guide breaks down the architectural decisions, vendors, security protocols, and implementation workflows required to deploy modern authentication on your custom web application without stalling your roadmap.


The Trap: Why Most Teams Get Web App Authentication Wrong

The fundamental mistake teams make when approaching identity management is treating authentication ("who are you?") and authorization ("what are you allowed to do?") as an afterthought. It gets bolted onto an existing data model days before launch, leading to rewrites three months down the road.

The Build vs. Buy Dilemma in 2025

Five years ago, developers routinely argued about whether to roll custom authentication using bcrypt and JSON Web Tokens (JWTs) or pay for legacy tools like Auth0. In 2025, the consensus among elite software engineers is unambiguous: do not build your own core authentication engine from scratch.

Writing custom password hashing, reset token invalidation, salt generation, and session renewal logic does not create business value for your startup. Unless you are building an identity provider like Okta or 1Password, authentication is pure table stakes infrastructure. When you hand-roll authentication, you assume 100% of the ongoing cryptographic risk and liability.

Consider the raw numbers:

  • Custom Auth Build Cost: 80 to 140 developer hours ($12,000 to $25,000 in blended engineering costs) to build login, registration, email verification, password reset, multi-factor authentication (MFA), and session management.
  • Ongoing Maintenance: 5 to 10 hours per month patching dependencies, maintaining OAuth client libraries, updating compliance logic, and monitoring security vulnerabilities.
  • Hosted Auth Cost: $0 to $35/month for your first 5,000 to 10,000 monthly active users (MAU), implemented in 8 to 16 developer hours.

Building your own crypto primitives is almost always a misuse of capital. However, "buying" does not mean you abdicate architecture. You still have to integrate identity correctly within your application layer.

The Hidden Maintenance Cost of "Simple" Auth

Teams that roll their own auth rarely account for secondary workflows:

  1. Email Deliverability: When a user requests a password reset, your transactional email service (Resend, Postmark, AWS SES) must deliver that message in under 15 seconds. If your IP address gets flagged or emails land in spam, your user churn spikes.
  2. Device & Session Revocation: What happens when an employee leaves an enterprise client? Or when a user loses their phone? If your database only stores static tokens, you cannot selectively invalidate single sessions without forcing every user to log out globally.
  3. OAuth Provider Breaking Changes: Google, Apple, and GitHub routinely deprecate API scopes and rotate verification certificates. If you manage OAuth integrations manually, a deprecated scope can take down your sign-in button overnight.

Before picking a tool, you must select the authentication modalities that fit your product's threat model and end-user expectations.

+-------------------------------------------------------------------------+
|                       User Interface / Front End                        |
+-------------------+-----------------+-----------------+-----------------+
| Passwords + MFA   | Passkeys (FIDO2)| Social / OAuth  | Magic Links     |
+-------------------+-----------------+-----------------+-----------------+
         |                   |                 |                 |
         v                   v                 v                 v
+-------------------------------------------------------------------------+
|               Identity Broker / Auth Gateway (e.g., Clerk)              |
+-------------------------------------------------------------------------+
         |                                              |
         v (Signed JWT / HttpOnly Cookie)               v (Webhook)
+-------------------------------------+   +-------------------------------+
|   Web App Server / Next.js / API    |   | Sync User to App Database     |
|   - Token Validation                |   | - Organizations               |
|   - Session Hydration               |   | - Roles & Permissions         |
+-------------------------------------+   +-------------------------------+

Username & Password: Dying, But Not Dead

Despite industry efforts to kill passwords, users still expect them. If you run a B2B SaaS platform, enterprise procurement teams will evaluate your password policy.

If you support passwords, adhere strictly to current NIST (National Institute of Standards and Technology) SP 800-63B guidelines:

  • Minimum length: 8 characters (allow up to 64+ characters for passphrases).
  • No arbitrary complexity rules: Requiring symbols, numbers, and capital letters often results in predictable substitutions (P@ssword1!) rather than true entropy.
  • Check against breached lists: Screen passwords against databases of compromised credentials like HaveIBeenPwned.
  • Hashing algorithm: Use Argon2id (memory-hard, resistant to GPU/ASIC attacks) or bcrypt with a work factor of at least 12.

Passkeys (WebAuthn): The Modern Gold Standard

Passkeys leverage the FIDO2/WebAuthn standard, replacing shared secrets with asymmetric cryptography. When a user registers via a passkey, their device generates a private key stored in secure hardware (like Apple's Secure Enclave or Android's Titan chip) and sends only the public key to your server.

  • Pros: Completely immune to phishing, credential stuffing, and SIM-swapping. Frictionless biometric login (Face ID, Touch ID, Windows Hello).
  • Cons: Recovery across devices remains slightly confusing for non-technical users, though Apple Keychain and Google Password Manager have significantly smoothed cross-device sync.

If you are launching a greenfield application, offering passkeys on day one dramatically increases conversion rates on mobile web.

Social Logins & OAuth2: Fast Onboarding with Edge Cases

Social login (Google, GitHub, Apple, Microsoft) reduces signup drop-off by removing the need to choose and store a password. However, it introduces subtle edge cases:

  • Email Verification Conflicts: What happens if a user signs up with jane@gmail.com using email/password, and later clicks "Sign in with Google" using that same address? Without strict account-linking rules, you can accidentally create two separate accounts or open an account-takeover vector if the provider does not guarantee email verification.
  • Enterprise Restrictions: Enterprise clients will block social sign-in. If your customer is a healthcare system or financial services firm, they will mandate SAML 2.0 or OIDC single sign-on (SSO) mapped to their corporate directory (Azure AD/Entra ID, Okta).

Magic links (passwordless email logins) eliminate passwords by sending a cryptographically signed, short-lived token to the user's inbox.

While consumer apps love magic links, they have serious real-world failure points:

  • Corporate Email Scanners: Enterprise firewalls (like Mimecast or Proofpoint) automatically follow and pre-fetch links in incoming emails to check for malware. If the scanner hits your magic link with a GET request, the single-use token gets consumed before the user ever opens the email, resulting in an "invalid or expired link" error.
  • Latency: If your transactional email provider has a 45-second queue spike, users abandon your application.
  • Security: Account security becomes entirely dependent on the security of the user's email client.

Best practice: Use magic links as an alternative fallback or onboarding aid, not as the single mandatory authentication method for enterprise workflows.


Evaluating the Stack: Clerk vs. Supabase vs. Auth0 vs. Custom

Choosing the right identity provider depends on your application architecture, expected user volume, and internal technical bandwidth.

Provider Best For Free Tier Pricing at 50k MAU Primary Advantage Main Risk / Limitation
Clerk Modern Next.js/React SaaS Up to 10k MAU ~$100–$250/mo Pre-built, polished UI components & B2B orgs Vendor lock-in to their component ecosystem
Supabase Auth Full-stack apps needing Postgres Up to 50k MAU $25/mo (Pro tier base) Native integration with Postgres Row-Level Security (RLS) UI components require manual styling; complex SSR handling
Auth0 / Okta Mid-market & Enterprise B2B Up to 7.5k MAU $800–$2,500+/mo Enterprise SAML/SSO maturity, compliance certifications Expensive scale pricing; clunky, legacy developer experience
Self-Hosted (Lucia / Auth.js) Strict data sovereignty / offline apps Free (Infrastructure cost only) Server costs only (~$40–$200/mo) Complete control over data, no monthly active user fees Zero support; requires deep cryptography and maintenance knowledge

Hosted Auth Providers: Clerk and Stytch

For most modern web apps—especially those built with Next.js, Remix, or SvelteKit—providers like Clerk represent the sweet spot between developer velocity and UX quality.

Clerk provides complete drop-in UI components for user profile management, multi-tenant organization switching, invitation flows, and session tokens. If you are building a B2B SaaS MVP, using Clerk can save two to three weeks of frontend engineering. Their webhooks allow you to listen for user creation events and sync user records directly into your core application database (e.g., PostgreSQL or Prisma).

Database-Coupled Auth: Supabase and Firebase

If you are using Supabase as your primary backend, using Supabase Auth (powered by GoTrue) is often the most architectural coherent choice.

The primary benefit is PostgreSQL Row-Level Security (RLS). With Supabase Auth, the user's ID is injected directly into Postgres session variables (auth.uid()). This allows you to enforce database-level access policies:

-- Example: A user can only read invoices belonging to their organization
CREATE POLICY "Users can view organization invoices" 
ON invoices 
FOR SELECT 
USING (organization_id = (SELECT org_id FROM users WHERE id = auth.uid()));

This architecture eliminates an entire class of authorization bugs by preventing the application layer from accidentally exposing another user's data.

Talk to a builder

Want this shipped, not just read about?

Book a free scoping call. We'll map the smallest billable wedge of your idea and tell you honestly if we're the right team to build it.

Book a free scoping call

See what we've shipped →

Enterprise Incumbents: Auth0 and Okta

Auth0 remains the default recommendation for legacy enterprises, but it comes with baggage. While their self-serve tiers are manageable, upgrading to support enterprise SAML SSO frequently lands you in enterprise contract territory, starting between $12,000 and $24,000 annually. For early-stage startups, this pricing cliff can be paralyzing. Unless an enterprise customer is currently writing a five-figure check conditioned on SAML SSO, avoid over-indexing on Auth0 early on.

(Note: If you are building custom software with Envert—whether that is an enterprise internal tool, a mobile app, or a high-growth SaaS MVP—our engineering team structures your identity layer so you can start on lightweight infrastructure and graduate to SAML/SSO without rebuilding your user database.)

Cost Comparison: Day 1 vs. Year 2 (Real Numbers)

Let’s analyze the true cost of these approaches over time for a B2B SaaS app that scales from 1,000 to 25,000 monthly active users over two years:

  • Scenario A: Built from scratch in-house

    • Initial build: $18,000 (120 engineering hours @ $150/hr blended)
    • Ongoing security patches/maintenance: $900/month (6 hours/mo)
    • Infrastructure & email sending: $40/month
    • Total 2-Year Cost: ~$40,000 (plus substantial liability risk)
  • Scenario B: Built with Clerk

    • Initial integration: $1,800 (12 engineering hours)
    • Clerk subscription: Year 1 ($0 to $25/mo), Year 2 (~$150/mo average at scale)
    • Total 2-Year Cost: ~$4,000 to $6,000
  • Scenario C: Built with Supabase Auth

    • Initial integration: $2,400 (16 engineering hours)
    • Supabase Pro Plan: $25/month flat (covers up to 50k MAU)
    • Total 2-Year Cost: ~$3,000 to $4,000

Hosted solutions are not just faster to market; they are 80% to 90% cheaper over a 24-month horizon when factoring in developer wages.


Step-by-Step Implementation: How to Add Authentication to a Custom Web App

Regardless of the identity provider you select, you must execute four critical engineering steps within your application codebase.

Step 1: Session Management & Token Storage

The most dangerous architectural flaw in modern web apps is improper token storage.

Never store long-lived JWTs or access tokens in localStorage or sessionStorage.

Why? Code running inside localStorage is vulnerable to Cross-Site Scripting (XSS). If an attacker injects a malicious script via a compromised third-party npm package, an image tag, or an unescaped markdown parser, they can read localStorage.getItem('token') and exfiltrate your user’s session immediately.

The standard pattern: Store authentication tokens in HttpOnly, Secure, SameSite=Lax (or Strict) cookies.

  • HttpOnly: Makes the cookie inaccessible to JavaScript (document.cookie returns nothing), preventing XSS token theft.
  • Secure: Ensures the browser only transmits the cookie over encrypted HTTPS connections.
  • SameSite: Protects your application from Cross-Site Request Forgery (CSRF) by preventing the cookie from being sent on cross-origin requests.
// Example: Setting a secure session cookie in a Node/Next.js API route
res.setHeader('Set-Cookie', [
  `session_token=${token}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800;`
]);

Step 2: Role-Based Access Control (RBAC) and Multi-Tenancy

Authentication proves identity; authorization defines capabilities. In custom web apps, authorization usually follows one of two paradigms:

  1. RBAC (Role-Based Access Control): Users are assigned roles (e.g., Admin, Editor, Viewer), and permissions are assigned to those roles.
  2. ABAC (Attribute-Based Access Control): Permissions are evaluated dynamically based on user attributes, resource attributes, and environment variables (e.g., "Users in Department X can edit Document Y if status is Draft").

For 95% of web applications, RBAC is sufficient. However, if you are building B2B software, you need Multi-Tenant RBAC.

A user is never an "Admin" globally; they are an "Admin" within a specific Tenant (Workspace, Organization, or Team).

Your database schema must enforce this hierarchy:

CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(255) NOT NULL,
  slug VARCHAR(255) UNIQUE NOT NULL
);

CREATE TABLE organization_members (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
  user_id UUID REFERENCES users(id) ON DELETE CASCADE,
  role VARCHAR(50) NOT NULL CHECK (role IN ('owner', 'admin', 'member', 'guest')),
  UNIQUE(organization_id, user_id)
);

Every operational query in your backend must filter by both organization_id and the user's verified role within that organization.

Step 3: Protecting API Routes and Server-Side Rendering (SSR)

In modern full-stack web applications (such as Next.js App Router or Remix), authentication must run at the edge or middleware level before rendering protected views or responding to API requests.

Do not rely solely on client-side route guards (e.g., checking if (!user) router.push('/login') inside a useEffect). Doing so displays a momentary flash of sensitive UI or unhydrated data before redirecting.

Here is how middleware authentication flow should operate:

// Next.js Middleware Example (Conceptual)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifySessionToken } from '@/lib/auth';

export async function middleware(request: NextRequest) {
  const sessionToken = request.cookies.get('session_token')?.value;
  const isAuthRoute = request.nextUrl.pathname.startsWith('/dashboard');

  if (isAuthRoute) {
    if (!sessionToken) {
      return NextResponse.redirect(new URL('/login', request.url));
    }

    const isValid = await verifySessionToken(sessionToken);
    if (!isValid) {
      return NextResponse.redirect(new URL('/login?expired=true', request.url));
    }
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};

Step 4: Password Resets, MFA, and Account Recovery

If you support username/password authentication, your account recovery flow must be engineered defensively:

  1. Cryptographically Secure Random Tokens: Generate reset tokens using cryptographically secure pseudorandom number generators (like Node's crypto.randomBytes(32)), not generic UUIDs or sequential numbers.
  2. Store Hashes, Not Raw Tokens: Never store plain-text reset tokens in your database. Store a SHA-256 hash of the token. When the user clicks the reset link, hash the incoming token and match it against the stored hash. This ensures that even if your database is leaked via SQL injection, an attacker cannot reset user accounts.
  3. Short Expiration Windows: Password reset tokens should expire in 15 minutes or less.
  4. Invalidate Sessions Upon Reset: Once a password is changed, invalidate all existing active sessions and refresh tokens across all devices.

Critical Security Checklist Before You Ship to Production

Before launching any custom web app with authentication to real users, run through this non-negotiable hardening checklist.

[ ] Rate Limiting Active on /login, /signup, and /reset-password (Max 5 attempts / IP / min)
[ ] Passwords Hashed with Argon2id or bcrypt (work factor >= 12)
[ ] Session Cookies Configured: HttpOnly, Secure, SameSite=Lax/Strict
[ ] Anti-CSRF Protection Configured on State-Changing Endpoints (POST/PUT/DELETE)
[ ] Content Security Policy (CSP) Headers Active (Mitigate XSS)
[ ] Timing Attacks Neutralized via Constant-Time Comparisons
[ ] Comprehensive Audit Logging for Identity State Changes

Rate Limiting and Brute-Force Mitigation

Your login and password-reset endpoints will be targeted by credential-stuffing bots within hours of hitting the public internet. An attacker takes a list of 100,000 breached username/password combos and writes a multi-threaded script to blast your /api/auth/login route.

To prevent this:

  • IP-Based Rate Limiting: Restrict requests using tools like Upstash Redis or Cloudflare Rate Limiting (e.g., maximum 5 failed attempts per IP address per 15-minute window).
  • Account Lockout or Exponential Backoff: Temporarily freeze login attempts for specific accounts after multiple consecutive failures.
  • CAPTCHA / Bot Protection: Use invisible proof-of-work systems like Cloudflare Turnstile to block automated scripts without frustrating human users with fire-hydrant pictures.

If you use cookies for session authentication, you must defend against Cross-Site Request Forgery. If an authenticated user visits a malicious website, that site can craft a hidden form submission pointing to yourapp.com/api/transfer-funds—and the browser will automatically include your user's auth cookie.

Mitigate this using:

  • SameSite=Lax or Strict: Tells browsers not to send the cookie on third-party cross-site requests.
  • Custom Header Checks: Enforce that your API requires custom headers (e.g., X-Requested-With: XMLHttpRequest or an explicit CSRF token), which cross-origin <form> tags cannot forge.

Audit Logging and Compliance (SOC 2, GDPR, HIPAA)

If you plan to sell software to mid-market or enterprise buyers, you will eventually face a SOC 2 audit or security questionnaire.

Your application must maintain an immutable Audit Log tracking identity events:

  • Successful logins (including timestamp, IP address, user agent, and auth method)
  • Failed login attempts
  • Password changes and reset requests
  • MFA device enrollments and removals
  • Permission and role changes (e.g., "User A promoted User B to Admin")

Treat audit logs as append-only. Do not allow application users or admins to alter historical audit trails.


When to Hand Auth Off to a Product Studio

Authentication is one of those deceptive software domains where a 90% implementation feels working, but that remaining 10% contains almost all the risk.

If you are an early-stage founder or business leader evaluating a build, ask yourself:

  • Do our developers have deep experience handling multi-tenant tenant isolation and RBAC architecture?
  • Can we afford to dedicate three to four weeks of senior engineering capacity to auth plumbing instead of building our core product features?
  • If we experience a security vulnerability or session breach, what is the legal and reputational impact on our business?

At Envert, we build custom web applications, SaaS MVPs, mobile apps, and internal tools for founders and established operators. Because we are a US-based custom software studio that handles architecture end-to-end, we don't cut corners on security primitives.

Whether we are implementing Clerk with enterprise-ready multi-tenancy, setting up Supabase with PostgreSQL Row-Level Security, or engineering bespoke HIPAA-compliant permission systems, we build architectures designed to scale from user 1 to user 100,000 without requiring an expensive rewrite.

If you have a custom web application in development or need an experienced engineering team to build your platform from the ground up, book a free scoping call with the Envert team today. Let's review your product roadmap, evaluate your technical requirements, and build your application the right way.

Frequently asked questions

Should I use JWTs (JSON Web Tokens) or database sessions for web app auth?+

For most web apps, server-backed database sessions or short-lived JWTs stored in HttpOnly cookies are the best approach. While stateless JWTs are popular, they cannot be instantly revoked without maintaining a centralized revocation list, which effectively turns them back into stateful sessions. Use database sessions unless you have massive distributed scale requiring edge verification.

Is Clerk better than Supabase Auth for a SaaS application?+

Clerk is superior if you want pre-built, beautifully styled UI components, turnkey organization management (B2B SaaS multi-tenancy), and minimal frontend code. Supabase Auth is better if you are using Supabase Postgres as your database and want to enforce authorization directly at the database layer using Row-Level Security (RLS).

Why shouldn't I store authentication tokens in localStorage?+

LocalStorage is accessible to any JavaScript code running on your domain, making it highly vulnerable to Cross-Site Scripting (XSS) attacks. If an attacker manages to inject a malicious script through an npm dependency or unescaped user input, they can extract the token and hijack the user session. Always store sensitive auth tokens in HttpOnly, Secure cookies.

How much does it cost to implement authentication in a custom web app?+

Using a modern hosted provider like Clerk or Supabase, integration typically takes 10 to 30 developer hours ($1,500 to $4,500 in dev costs) with running software costs of $0 to $35/month to start. Building an equivalent custom authentication engine in-house from scratch takes 80 to 140+ hours ($12,000 to $25,000+), plus continuous security maintenance.

What is the easiest way to add enterprise Single Sign-On (SAML/SSO) later?+

Plan your user data model around an 'Organizations' and 'Memberships' abstraction from day one. By isolating users by tenant rather than assuming flat account structures, you can seamlessly plug in enterprise SSO brokers like WorkOS, BoxyHQ, or Clerk's enterprise tier later without needing to migrate or restructure your database.

#how to add authentication to a custom web app#best auth provider for saas startup#clerk vs supabase auth comparison#implementing rbac in custom web app#b2b saas multi tenant authentication
Ready to ship

Ready to ship your next product?

Free 30-minute call. We'll scope your build, name the smallest billable wedge, and tell you honestly if we're the right team.

Book a free scoping call

Reply within 24 hours · No obligation