MongoDB Tutorial 0/120 lessons ~6 min read Lesson 93

    User Authentication Systems

    Almost every app on MongoDB stores users, sessions and identity in MongoDB itself.

    Course progress0%
    Focus
    6 guided sections
    Practice signal
    Examples included
    Career prep
    Foundation builder

    Introduction

    Almost every app on MongoDB stores users, sessions and identity in MongoDB itself. The document model is ideal: roles, permissions, MFA secrets, OAuth identities and audit history all sit naturally on the same user document, with embedded sub-documents and arrays.

    This lesson covers the production-grade user authentication system: password hashing, JWT/session storage, refresh tokens, OAuth identity linking, MFA, lockouts and audit. Frameworks like NextAuth, Auth.js, Passport and Spring Security all integrate seamlessly.

    Understanding the topic

    The user document — battle-tested shape:

    • email + unique index, normalized lowercase.
    • passwordHash with argon2id or bcrypt(cost ≥ 12).
    • identities[] — array of OAuth providers ({ provider, sub, linkedAt }).
    • mfa: { totpSecret, recoveryCodes, enabledAt }.
    • sessions in a separate collection with TTL index on expiresAt.
    • loginAttempts + lockedUntil for brute-force protection.
    • auditLog[] embedded array (capped at 50) for last logins, device fingerprints, IP.

    Informative example

    Sessions collection with TTL — sessions self-clean:

    js
    // One-time setup
    db.sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
    db.sessions.createIndex({ userId: 1, createdAt: -1 });
    // Create
    await db.sessions.insertOne({
    _id: crypto.randomUUID(),
    userId,
    refreshTokenHash: sha256(refreshToken),
    device: { ua, ip, country },
    createdAt: new Date(),
    expiresAt: new Date(Date.now() + 30 * 24 * 3600 * 1000)
    });
    // Rotate refresh token
    await db.sessions.findOneAndUpdate(
    { _id: sessionId, refreshTokenHash: sha256(oldRt) },
    { $set: { refreshTokenHash: sha256(newRt), expiresAt: new Date(Date.now() + 30*864e5) } }
    );

    Real-world use

    NextAuth.js, Auth.js, Clerk and Supabase Auth (in MongoDB-backed deployments) all use this shape. Argon2id + per-session refresh tokens + TTL-based expiry is the modern baseline.

    Best practices

    • Hash with argon2id; bcrypt cost 12+ is acceptable on legacy systems.
    • Store refresh tokens hashed — never plaintext, even in the DB.
    • Use a TTL index on sessions.expiresAt for automatic cleanup.
    • Rate-limit login attempts in the app layer; lock the account after 10 fails.

    Common mistakes

    • Storing the JWT secret in the same DB it protects.
    • Skipping refresh-token rotation — a stolen refresh token = lifetime account access.
    • Embedding sessions inside the user document — the doc grows unbounded.
    Ready to mark this lesson complete?Track your journey across the entire course.