{
  "project": {
    "name": "node-express-jwt-api",
    "description": "Minimal Express REST API with JWT authentication",
    "version": "1.0.0"
  },
  "steps": [
    {
      "title": "Prerequisites and stop-here checks",
      "goal": "Confirm the runtime and security assumptions before building.",
      "validation": [
        "node -v",
        "npm -v"
      ],
      "checks": [
        "Use Node.js 18 or later",
        "Use npm for package management",
        "Do not hardcode JWT secrets in source control",
        "Decide where signing secrets come from in each environment"
      ]
    },
    {
      "title": "Prepare the project",
      "goal": "Create the project and install dependencies.",
      "commands": [
        "mkdir node-express-jwt-api",
        "cd node-express-jwt-api",
        "npm init -y",
        "npm install express jsonwebtoken dotenv",
        "npm install -D nodemon"
      ],
      "files": [
        {
          "path": "package.json",
          "content": {
            "scripts": {
              "start": "node src/server.js",
              "dev": "nodemon src/server.js"
            }
          }
        },
        {
          "path": ".env",
          "content": {
            "PORT": "3000",
            "JWT_SECRET": "replace_with_a_long_random_secret",
            "JWT_EXPIRES_IN": "15m"
          }
        }
      ],
      "structure": [
        "src/server.js",
        "src/auth.js",
        "src/middleware/authenticateToken.js"
      ]
    },
    {
      "title": "Build the Express server",
      "goal": "Create the API server and add a health check.",
      "file": {
        "path": "src/server.js",
        "content": "require('dotenv').config();\n\nconst express = require('express');\nconst authRoutes = require('./auth');\nconst authenticateToken = require('./middleware/authenticateToken');\n\nconst app = express();\nconst port = process.env.PORT || 3000;\n\napp.use(express.json());\n\napp.get('/health', (req, res) => {\n  res.status(200).json({ status: 'ok' });\n});\n\napp.use('/auth', authRoutes);\n\napp.get('/profile', authenticateToken, (req, res) => {\n  res.status(200).json({\n    message: 'Protected content',\n    user: req.user\n  });\n});\n\napp.listen(port, () => {\n  console.log(`API listening on port ${port}`);\n});"
      },
      "validation": [
        "npm run dev",
        "curl http://localhost:3000/health"
      ],
      "expected_response": {
        "status": "ok"
      }
    },
    {
      "title": "Implement token issuance",
      "goal": "Add a login endpoint that returns a signed JWT on success.",
      "file": {
        "path": "src/auth.js",
        "content": "const express = require('express');\nconst jwt = require('jsonwebtoken');\n\nconst router = express.Router();\n\nconst demoUser = {\n  id: 1,\n  username: 'admin',\n  password: 'change-me'\n};\n\nrouter.post('/login', (req, res) => {\n  const { username, password } = req.body;\n\n  if (username !== demoUser.username || password !== demoUser.password) {\n    return res.status(401).json({ message: 'Invalid credentials' });\n  }\n\n  const payload = {\n    sub: demoUser.id,\n    username: demoUser.username\n  };\n\n  const token = jwt.sign(payload, process.env.JWT_SECRET, {\n    expiresIn: process.env.JWT_EXPIRES_IN || '15m'\n  });\n\n  return res.status(200).json({\n    accessToken: token\n  });\n});\n\nmodule.exports = router;"
      },
      "validation": [
        "curl -X POST http://localhost:3000/auth/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"change-me\"}'"
      ],
      "expected_response": {
        "accessToken": "<encoded-jwt>"
      }
    },
    {
      "title": "Protect routes with JWT middleware",
      "goal": "Verify bearer tokens and reject unauthenticated requests.",
      "file": {
        "path": "src/middleware/authenticateToken.js",
        "content": "const jwt = require('jsonwebtoken');\n\nfunction authenticateToken(req, res, next) {\n  const authHeader = req.headers.authorization;\n  const token = authHeader && authHeader.startsWith('Bearer ')\n    ? authHeader.slice(7)\n    : null;\n\n  if (!token) {\n    return res.status(401).json({ message: 'Missing bearer token' });\n  }\n\n  try {\n    const decoded = jwt.verify(token, process.env.JWT_SECRET);\n    req.user = decoded;\n    return next();\n  } catch (error) {\n    return res.status(401).json({ message: 'Invalid or expired token' });\n  }\n}\n\nmodule.exports = authenticateToken;"
      },
      "validation": [
        "curl http://localhost:3000/profile",
        "curl http://localhost:3000/profile -H \"Authorization: Bearer YOUR_TOKEN_HERE\""
      ],
      "expected_response_without_token": {
        "message": "Missing bearer token"
      },
      "expected_response_with_token": {
        "message": "Protected content",
        "user": {
          "sub": 1,
          "username": "admin"
        }
      }
    },
    {
      "title": "Decide what belongs in the token",
      "goal": "Keep JWT payloads small and safe to expose to clients.",
      "rules": [
        "Include only claims needed for request processing",
        "Avoid passwords, secrets, and unnecessary personal data",
        "Use minimal roles or tenant claims when required",
        "Document how downstream services validate those claims"
      ]
    },
    {
      "title": "Production-readiness checks",
      "goal": "Confirm the implementation is safe to extend toward production.",
      "checks": [
        "JWT secret comes from environment-specific configuration",
        "Token expiry is set and reviewed",
        "Credential checks use a real identity source in non-demo deployments",
        "Sensitive errors are not exposed to clients",
        "The API has a clear path for rate limiting, rotation, and authorization rules"
      ]
    }
  ],
  "quick_test_plan": [
    {
      "name": "Health check",
      "command": "curl http://localhost:3000/health",
      "expected": "{\"status\":\"ok\"}"
    },
    {
      "name": "Login",
      "command": "curl -X POST http://localhost:3000/auth/login -H 'Content-Type: application/json' -d '{\"username\":\"admin\",\"password\":\"change-me\"}'",
      "expected": "Response includes accessToken"
    },
    {
      "name": "Protected route without token",
      "command": "curl http://localhost:3000/profile",
      "expected": "{\"message\":\"Missing bearer token\"}"
    },
    {
      "name": "Protected route with token",
      "command": "curl http://localhost:3000/profile -H \"Authorization: Bearer YOUR_TOKEN_HERE\"",
      "expected": "Response includes message and decoded user claims"
    }
  ],
  "notes": [
    "This resource is intentionally minimal and suitable for local development and extension.",
    "Replace the demo login with real credential verification before production use.",
    "Use a long, random JWT secret and keep it out of version control."
  ]
}