DEV Community

Aisalkyn Aidarova
Aisalkyn Aidarova

Posted on

LAB 3 β€” JWT + Secrets + CI/CD + Newman (PRODUCTION LEVEL)

πŸ”₯ 1. ARCHITECTURE (REAL FLOW)

Client β†’ /login β†’ JWT token
       β†’ /protected β†’ validate token
       β†’ Newman tests β†’ CI/CD validation
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 2. PROJECT STRUCTURE

jwt-newman-lab/
β”œβ”€β”€ app/
β”‚   └── server.js
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ package.json
β”œβ”€β”€ collection.json
β”œβ”€β”€ environment.json
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       └── api-test.yml
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 3. STEP 1 β€” SECURE API WITH JWT

πŸ“„ app/server.js

const express = require("express");
const jwt = require("jsonwebtoken");

const app = express();
app.use(express.json());

// πŸ”’ Secret from ENV (IMPORTANT)
const SECRET = process.env.JWT_SECRET || "dev-secret";

// LOGIN
app.post("/login", (req, res) => {
    const { username } = req.body;

    if (!username) {
        return res.status(400).json({ error: "Username required" });
    }

    const token = jwt.sign({ username }, SECRET, { expiresIn: "1h" });

    res.json({ token });
});

// PROTECTED ROUTE
app.get("/protected", (req, res) => {
    const auth = req.headers["authorization"];

    if (!auth) {
        return res.status(401).json({ error: "No token" });
    }

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

    try {
        const decoded = jwt.verify(token, SECRET);
        res.json({ message: "Access granted", user: decoded.username });
    } catch (err) {
        res.status(403).json({ error: "Invalid token" });
    }
});

app.listen(3000, () => {
    console.log("JWT API running on 3000");
});
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 4. STEP 2 β€” PACKAGE.JSON

{
  "name": "jwt-api",
  "version": "1.0.0",
  "main": "app/server.js",
  "scripts": {
    "start": "node app/server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 5. STEP 3 β€” DOCKERFILE

FROM node:18

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 6. STEP 4 β€” POSTMAN COLLECTION (IMPORTANT)

Request 1 β€” LOGIN

POST {{base_url}}/login
Enter fullscreen mode Exit fullscreen mode

Body:

{
  "username": "aisalkyn"
}
Enter fullscreen mode Exit fullscreen mode

Tests (SAVE TOKEN)

let json = pm.response.json();
pm.environment.set("token", json.token);

pm.test("Token received", function () {
    pm.expect(json.token).to.exist;
});
Enter fullscreen mode Exit fullscreen mode

Request 2 β€” PROTECTED

GET {{base_url}}/protected
Enter fullscreen mode Exit fullscreen mode

Headers:

Authorization: Bearer {{token}}
Enter fullscreen mode Exit fullscreen mode

Tests

pm.test("Access granted", function () {
    pm.response.to.have.status(200);
});
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 7. STEP 5 β€” ENVIRONMENT FILE

{
  "name": "local",
  "values": [
    { "key": "base_url", "value": "http://localhost:3000" },
    { "key": "token", "value": "" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 8. STEP 6 β€” RUN LOCALLY

docker build -t jwt-api .
docker run -d -p 3000:3000 -e JWT_SECRET=mysecret jwt-api

newman run collection.json -e environment.json
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 9. STEP 7 β€” GITHUB ACTIONS (REAL CI/CD)

πŸ“„ .github/workflows/api-test.yml

name: JWT API Test

on:
  push:
    branches: [ main ]

jobs:
  test-api:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Build & Run API
        run: |
          docker build -t jwt-api .
          docker run -d -p 3000:3000 -e JWT_SECRET=${{ secrets.JWT_SECRET }} jwt-api

      - name: Wait for API
        run: sleep 10

      - name: Install Newman
        run: npm install -g newman

      - name: Run Tests
        run: newman run collection.json -e environment.json
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 10. πŸ” SECRETS (VERY IMPORTANT)

In GitHub:

Settings β†’ Secrets β†’ Actions
Enter fullscreen mode Exit fullscreen mode

Add:

JWT_SECRET = mysupersecret
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 11. WHY THIS IS REAL DEVOPS

You are doing:

  • Secure API (JWT)
  • No hardcoding secrets
  • Pipeline validation
  • Auth testing
  • Fail-safe deployment

πŸ”₯ 12. COMMON FAILURES (INTERVIEW GOLD)

❌ Token not saved

Newman fails because:

{{token}} is empty
Enter fullscreen mode Exit fullscreen mode

❌ Wrong secret

Token created β‰  token verified


❌ Missing Authorization header

401 Unauthorized
Enter fullscreen mode Exit fullscreen mode

❌ Pipeline too fast

API not ready β†’ tests fail


❌ Secrets not configured

GitHub:

JWT_SECRET undefined
Enter fullscreen mode Exit fullscreen mode

πŸ”₯ 13. WHAT YOU SAY IN INTERVIEW

Strong answer:

"We test authenticated APIs using Postman and Newman. Token is dynamically generated and stored during test execution. In CI/CD, secrets are injected securely using GitHub Actions. This ensures secure and automated API validation before release."


πŸ”₯ 14. WHAT YOU REPORT TO MANAGER

Example:

  • Auth Test: PASS
  • Protected Endpoint: PASS
  • Token Validation: PASS
  • Security: JWT verified
  • Failures: 0

πŸ”₯ 15. REAL PRODUCTION PIPELINE

Code Push
   ↓
Build Docker Image
   ↓
Deploy (ECS / Kubernetes)
   ↓
Run Newman (Auth + API tests)
   ↓
IF FAIL β†’ rollback
IF PASS β†’ production
Enter fullscreen mode Exit fullscreen mode

-

Top comments (0)