Build JWT Protected Routes - Build JWT Protected Routes

Tell us what’s happening:

Build JWT Protected Routes - Lesson 35, I have completed the assignment but I can’t get test 6 to pass when running the final set of tests.

Tests:

    1. ✓

    POST /api/auth/register should respond with status 201 and a token.

    1. ✓

    POST /api/auth/login should respond with status 200 and a token for valid credentials.

    1. ✓

    GET /api/auth/profile without a token should respond with status 401.

    1. ✓

    GET /api/auth/profile with a valid token should respond with status 200 and the user.

    1. ✓

    GET /api/admin/users with a normal user’s token should respond with status 403.

    1. ✗

    GET /api/admin/users with an admin token should respond with 200 and users that omit passwordHash.

    1. ✓

    POST /api/auth/logout should invalidate the token so reusing it responds with 401.

Console:

{
  "message": "An admin token should access /api/admin/users with status 200.: expected 401 to equal 200",
  "showDiff": true,
  "actual": 401,
  "expected": 200,
  "operator": "strictEqual",
  "stack": "AssertionError: An admin token should access /api/admin/users with status 200.: expected 401 to equal 200\n    at eval (eval at <anonymous> (file:///workspaces/back-end-development-and-apis/node_modules/@freecodecamp/freecodecamp-os/.freeCodeCamp/tooling/tests/test-worker.js:25:29), <anonymous>:26:8)\n    at process.processTicksAndRejections (node:internal/process/task_queues:104:5)\n    at async MessagePort.<anonymous> (file:///workspaces/back-end-development-and-apis/node_modules/@freecodecamp/freecodecamp-os/.freeCodeCamp/tooling/tests/test-worker.js:25:23)",
  "type": "AssertionError"
}

Your code so far

-------------------index.js------------------

import express from "express";
import helmet from "helmet";
import authroutes from "./routes/auth.js";
import adminRoutes from "./routes/admin.js";

const PORT = process.env.PORT;

const app = express();

app.use(helmet());
app.use(express.json());

app.get("/", (req, res) => {
  res.json({ message: "Auth API is running" });
});

app.use("/api/auth", authroutes);

app.use("/api/admin", adminRoutes);

app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({
    "error": err.message
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}...`);
});
/Routes

-------------------- admin.js. ------------------

import express from "express";
import authenticate from "../middleware/authenticate.js";
import authorizeRole from "../middleware/authorize.js";
import { readUsers } from "../utils/db.js";

const router = express.Router();

router.get("/users", authenticate, authorizeRole("admin"), (req, res) => {
    const users = readUsers().map(({ passwordHash, ...rest }) => rest);
    return res.status(200).json(users);
});

export default router;

--------------------auth.js---------------

import express from "express";
import bcrypt from "bcryptjs";
import { randomUUID } from "crypto";
import { findByEmail, readUsers, writeUsers } from "../utils/db.js";
import { signToken } from "../utils/jwt.js";
import authenticate from "../middleware/authenticate.js";
import { blacklistToken } from "../utils/token-blacklist.js";



const router = express.Router();

router.post("/register", async (req, res) => {

    const { email, password } = req.body;
    if (!email || !password) {
        return res.status(400).json({ message: "Email and password are required" });
    }
    if (findByEmail(email) != null) {
        return res.status(409).json({ message: "Email already in use" });
    }
    const passwordHash = await bcrypt.hash(password, 10);
    const newUser = {
        id: randomUUID(),
        email,
        passwordHash,
        role: "user"
    };
    const users = readUsers();
    users.push(newUser);
    writeUsers(users);

    const token = signToken({ id: newUser.id, email: newUser.email, role: newUser.role });
    res.status(201).json({ message: "User registered successfully.", token });

});

router.post("/login", async (req, res) => {

    const { email, password } = req.body;
    if (!email || !password) {
        return res.status(400).json({ message: "Email and password required." });
    }
    const user = findByEmail(email);
    if (user === null) {
        return res.status(401).json({ message: "Invalid credentials" });
    }
    const match = await bcrypt.compare(password, user.passwordHash);
    if (!match) return res.status(401).json({ message: "Invalid credentials" });

    const token = signToken({ id: user.id, email: user.email, role: user.role });
    res.status(200).json({ message: "login sucessful", token });

});

router.get("/profile", authenticate, (req, res) => {
    res.status(200).json({ user: req.user });

});

router.post("/logout", authenticate, (req, res) => {
    const token = req.headers.authorization.split(" ")[1];
    blacklistToken(token);
    res.status(200).json({ message: "Logged out successfully" });

});

export default router;
/Middleware

-----------------authenticate.js --------------

import { verifyToken } from "../utils/jwt.js";
import { isBlacklisted } from "../utils/token-blacklist.js";

export default function authenticate(req, res, next) {
    const authentication = req.headers.authorization || req.headers.Authorization;

    if (!authentication || !authentication.startsWith("Bearer ")) {
        return res.status(401).json({ message: "No token provided" });
    }

    const token = authentication.split(" ")[1];

    if (isBlacklisted(token)) {
        return res.status(401).json({ message: "Token has been invalidated. Log in again." });
    }
    
    const payload = verifyToken(token);

    if (!payload) {
        return res.status(401).json({ message: "Invalid or expired token" });
    }

    req.user = payload;
    next();
};

------------authorize.js---------------

export default function authorizeRole(role) {
    return (req, res, next) => {
        if (!req.user || req.user.role !== role) {
            return res.status(403).json({ message: "Access denied" });
        }
        next();
    };
};
/Utils

------------------db.js------------------

import path from "path";
import fs from "fs";

const DB_PATH = path.join(import.meta.dirname, "../data/users.json");

export function readUsers() {
    const users = fs.readFileSync(DB_PATH, "utf-8").trim();
    if (users) return JSON.parse(users);
    else return [];
}

export function writeUsers(users) {
    fs.writeFileSync(DB_PATH, JSON.stringify(users, null, 2));
}

export function findByEmail(email) {
    return readUsers().find((u) => u.email === email) || null;
}

export function findById(id) {
    return readUsers().find(u => u.id === id) || null;
}

------------------jwt.js-----------------

import jwt from "jsonwebtoken";

export function signToken(payload) {
    return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1d" });
}

export function verifyToken(token) {
    try {
        const payload = jwt.verify(token, process.env.JWT_SECRET);
        return payload;
    } catch (err) {
        return null;
    }
}

--------------token-blacklist.js-------------

const blacklist = new Set();

export function blacklistToken(token) {
    blacklist.add(token);
}

export function isBlacklisted(token) {
    return blacklist.has(token);
}

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X __) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/...* Safari/***

Challenge Information:

Build JWT Protected Routes - Build JWT Protected Routes

GitHub Link: freeCodeCamp/curriculum/challenges/english/blocks/workshop-jwt-protected-routes/6a55c4b3102207cfc2491584.md at main · freeCodeCamp/freeCodeCamp · GitHub

I also have the similar problem, even done the manual testing it works.
I think it’s not working because the code hard-sets the user.role = “user” for every user who want to register.

I even checked the users.json file, after running the test, there are lot of users, but none of them have role = “admin”. Therefore, no matter what, it will never give output for the Test 6, because it fails at authorizeRole(“admin”) middleware.

@moderators @ILM please fix.

Welcome to the forum @engrsubhashodhano,

Thank you for helping make FCC better. Bugs can be reported as GitHub Issues. Whenever reporting a bug, please check first that there isn’t already an issue for it and provide as much detail as possible.

Happy coding

see if this issue seems to match your experience

if so please update your .env file and try again