{"id":24711755,"url":"https://github.com/sunthecoder/prep","last_synced_at":"2026-04-07T22:31:54.849Z","repository":{"id":272845767,"uuid":"917907834","full_name":"SunTheCoder/prep","owner":"SunTheCoder","description":null,"archived":false,"fork":false,"pushed_at":"2025-01-17T15:15:36.000Z","size":91,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-11T08:07:19.242Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SunTheCoder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-16T21:41:28.000Z","updated_at":"2025-01-17T15:15:37.000Z","dependencies_parsed_at":"2025-06-06T19:46:16.555Z","dependency_job_id":null,"html_url":"https://github.com/SunTheCoder/prep","commit_stats":null,"previous_names":["sunthecoder/prep"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SunTheCoder/prep","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SunTheCoder%2Fprep","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SunTheCoder%2Fprep/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SunTheCoder%2Fprep/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SunTheCoder%2Fprep/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SunTheCoder","download_url":"https://codeload.github.com/SunTheCoder/prep/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SunTheCoder%2Fprep/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31532228,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"ssl_error","status_checked_at":"2026-04-07T16:28:06.951Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-27T07:15:50.708Z","updated_at":"2026-04-07T22:31:54.834Z","avatar_url":"https://github.com/SunTheCoder.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# prep\n### **Authentication Controller Documentation**\nThis document explains how the authentication system works in our **TypeScript Express API**, covering **user registration and login** using **Prisma (PostgreSQL), JWT, and bcrypt**.\n\n---\n\n## **🔹 Overview**\nThis module provides authentication functionalities:\n- **User Registration** (`/register`)\n- **User Login** (`/login`)\n- **JWT Token-Based Authentication**\n- **Secure Password Hashing**\n- **Manual Input Validation**\n\n---\n\n## **🔹 Dependencies Used**\n| Package        | Purpose |\n|---------------|---------|\n| `express`     | Web framework for Node.js |\n| `bcryptjs`    | Hashing passwords securely |\n| `jsonwebtoken` | Generating \u0026 verifying JWT tokens |\n| `@prisma/client` | ORM for PostgreSQL database |\n| `cookie-parser` | Storing JWT in secure cookies (optional) |\n\n---\n\n## **🔹 Environment Variables**\n| Variable       | Description |\n|---------------|-------------|\n| `JWT_SECRET`  | Secret key for signing JWT tokens |\n| `DATABASE_URL` | PostgreSQL connection string |\n\n📌 **Ensure you define these in your `.env` file** before running the server.\n\n---\n\n# **1️⃣ Register User**\n### **🔹 Endpoint:**\n```http\nPOST /api/auth/register\n```\n### **🔹 Request Body (JSON):**\n```json\n{\n  \"name\": \"John Doe\",\n  \"email\": \"john.doe@example.com\",\n  \"password\": \"securepassword\"\n}\n```\n### **🔹 Response (Success)**\n```json\n{\n  \"message\": \"User registered successfully\",\n  \"userId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}\n```\n### **🔹 Response (Errors)**\n| Status Code | Message |\n|------------|----------|\n| `400` | Name must be at least 2 characters |\n| `400` | Invalid email format |\n| `400` | Password must be at least 6 characters |\n| `500` | Internal Server Error |\n\n### **🔹 Implementation**\n```ts\nexport const register = async (req: Request, res: Response, next: NextFunction) =\u003e {\n  try {\n    const { name, email, password } = req.body;\n\n    // Manual validation\n    if (!name || name.length \u003c 2) {\n      return res.status(400).json({ message: \"Name must be at least 2 characters\" });\n    }\n    if (!email || !email.includes(\"@\") || !email.includes(\".\")) {\n      return res.status(400).json({ message: \"Invalid email format\" });\n    }\n    if (!password || password.length \u003c 6) {\n      return res.status(400).json({ message: \"Password must be at least 6 characters\" });\n    }\n\n    // Hash password before storing in DB\n    const hashedPassword = await bcrypt.hash(password, 10);\n\n    // Create user in PostgreSQL using Prisma\n    const user = await prisma.user.create({\n      data: { name, email, password: hashedPassword },\n    });\n\n    res.status(201).json({ message: \"User registered successfully\", userId: user.id });\n  } catch (error) {\n    console.error(\"Error registering user:\", error);\n    res.status(500).json({ message: \"Internal Server Error\" });\n  }\n};\n```\n\n---\n\n# **2️⃣ Login User**\n### **🔹 Endpoint:**\n```http\nPOST /api/auth/login\n```\n### **🔹 Request Body (JSON):**\n```json\n{\n  \"email\": \"john.doe@example.com\",\n  \"password\": \"securepassword\"\n}\n```\n### **🔹 Response (Success)**\n```json\n{\n  \"message\": \"Login successful\",\n  \"token\": \"eyJhbGciOiJIUzI1...\"\n}\n```\n### **🔹 Response (Errors)**\n| Status Code | Message |\n|------------|----------|\n| `400` | Invalid email format |\n| `400` | Password must be at least 6 characters |\n| `401` | Invalid credentials |\n| `500` | Internal Server Error |\n\n### **🔹 Implementation**\n```ts\nexport const login = async (req: Request, res: Response, next: NextFunction) =\u003e {\n  try {\n    const { email, password } = req.body;\n\n    // Manual validation\n    if (!email || !email.includes(\"@\") || !email.includes(\".\")) {\n      return res.status(400).json({ message: \"Invalid email format\" });\n    }\n    if (!password || password.length \u003c 6) {\n      return res.status(400).json({ message: \"Password must be at least 6 characters\" });\n    }\n\n    // Find user by email\n    const user = await prisma.user.findUnique({ where: { email } });\n\n    if (!user || !(await bcrypt.compare(password, user.password))) {\n      return res.status(401).json({ message: \"Invalid credentials\" });\n    }\n\n    // Generate JWT token\n    const token = jwt.sign({ id: user.id }, JWT_SECRET!, { expiresIn: \"1h\" });\n\n    // Secure cookie storage\n    res.cookie(\"token\", token, { httpOnly: true, secure: true, sameSite: \"strict\" });\n    res.json({ message: \"Login successful\", token });\n  } catch (error) {\n    console.error(\"Error logging in:\", error);\n    res.status(500).json({ message: \"Internal Server Error\" });\n  }\n};\n```\n\n---\n\n# **3️⃣ JWT-Based Authentication**\n### **How JWT Works**\n1. **User Logs In** → Receives a **JWT token**.\n2. **Token is Sent in Requests** → Stored in **cookies** or sent via `Authorization` headers.\n3. **Protected Routes Verify Token** → Only authenticated users can access them.\n\n### **🔹 Middleware for Protecting Routes**\n```ts\nimport { Request, Response, NextFunction } from \"express\";\nimport jwt from \"jsonwebtoken\";\n\nconst JWT_SECRET = process.env.JWT_SECRET!;\n\ninterface AuthenticatedRequest extends Request {\n  user?: { id: string };\n}\n\nexport const authMiddleware = (req: AuthenticatedRequest, res: Response, next: NextFunction) =\u003e {\n  const token = req.cookies?.token || req.headers.authorization?.split(\" \")[1];\n\n  if (!token) {\n    return res.status(401).json({ message: \"Unauthorized: No token provided\" });\n  }\n\n  try {\n    const decoded = jwt.verify(token, JWT_SECRET) as { id: string };\n    req.user = decoded;\n    next();\n  } catch {\n    res.status(401).json({ message: \"Invalid token\" });\n  }\n};\n```\n\n---\n\n# **4️⃣ Logout User**\n### **🔹 Endpoint:**\n```http\nPOST /api/auth/logout\n```\n### **🔹 Response**\n```json\n{\n  \"message\": \"Logout successful\"\n}\n```\n\n### **🔹 Implementation**\n```ts\nexport const logout = async (req: Request, res: Response) =\u003e {\n  res.clearCookie(\"token\", { httpOnly: true, secure: true, sameSite: \"strict\" });\n  res.json({ message: \"Logout successful\" });\n};\n```\n\n---\n\n# **🔹 Summary of API Routes**\n| HTTP Method | Endpoint | Description | Middleware |\n|------------|----------|-------------|------------|\n| `POST` | `/api/auth/register` | Register a new user | None |\n| `POST` | `/api/auth/login` | Authenticate user \u0026 return JWT | None |\n| `POST` | `/api/auth/logout` | Clear authentication token | None |\n| `GET` | `/api/protected` | Example protected route | `authMiddleware` |\n\n---\n\n\n✅ What You Mastered\nTypeScript Express Backend\nJWT Authentication \u0026 Secure Cookies\nUser Registration, Login, and Deletion\nRedux Toolkit for Authentication Management\nFetching and Managing Users in React\nDebugging CORS \u0026 API Route Issues\nProper Backend \u0026 Frontend Integration\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunthecoder%2Fprep","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunthecoder%2Fprep","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunthecoder%2Fprep/lists"}