Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

9. User and Video Model with Hooks and JWT

Lesson 9 of 23 in the free Backend Development notes on Siksha Sarovar, written by Rohit Jangra.

Designing the User Model for a YouTube-like App

The User model is the most critical schema in your backend. It needs to support authentication (password hashing, JWT tokens), profile management (avatar, cover image), and relationships (watch history, subscriptions). Let us design it properly from the start.

---

Full User Schema Fields

FieldTypePurpose
usernameString, unique, lowercaseURL-friendly unique identifier
emailString, unique, lowercaseLogin identifier
fullNameStringDisplay name
passwordString, select: falseHashed with bcrypt
avatarStringCloudinary URL for profile picture
coverImageStringCloudinary URL for channel banner
watchHistory[ObjectId ref Video]Array of watched video IDs
refreshTokenString, select: falseStored for token rotation
createdAt, updatedAtDateAdded by timestamps option

---

Full Video Schema Fields

FieldTypePurpose
videoFileStringCloudinary URL for the video
thumbnailStringCloudinary URL for video thumbnail
titleString, requiredVideo title
descriptionStringVideo description
durationNumberDuration in seconds (from Cloudinary)
viewsNumber, default 0View count
isPublishedBoolean, default trueDraft vs published
ownerObjectId ref UserWho uploaded the video
createdAt, updatedAtDateTimestamps

---

Pre-save Hook: Password Hashing

The pre-save hook runs before a document is saved to MongoDB. We use it to hash the password automatically whenever it is created or changed:

userSchema.pre('save', async function (next) {
  // Only hash if the password field was actually modified
  // This prevents re-hashing an already-hashed password on every save
  if (!this.isModified('password')) return next();

  this.password = await bcrypt.hash(this.password, 10);
  // 10 = salt rounds. Higher = more secure but slower.
  // 10 is the industry standard — takes ~100ms on modern hardware
  next();
});

Why 10 salt rounds? bcrypt generates a unique salt for each hash, preventing rainbow table attacks. 10 rounds means bcrypt runs 2^10 = 1024 iterations. This makes brute-force attacks computationally expensive.

---

isPasswordCorrect Instance Method

userSchema.methods.isPasswordCorrect = async function (password) {
  // this.password is the hashed password from the database
  // bcrypt.compare() hashes the candidate password and compares
  return await bcrypt.compare(password, this.password);
};
You need to explicitly select the password field when querying: User.findOne({ email }).select('+password') because we set select: false on the password field.

---

JWT: Access Token vs Refresh Token

Why two tokens?

  • Access Token: Short-lived (15 minutes to 1 hour). Sent with every API request. If stolen, attacker can only use it briefly before it expires.
  • Refresh Token: Long-lived (7-30 days). Stored in an httpOnly cookie and in the database. Used only to generate new access tokens. Storing it in the DB allows revocation — if a user logs out or the token is suspected stolen, delete it from DB.

---

generateAccessToken Method

userSchema.methods.generateAccessToken = function () {
  return jwt.sign(
    {
      _id: this._id,
      email: this.email,
      username: this.username,
      fullName: this.fullName,
    },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRY || '15m' }
  );
};

The JWT payload contains just enough info to identify the user and populate req.user in middleware without a DB query for every request.

---

generateRefreshToken Method

userSchema.methods.generateRefreshToken = function () {
  return jwt.sign(
    { _id: this._id },  // Minimal payload — only need _id to look up user
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: process.env.JWT_REFRESH_EXPIRY || '7d' }
  );
};

The refresh token has only _id in the payload because it is used purely to generate a new access token — we immediately look up the full user from the database.

---

Why Store Refresh Token in the Database?

BenefitExplanation
RevocationDelete from DB on logout — even valid tokens won't work
RotationIssue new refresh token on each use, invalidate old one
Multi-device logoutCan clear all refresh tokens for a user (logout from all devices)
Security auditCan track when refresh tokens were issued and used

---

JWT Payload Design Principles

  • Include only what is needed to serve requests without a DB hit
  • Do NOT include sensitive data (password, credit card, etc.)
  • Access token payload: { _id, email, username, fullName, role }
  • Refresh token payload: { _id } only
  • JWT is base64url-encoded (not encrypted) — anyone can decode the payload; sign ensures integrity