DEV Community

Cover image for How to Secure Your Web App with JWT, OAuth, and Role-Based Access Control
Raji moshood
Raji moshood

Posted on

How to Secure Your Web App with JWT, OAuth, and Role-Based Access Control

Web security is non-negotiable. Whether you're building a SaaS platform, an e-commerce site, or an enterprise dashboard, implementing secure authentication and authorization is critical to protecting user data and ensuring compliance.

This guide covers:
βœ… JWT (JSON Web Token) authentication
βœ… OAuth for third-party authentication
βœ… Role-Based Access Control (RBAC) for fine-grained permissions

  1. Understanding Authentication vs. Authorization

πŸ”Ή Authentication β†’ Verifies who a user is (Login with email, OAuth, etc.)
πŸ”Ή Authorization β†’ Determines what they can access (Admin vs. User roles)

Think of authentication as verifying your identity with a passport and authorization as the visa that defines where you can travel.

  1. Implementing JWT Authentication (Stateless & Scalable)

Why JWT?

βœ” Stateless (No session storage needed)
βœ” Compact & Fast (Base64 encoded)
βœ” Secure (Can be signed with a secret or public/private keys)

Step 1: Install Dependencies

npm install express jsonwebtoken bcryptjs dotenv cors helmet express-validator
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Package Breakdown:

express β†’ Web framework

jsonwebtoken β†’ JWT generation & verification

bcryptjs β†’ Hashing passwords

dotenv β†’ Secure environment variables

helmet, cors β†’ Security best practices

Step 2: Generate & Verify JWT Tokens

A. User Registration (auth.js)

const express = require("express");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const { body, validationResult } = require("express-validator");

const router = express.Router();
const users = []; // Mock user storage

router.post(
  "/register",
  [body("email").isEmail(), body("password").isLength({ min: 6 })],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });

    const { email, password } = req.body;
    const hashedPassword = await bcrypt.hash(password, 10);
    users.push({ email, password: hashedPassword });

    res.status(201).json({ message: "User registered successfully" });
  }
);
Enter fullscreen mode Exit fullscreen mode

B. User Login & Token Generation

router.post("/login", async (req, res) => {
  const { email, password } = req.body;
  const user = users.find((u) => u.email === email);

  if (!user || !(await bcrypt.compare(password, user.password)))
    return res.status(401).json({ message: "Invalid credentials" });

  const token = jwt.sign({ email }, process.env.JWT_SECRET, { expiresIn: "1h" });

  res.json({ token });
});
Enter fullscreen mode Exit fullscreen mode

C. Protect Routes with JWT Middleware

const authenticateJWT = (req, res, next) => {
  const token = req.header("Authorization");
  if (!token) return res.status(403).json({ message: "Access denied" });

  try {
    const decoded = jwt.verify(token.split(" ")[1], process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ message: "Invalid token" });
  }
};

// Example protected route
router.get("/dashboard", authenticateJWT, (req, res) => {
  res.json({ message: "Welcome to the dashboard!" });
});
Enter fullscreen mode Exit fullscreen mode

βœ… Now, users need a valid JWT to access protected routes.

  1. Implementing OAuth for Third-Party Authentication

Why Use OAuth?

βœ” Improves UX (No password required)
βœ” More secure (Uses short-lived tokens)
βœ” **Integrates with Google, GitHub, Facebook, etc.)

OAuth Flow (Example: Google Authentication)

1️⃣ User clicks "Login with Google"
2️⃣ Redirected to Google’s OAuth page
3️⃣ Google verifies and redirects back with an authorization code
4️⃣ Backend exchanges the code for an access token
5️⃣ App uses the token to fetch user data

Step 1: Install Dependencies

npm install passport passport-google-oauth20 express-session
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure OAuth Strategy (oauth.js)

const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20").Strategy;

passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: "/auth/google/callback",
    },
    (accessToken, refreshToken, profile, done) => {
      return done(null, profile);
    }
  )
);

// Serialize & Deserialize user
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((obj, done) => done(null, obj));
Enter fullscreen mode Exit fullscreen mode

Step 3: Setup Routes (auth.js)

const express = require("express");
const passport = require("passport");

const router = express.Router();

router.get("/google", passport.authenticate("google", { scope: ["profile", "email"] }));

router.get("/google/callback", passport.authenticate("google", { failureRedirect: "/" }), (req, res) => {
  res.redirect("/dashboard");
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

βœ… Now, users can log in with Google OAuth instead of passwords!

  1. Implementing Role-Based Access Control (RBAC)

RBAC ensures fine-grained permissions based on user roles (Admin, Editor, User).

Step 1: Define User Roles (roles.js)

const roles = {
  ADMIN: "admin",
  EDITOR: "editor",
  USER: "user",
};

module.exports = roles;
Enter fullscreen mode Exit fullscreen mode

Step 2: Protect Routes Based on Role

const checkRole = (role) => (req, res, next) => {
  if (req.user.role !== role) return res.status(403).json({ message: "Access denied" });
  next();
};

// Example usage
router.get("/admin", authenticateJWT, checkRole("admin"), (req, res) => {
  res.json({ message: "Welcome, Admin!" });
});
Enter fullscreen mode Exit fullscreen mode

βœ… Only Admins can access this route.

  1. Best Security Practices

βœ… Use HTTPS β†’ Encrypt traffic with SSL
βœ… Limit failed login attempts β†’ Prevent brute-force attacks
βœ… Enable Multi-Factor Authentication (MFA) β†’ Extra security layer
βœ… Rotate JWT secrets periodically
βœ… Use refresh tokens securely β†’ Avoid infinite sessions

Final Thoughts

By combining JWT, OAuth, and RBAC, you can build a secure authentication and authorization system that is scalable, user-friendly, and robust. πŸš€

Key Takeaways:

βœ” JWT β†’ Best for stateless authentication
βœ” OAuth β†’ Ideal for third-party logins (Google, GitHub)
βœ” RBAC β†’ Essential for role-based security

I am open to collaboration on projects and work. Let's transform ideas into digital reality.

WebSecurity #Authentication #Authorization #NodeJS #OAuth #JWT #RBAC

Top comments (0)