From d318de7b98a2f1feae06d1b7dd8660c52ba80985 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Fri, 13 Dec 2024 22:42:45 -0600 Subject: [PATCH 01/17] feat:user-auth-api --- src/services/userauthentication.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/services/userauthentication.tsx diff --git a/src/services/userauthentication.tsx b/src/services/userauthentication.tsx new file mode 100644 index 0000000..e69de29 From 4fdfbeb8b24816a7ef548cd1d76dfaee9b39b5f8 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Mon, 16 Dec 2024 22:08:03 -0600 Subject: [PATCH 02/17] Ft: auth method api --- src/entities/User.ts | 19 ++++++++++++ src/middleware/auth.middleware.ts | 48 +++++++++++++++++++++++++++++ src/services/auth.service.tsx | 43 ++++++++++++++++++++++++++ src/services/userauthentication.tsx | 0 4 files changed, 110 insertions(+) create mode 100644 src/entities/User.ts create mode 100644 src/middleware/auth.middleware.ts create mode 100644 src/services/auth.service.tsx delete mode 100644 src/services/userauthentication.tsx diff --git a/src/entities/User.ts b/src/entities/User.ts new file mode 100644 index 0000000..9f4877d --- /dev/null +++ b/src/entities/User.ts @@ -0,0 +1,19 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm"; + +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number; + + @Column({ unique: true }) + walletAddress: string; + + @Column({ default: 'user' }) + role: string; + + @CreateDateColumn() + createdAt: Date; + + @UpdateDateColumn() + updatedAt: Date; +} \ No newline at end of file diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts new file mode 100644 index 0000000..4aaec48 --- /dev/null +++ b/src/middleware/auth.middleware.ts @@ -0,0 +1,48 @@ +import { Request, Response, NextFunction } from 'express'; +import { AuthService } from '../services/auth.service'; +import { UnauthorizedError } from '../utils/errors'; + +export interface AuthenticatedRequest extends Request { + user?: { + userId: number; + walletAddress: string; + role: string; + }; +} + +export const authMiddleware = async ( + req: AuthenticatedRequest, + res: Response, + next: NextFunction +) => { + try { + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith('Bearer ')) { + throw new UnauthorizedError('No token provided'); + } + + const token = authHeader.split(' ')[1]; + const decoded = AuthService.verifyToken(token); + + req.user = { + userId: decoded.userId, + walletAddress: decoded.walletAddress, + role: decoded.role + }; + + next(); + } catch (error) { + next(error); + } +}; + +// Optional: Role-based authentication middleware +export const requireRole = (role: string) => { + return (req: AuthenticatedRequest, res: Response, next: NextFunction) => { + if (!req.user || req.user.role !== role) { + throw new UnauthorizedError('Insufficient permissions'); + } + next(); + }; +}; \ No newline at end of file diff --git a/src/services/auth.service.tsx b/src/services/auth.service.tsx new file mode 100644 index 0000000..813f394 --- /dev/null +++ b/src/services/auth.service.tsx @@ -0,0 +1,43 @@ +import jwt from 'jsonwebtoken'; +import { AppDataSource } from '../config/data-source'; +import { User } from '../entities/User'; +import { UnauthorizedError } from '../utils/errors'; + +export class AuthService { + private static readonly JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; + private static readonly JWT_EXPIRES_IN = '24h'; + + static async authenticateUser(walletAddress: string): Promise { + const userRepository = AppDataSource.getRepository(User); + + // Find or create user + let user = await userRepository.findOne({ where: { walletAddress } }); + + if (!user) { + // Create new user if doesn't exist + user = userRepository.create({ walletAddress }); + await userRepository.save(user); + } + + // Generate JWT token + const token = jwt.sign( + { + userId: user.id, + walletAddress: user.walletAddress, + role: user.role + }, + this.JWT_SECRET, + { expiresIn: this.JWT_EXPIRES_IN } + ); + + return token; + } + + static verifyToken(token: string): any { + try { + return jwt.verify(token, this.JWT_SECRET); + } catch (error) { + throw new UnauthorizedError('Invalid token'); + } + } +} \ No newline at end of file diff --git a/src/services/userauthentication.tsx b/src/services/userauthentication.tsx deleted file mode 100644 index e69de29..0000000 From c8c0c9ea6bb779fa4319b9cca95e58f4961f324f Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Wed, 18 Dec 2024 17:10:52 -0600 Subject: [PATCH 03/17] Feat: Auth Method API --- package.json | 3 +++ src/controllers/auth.controlers.ts | 19 +++++++++++++++++++ src/middleware/auth.middleware.ts | 7 +++---- src/route/protected.routes.ts | 15 +++++++++++++++ .../{auth.service.tsx => auth.service.ts} | 9 +++------ 5 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 src/controllers/auth.controlers.ts create mode 100644 src/route/protected.routes.ts rename src/services/{auth.service.tsx => auth.service.ts} (79%) diff --git a/package.json b/package.json index e0d8596..e7a2d0c 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "license": "ISC", "description": "", "dependencies": { + "dotenv": "^16.4.7", "express": "^4.21.1", + "jsonwebtoken": "^9.0.2", "pg": "^8.13.1", "reflect-metadata": "^0.2.2", "typeorm": "^0.3.20" @@ -23,6 +25,7 @@ "@nestjs/testing": "^10.4.13", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", + "@types/jsonwebtoken": "^9.0.7", "@types/node": "^22.10.1", "@types/supertest": "^6.0.2", "jest": "^29.7.0", diff --git a/src/controllers/auth.controlers.ts b/src/controllers/auth.controlers.ts new file mode 100644 index 0000000..1872271 --- /dev/null +++ b/src/controllers/auth.controlers.ts @@ -0,0 +1,19 @@ +import { Request, Response, NextFunction } from 'express'; +import { AuthService } from '../services/auth.service'; + +export class AuthController { + static async login(req: Request, res: Response, next: NextFunction) { + try { + const { walletAddress } = req.body; + + if (!walletAddress) { + throw new Error('Wallet address is required'); + } + + const token = await AuthService.authenticateUser(walletAddress); + res.json({ token }); + } catch (error) { + next(error); + } + } +} \ No newline at end of file diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts index 4aaec48..0ee9866 100644 --- a/src/middleware/auth.middleware.ts +++ b/src/middleware/auth.middleware.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from 'express'; import { AuthService } from '../services/auth.service'; -import { UnauthorizedError } from '../utils/errors'; +//import { UnauthorizedError } from '../utils/errors'; export interface AuthenticatedRequest extends Request { user?: { @@ -19,7 +19,7 @@ export const authMiddleware = async ( const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new UnauthorizedError('No token provided'); + throw new ReferenceError('No token provided'); } const token = authHeader.split(' ')[1]; @@ -37,11 +37,10 @@ export const authMiddleware = async ( } }; -// Optional: Role-based authentication middleware export const requireRole = (role: string) => { return (req: AuthenticatedRequest, res: Response, next: NextFunction) => { if (!req.user || req.user.role !== role) { - throw new UnauthorizedError('Insufficient permissions'); + throw new ReferenceError('Insufficient permissions'); } next(); }; diff --git a/src/route/protected.routes.ts b/src/route/protected.routes.ts new file mode 100644 index 0000000..55f45c3 --- /dev/null +++ b/src/route/protected.routes.ts @@ -0,0 +1,15 @@ +import { Router } from 'express'; +import { authMiddleware, requireRole } from '../middleware/auth.middleware'; + +const router = Router(); + +// Protected routes +router.get('/', authMiddleware, (req, res) => { + res.json({ message: 'Protected route accessed' }); +}); + +router.get('/admin', authMiddleware, requireRole('admin'), (req, res) => { + res.json({ message: 'Admin route accessed' }); +}); + +export default router; \ No newline at end of file diff --git a/src/services/auth.service.tsx b/src/services/auth.service.ts similarity index 79% rename from src/services/auth.service.tsx rename to src/services/auth.service.ts index 813f394..b4686e6 100644 --- a/src/services/auth.service.tsx +++ b/src/services/auth.service.ts @@ -1,7 +1,7 @@ import jwt from 'jsonwebtoken'; -import { AppDataSource } from '../config/data-source'; +import AppDataSource from '../config/ormconfig'; import { User } from '../entities/User'; -import { UnauthorizedError } from '../utils/errors'; +//import { UnauthorizedError } from '../utils/errors'; export class AuthService { private static readonly JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; @@ -10,16 +10,13 @@ export class AuthService { static async authenticateUser(walletAddress: string): Promise { const userRepository = AppDataSource.getRepository(User); - // Find or create user let user = await userRepository.findOne({ where: { walletAddress } }); if (!user) { - // Create new user if doesn't exist user = userRepository.create({ walletAddress }); await userRepository.save(user); } - // Generate JWT token const token = jwt.sign( { userId: user.id, @@ -37,7 +34,7 @@ export class AuthService { try { return jwt.verify(token, this.JWT_SECRET); } catch (error) { - throw new UnauthorizedError('Invalid token'); + throw new ReferenceError('Invalid token'); } } } \ No newline at end of file From ffd416c5099ced2ebcf003092a2ba94f3e0b5503 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Thu, 19 Dec 2024 00:42:35 -0600 Subject: [PATCH 04/17] Feat: Auth Method API --- .env.example | 10 ---------- src/utils/errors.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 10 deletions(-) delete mode 100644 .env.example create mode 100644 src/utils/errors.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index bfc59b4..0000000 --- a/.env.example +++ /dev/null @@ -1,10 +0,0 @@ -# Database Configuration -DATABASE_URL="" - - -# Application Configuration -PORT=3000 # Port to run the server - -# Other Configuration -NODE_ENV=development # Node environment (development, production) -JWT_SECRET=your-secret-key # Secret key for JWT authentication diff --git a/src/utils/errors.ts b/src/utils/errors.ts new file mode 100644 index 0000000..7e63f02 --- /dev/null +++ b/src/utils/errors.ts @@ -0,0 +1,35 @@ +export class UnauthorizedError extends Error { + constructor(message: string = 'Unauthorized access') { + super(message); + this.name = 'UnauthorizedError'; + } +} + +export class ValidationError extends Error { + constructor(message: string = 'Validation failed') { + super(message); + this.name = 'ValidationError'; + } +} + +export class NotFoundError extends Error { + constructor(message: string = 'Resource not found') { + super(message); + this.name = 'NotFoundError'; + } +} + +export class BadRequestError extends Error { + constructor(message: string = 'Bad request') { + super(message); + this.name = 'BadRequestError'; + } +} + +// Custom error for handling reference errors in a more structured way +export class ReferenceValidationError extends Error { + constructor(message: string = 'Invalid reference') { + super(message); + this.name = 'ReferenceValidationError'; + } +} \ No newline at end of file From d4c689d3e3bf62f0ae0c05adef6862e708043468 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 01:29:06 -0600 Subject: [PATCH 05/17] Feat/Auth Method API documenting functions --- src/utils/errors.ts | 48 ++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 7e63f02..02c49a6 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,35 +1,51 @@ +// errors.ts - Custom Error Classes + +// UnauthorizedError +// Used when someone tries to access something they're not allowed to +// Like trying to enter a VIP area without a VIP pass export class UnauthorizedError extends Error { constructor(message: string = 'Unauthorized access') { - super(message); - this.name = 'UnauthorizedError'; + super(message); // Sends the message to the parent Error class + this.name = 'UnauthorizedError'; // Gives our error a specific name } -} - -export class ValidationError extends Error { + } + + // ValidationError + // Used when the data provided isn't in the correct format + // Like when someone types letters in a phone number field + export class ValidationError extends Error { constructor(message: string = 'Validation failed') { super(message); this.name = 'ValidationError'; } -} - -export class NotFoundError extends Error { + } + + // NotFoundError + // Used when we try to find something that doesn't exist + // Like trying to find a page that was deleted + export class NotFoundError extends Error { constructor(message: string = 'Resource not found') { super(message); this.name = 'NotFoundError'; } -} - -export class BadRequestError extends Error { + } + + // BadRequestError + // Used when a request is incorrectly formatted + // Like sending an empty form when all fields are required + export class BadRequestError extends Error { constructor(message: string = 'Bad request') { super(message); this.name = 'BadRequestError'; } -} - -// Custom error for handling reference errors in a more structured way -export class ReferenceValidationError extends Error { + } + + // ReferenceValidationError + // Used when a reference to another piece of data is invalid + // Like trying to like a post that doesn't exist anymore + export class ReferenceValidationError extends Error { constructor(message: string = 'Invalid reference') { super(message); this.name = 'ReferenceValidationError'; } -} \ No newline at end of file + } \ No newline at end of file From c029c07920d1f05fec8e16d2e2122a767cccc8e9 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 01:35:08 -0600 Subject: [PATCH 06/17] feat/ auth.service.ts --- src/services/auth.service.ts | 87 ++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index b4686e6..ec93cdd 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,40 +1,61 @@ -import jwt from 'jsonwebtoken'; -import AppDataSource from '../config/ormconfig'; -import { User } from '../entities/User'; -//import { UnauthorizedError } from '../utils/errors'; +// auth.service.ts - Authentication Service + +// Import necessary packages and modules +import jwt from 'jsonwebtoken'; // Used for creating and verifying JWT tokens +import AppDataSource from '../config/ormconfig'; // Database connection +import { User } from '../entities/User'; // User model/entity export class AuthService { - private static readonly JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; - private static readonly JWT_EXPIRES_IN = '24h'; + // Define constants for JWT configuration + // JWT_SECRET is used to sign and verify tokens - loaded from environment variables + private static readonly JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; + // JWT_EXPIRES_IN sets how long the token is valid + private static readonly JWT_EXPIRES_IN = '24h'; - static async authenticateUser(walletAddress: string): Promise { - const userRepository = AppDataSource.getRepository(User); - - let user = await userRepository.findOne({ where: { walletAddress } }); - - if (!user) { - user = userRepository.create({ walletAddress }); - await userRepository.save(user); - } + /** + * Authenticates a user based on their wallet address + * If the user doesn't exist, creates a new user + * Returns a JWT token containing user information + */ + static async authenticateUser(walletAddress: string): Promise { + // Get access to the User table in the database + const userRepository = AppDataSource.getRepository(User); + + // Try to find an existing user with this wallet address + let user = await userRepository.findOne({ where: { walletAddress } }); + + // If no user exists, create a new one + if (!user) { + user = userRepository.create({ walletAddress }); + await userRepository.save(user); + } - const token = jwt.sign( - { - userId: user.id, - walletAddress: user.walletAddress, - role: user.role - }, - this.JWT_SECRET, - { expiresIn: this.JWT_EXPIRES_IN } - ); + // Create a JWT token containing user information + const token = jwt.sign( + { + userId: user.id, // Include user ID in token + walletAddress: user.walletAddress, // Include wallet address + role: user.role // Include user role + }, + this.JWT_SECRET, // Sign with secret key + { expiresIn: this.JWT_EXPIRES_IN } // Set token expiration + ); - return token; - } + return token; + } - static verifyToken(token: string): any { - try { - return jwt.verify(token, this.JWT_SECRET); - } catch (error) { - throw new ReferenceError('Invalid token'); - } - } + /** + * Verifies if a JWT token is valid + * Returns the decoded token information if valid + * Throws an error if the token is invalid + */ + static verifyToken(token: string): any { + try { + // Attempt to verify the token + return jwt.verify(token, this.JWT_SECRET); + } catch (error) { + // If verification fails, throw an error + throw new ReferenceError('Invalid token'); + } + } } \ No newline at end of file From 49bebbf3489c9f3c1c6db2fe0fc5630549841cfb Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 01:38:22 -0600 Subject: [PATCH 07/17] auth.middleware --- src/middleware/auth.middleware.ts | 93 ++++++++++++++++++++----------- src/route/protected.routes.ts | 25 +++++++-- 2 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts index 0ee9866..44a8716 100644 --- a/src/middleware/auth.middleware.ts +++ b/src/middleware/auth.middleware.ts @@ -1,47 +1,74 @@ +// auth.middleware.ts - Authentication Middleware + +// Import necessary types and services import { Request, Response, NextFunction } from 'express'; import { AuthService } from '../services/auth.service'; -//import { UnauthorizedError } from '../utils/errors'; +/** +* Interface for extending the Express Request type +* Adds user information to the request object after authentication +*/ export interface AuthenticatedRequest extends Request { - user?: { - userId: number; - walletAddress: string; - role: string; - }; + user?: { + userId: number; // User's unique identifier + walletAddress: string; // User's blockchain wallet address + role: string; // User's role (e.g., 'user', 'admin') + }; } +/** +* Authentication Middleware +* Checks if the request has a valid JWT token +* If valid, adds user information to the request object +*/ export const authMiddleware = async ( - req: AuthenticatedRequest, - res: Response, - next: NextFunction + req: AuthenticatedRequest, + res: Response, + next: NextFunction ) => { - try { - const authHeader = req.headers.authorization; - - if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new ReferenceError('No token provided'); - } + try { + // Get the authorization header + const authHeader = req.headers.authorization; + + // Check if authorization header exists and has correct format + if (!authHeader || !authHeader.startsWith('Bearer ')) { + throw new ReferenceError('No token provided'); + } - const token = authHeader.split(' ')[1]; - const decoded = AuthService.verifyToken(token); - - req.user = { - userId: decoded.userId, - walletAddress: decoded.walletAddress, - role: decoded.role - }; + // Extract the token from the header + // Format: "Bearer " + const token = authHeader.split(' ')[1]; + + // Verify the token and decode its contents + const decoded = AuthService.verifyToken(token); + + // Add user information to the request object + req.user = { + userId: decoded.userId, + walletAddress: decoded.walletAddress, + role: decoded.role + }; - next(); - } catch (error) { - next(error); - } + // Continue to the next middleware or route handler + next(); + } catch (error) { + // Pass any errors to error handling middleware + next(error); + } }; +/** +* Role-based Access Control Middleware +* Checks if the authenticated user has the required role +* Must be used after authMiddleware +*/ export const requireRole = (role: string) => { - return (req: AuthenticatedRequest, res: Response, next: NextFunction) => { - if (!req.user || req.user.role !== role) { - throw new ReferenceError('Insufficient permissions'); - } - next(); - }; + return (req: AuthenticatedRequest, res: Response, next: NextFunction) => { + // Check if user exists and has the required role + if (!req.user || req.user.role !== role) { + throw new ReferenceError('Insufficient permissions'); + } + // If role matches, continue to next middleware or route handler + next(); + }; }; \ No newline at end of file diff --git a/src/route/protected.routes.ts b/src/route/protected.routes.ts index 55f45c3..2673955 100644 --- a/src/route/protected.routes.ts +++ b/src/route/protected.routes.ts @@ -1,15 +1,30 @@ -import { Router } from 'express'; -import { authMiddleware, requireRole } from '../middleware/auth.middleware'; +// protected.routes.ts - Protected Routes Configuration +// Import necessary packages and middleware +import { Router } from 'express'; // Express Router for handling routes +import { authMiddleware, requireRole } from '../middleware/auth.middleware'; // Authentication middleware + +// Create a new router instance const router = Router(); -// Protected routes +/** +* Protected Route - Basic User Access +* Route: GET / +* This route is protected and requires a valid authentication token +* Any authenticated user can access this route +*/ router.get('/', authMiddleware, (req, res) => { - res.json({ message: 'Protected route accessed' }); + res.json({ message: 'Protected route accessed' }); }); +/** +* Protected Route - Admin Only Access +* Route: GET /admin +* This route requires both authentication and admin role +* Only users with admin role can access this route +*/ router.get('/admin', authMiddleware, requireRole('admin'), (req, res) => { - res.json({ message: 'Admin route accessed' }); + res.json({ message: 'Admin route accessed' }); }); export default router; \ No newline at end of file From 8a71fddb011b3ca0ba7ee9ebdef2d52e9541ced9 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 01:53:39 -0600 Subject: [PATCH 08/17] feat/user --- src/entities/User.ts | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/entities/User.ts b/src/entities/User.ts index 9f4877d..e3a9fa8 100644 --- a/src/entities/User.ts +++ b/src/entities/User.ts @@ -1,19 +1,34 @@ +// user.entity.ts - User Database Model + +// Import decorators from TypeORM to define database structure import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm"; -@Entity() +/** +* User Entity +* Represents the structure of the User table in the database +* Uses TypeORM decorators to define table columns and properties +*/ +@Entity() // Marks this class as a database table export class User { - @PrimaryGeneratedColumn() - id: number; + // Primary key - Auto-incrementing ID number + @PrimaryGeneratedColumn() + id: number; - @Column({ unique: true }) - walletAddress: string; + // User's blockchain wallet address + // unique: true means no two users can have the same address + @Column({ unique: true }) + walletAddress: string; - @Column({ default: 'user' }) - role: string; + // User's role in the system + // default: 'user' means new users automatically get this role + @Column({ default: 'user' }) + role: string; - @CreateDateColumn() - createdAt: Date; + // Automatically tracks when the user record was created + @CreateDateColumn() + createdAt: Date; - @UpdateDateColumn() - updatedAt: Date; + // Automatically updates whenever the user record is modified + @UpdateDateColumn() + updatedAt: Date; } \ No newline at end of file From b89c74fe85c1f7a752d3ab7654326af53f08495b Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 01:58:35 -0600 Subject: [PATCH 09/17] feat/ auth.controlers --- src/controllers/auth.controlers.ts | 44 +++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/controllers/auth.controlers.ts b/src/controllers/auth.controlers.ts index 1872271..7abb748 100644 --- a/src/controllers/auth.controlers.ts +++ b/src/controllers/auth.controlers.ts @@ -1,19 +1,37 @@ +// auth.controller.ts - Authentication Controller + +// Import necessary types and services import { Request, Response, NextFunction } from 'express'; import { AuthService } from '../services/auth.service'; +/** +* Authentication Controller +* Handles login requests and user authentication +*/ export class AuthController { - static async login(req: Request, res: Response, next: NextFunction) { - try { - const { walletAddress } = req.body; - - if (!walletAddress) { - throw new Error('Wallet address is required'); - } + /** + * Login Handler + * Processes user login attempts using wallet addresses + * Returns a JWT token if authentication is successful + */ + static async login(req: Request, res: Response, next: NextFunction) { + try { + // Extract wallet address from request body + const { walletAddress } = req.body; + + // Check if wallet address was provided + if (!walletAddress) { + throw new Error('Wallet address is required'); + } + + // Authenticate user and get JWT token + const token = await AuthService.authenticateUser(walletAddress); - const token = await AuthService.authenticateUser(walletAddress); - res.json({ token }); - } catch (error) { - next(error); - } - } + // Send token back to client + res.json({ token }); + } catch (error) { + // Pass any errors to error handling middleware + next(error); + } + } } \ No newline at end of file From d41d369d15dd4f35a9a5524fad113c4e0803ca78 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 02:30:36 -0600 Subject: [PATCH 10/17] Fix : ormconfig disable SSL for local development --- src/config/ormconfig.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/config/ormconfig.ts b/src/config/ormconfig.ts index fd19c3b..8543373 100644 --- a/src/config/ormconfig.ts +++ b/src/config/ormconfig.ts @@ -8,10 +8,9 @@ const AppDataSource = new DataSource({ url: process.env.DATABASE_URL, entities: [__dirname + '/../entities/*.ts'], migrations: [__dirname + '/../migrations/*.ts'], - ssl: { - rejectUnauthorized: false, - }, - synchronize: false, // Set to true only in development; false in production + synchronize: true, // Be careful with this in production + logging: true, + ssl: false // Disable SSL for local development }); -export default AppDataSource; +export default AppDataSource; \ No newline at end of file From f20ddecb3b37aa6e79941fc697e2728fbc6059f8 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 16:49:44 -0600 Subject: [PATCH 11/17] Feat: routes update --- src/index.ts | 4 ++++ src/middleware/auth.middleware.ts | 1 + src/route/protected.routes.ts | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 8a1421f..8534b0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import 'reflect-metadata'; import express from 'express'; import AppDataSource from './config/ormconfig'; +import router from './route/protected.routes'; const app = express(); const PORT = process.env.PORT || 3000; @@ -21,3 +22,6 @@ AppDataSource.initialize() app.get('/', (req, res) => { res.send('Hello, world!'); }); + + +app.use('/api', router) \ No newline at end of file diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts index 44a8716..0a2f654 100644 --- a/src/middleware/auth.middleware.ts +++ b/src/middleware/auth.middleware.ts @@ -42,6 +42,7 @@ export const authMiddleware = async ( // Verify the token and decode its contents const decoded = AuthService.verifyToken(token); + // Add user information to the request object req.user = { userId: decoded.userId, diff --git a/src/route/protected.routes.ts b/src/route/protected.routes.ts index 2673955..2612b90 100644 --- a/src/route/protected.routes.ts +++ b/src/route/protected.routes.ts @@ -13,7 +13,7 @@ const router = Router(); * This route is protected and requires a valid authentication token * Any authenticated user can access this route */ -router.get('/', authMiddleware, (req, res) => { +router.get('/login', authMiddleware, (req, res) => { res.json({ message: 'Protected route accessed' }); }); From d4fd7289a7ca3f4d14d819c5c8dcba03297b37b8 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sat, 21 Dec 2024 18:58:21 -0600 Subject: [PATCH 12/17] Auth Method API --- src/index.ts | 5 ++++- src/middleware/auth.middleware.ts | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8534b0e..ea41a42 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,4 +24,7 @@ app.get('/', (req, res) => { }); -app.use('/api', router) \ No newline at end of file +app.use('/api', router) + +console.log('JWT_SECRET:', process.env.JWT_SECRET); // Debug log (remove in production) + diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts index 0a2f654..7f490f6 100644 --- a/src/middleware/auth.middleware.ts +++ b/src/middleware/auth.middleware.ts @@ -29,7 +29,7 @@ export const authMiddleware = async ( try { // Get the authorization header const authHeader = req.headers.authorization; - + // Check if authorization header exists and has correct format if (!authHeader || !authHeader.startsWith('Bearer ')) { throw new ReferenceError('No token provided'); @@ -38,11 +38,11 @@ export const authMiddleware = async ( // Extract the token from the header // Format: "Bearer " const token = authHeader.split(' ')[1]; - + // Verify the token and decode its contents const decoded = AuthService.verifyToken(token); - - + + // Add user information to the request object req.user = { userId: decoded.userId, From 4fbf8e43dda512c1c236d8164f70a241f1a702a4 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sun, 22 Dec 2024 08:56:22 -0600 Subject: [PATCH 13/17] Featt: Auth Method Api --- src/route/protected.routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/route/protected.routes.ts b/src/route/protected.routes.ts index 2612b90..a080f68 100644 --- a/src/route/protected.routes.ts +++ b/src/route/protected.routes.ts @@ -13,7 +13,7 @@ const router = Router(); * This route is protected and requires a valid authentication token * Any authenticated user can access this route */ -router.get('/login', authMiddleware, (req, res) => { +router.get('/test-token', authMiddleware, (req, res) => { res.json({ message: 'Protected route accessed' }); }); From 2ee11dc6c22cb0f732a64b43977918bbb3250c55 Mon Sep 17 00:00:00 2001 From: "Anwar S." Date: Sun, 22 Dec 2024 10:48:17 -0600 Subject: [PATCH 14/17] Feat: Auth Method Api --- src/route/protected.routes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/route/protected.routes.ts b/src/route/protected.routes.ts index a080f68..27fabae 100644 --- a/src/route/protected.routes.ts +++ b/src/route/protected.routes.ts @@ -17,6 +17,7 @@ router.get('/test-token', authMiddleware, (req, res) => { res.json({ message: 'Protected route accessed' }); }); + /** * Protected Route - Admin Only Access * Route: GET /admin From 7537964055de3a8ac28c6a3b768bab2bae63fbce Mon Sep 17 00:00:00 2001 From: villarley <115122095+Villarley@users.noreply.github.com> Date: Sun, 22 Dec 2024 16:29:01 -0600 Subject: [PATCH 15/17] FIx: update ormconfig --- src/config/ormconfig.ts | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/config/ormconfig.ts b/src/config/ormconfig.ts index 8543373..5e99068 100644 --- a/src/config/ormconfig.ts +++ b/src/config/ormconfig.ts @@ -3,14 +3,31 @@ import dotenv from "dotenv"; dotenv.config(); -const AppDataSource = new DataSource({ - type: 'postgres', - url: process.env.DATABASE_URL, - entities: [__dirname + '/../entities/*.ts'], - migrations: [__dirname + '/../migrations/*.ts'], - synchronize: true, // Be careful with this in production - logging: true, - ssl: false // Disable SSL for local development -}); +const isTestEnv = process.env.NODE_ENV === "test"; + + +console.log(`Environment: ${process.env.NODE_ENV}`); +const AppDataSource = new DataSource( + isTestEnv + ? { + type: "sqlite", + database: ":memory:", + entities: [__dirname + '/../entities/*.{ts,js}'], + migrations: [__dirname + '/../migrations/*.{ts,js}'], + synchronize: true, + logging: false, + } + : { + type: "postgres", + url: process.env.DATABASE_URL, + entities: [__dirname + '/../entities/*.{ts,js}'], + migrations: [__dirname + '/../migrations/*.{ts,js}'], + ssl: { + rejectUnauthorized: false, + }, + synchronize: false, + logging: false, + } +); export default AppDataSource; \ No newline at end of file From 18cd7e2d1b8bab3b84153b35c1d6740b3c54cd3b Mon Sep 17 00:00:00 2001 From: villarley <115122095+Villarley@users.noreply.github.com> Date: Sun, 22 Dec 2024 16:48:26 -0600 Subject: [PATCH 16/17] Fix:update test --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index ea41a42..3c7432d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,7 @@ app.get('/', (req, res) => { }); -app.use('/api', router) +app.use('/', router) console.log('JWT_SECRET:', process.env.JWT_SECRET); // Debug log (remove in production) From 43d04157119e00f496f76ea14953c95fe8eda3fc Mon Sep 17 00:00:00 2001 From: villarley <115122095+Villarley@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:00:03 -0600 Subject: [PATCH 17/17] Fix: test cases --- .github/workflows/test.yml | 2 +- package-lock.json | 11 +++++++++++ package.json | 6 ------ src/config/ormconfig.ts | 28 ---------------------------- src/entities/User.ts | 36 ------------------------------------ src/index.ts | 11 +---------- src/tests/setup.ts | 2 +- 7 files changed, 14 insertions(+), 82 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1e841e8..9370147 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,4 +30,4 @@ jobs: run: echo "Environment set to test" - name: Run Tests - run: npm test + run: npm test \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 25581d9..b6e8d6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@nestjs/testing": "^10.4.13", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", + "@types/jsonwebtoken": "^9.0.7", "@types/node": "^22.10.1", "@types/supertest": "^6.0.2", "jest": "^29.7.0", @@ -1492,6 +1493,16 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", diff --git a/package.json b/package.json index 506b69b..77e691f 100644 --- a/package.json +++ b/package.json @@ -14,18 +14,12 @@ "license": "ISC", "description": "", "dependencies": { -<<<<<<< HEAD - "dotenv": "^16.4.7", - "express": "^4.21.1", - "jsonwebtoken": "^9.0.2", -======= "axios": "^1.7.9", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^16.4.7", "express": "^4.21.2", "express-validator": "^7.2.0", ->>>>>>> main "pg": "^8.13.1", "reflect-metadata": "^0.2.2", "typeorm": "^0.3.20" diff --git a/src/config/ormconfig.ts b/src/config/ormconfig.ts index 909107f..5e99068 100644 --- a/src/config/ormconfig.ts +++ b/src/config/ormconfig.ts @@ -4,34 +4,6 @@ import dotenv from "dotenv"; dotenv.config(); const isTestEnv = process.env.NODE_ENV === "test"; -<<<<<<< HEAD -======= - - -console.log(`Environment: ${process.env.NODE_ENV}`); -const AppDataSource = new DataSource( - isTestEnv - ? { - type: "sqlite", - database: ":memory:", - entities: [__dirname + '/../entities/*.{ts,js}'], - migrations: [__dirname + '/../migrations/*.{ts,js}'], - synchronize: true, - logging: false, - } - : { - type: "postgres", - url: process.env.DATABASE_URL, - entities: [__dirname + '/../entities/*.{ts,js}'], - migrations: [__dirname + '/../migrations/*.{ts,js}'], - ssl: { - rejectUnauthorized: false, - }, - synchronize: false, - logging: false, - } -); ->>>>>>> main console.log(`Environment: ${process.env.NODE_ENV}`); diff --git a/src/entities/User.ts b/src/entities/User.ts index 33a43f2..b6ec1a8 100644 --- a/src/entities/User.ts +++ b/src/entities/User.ts @@ -1,38 +1,3 @@ -<<<<<<< HEAD -// user.entity.ts - User Database Model - -// Import decorators from TypeORM to define database structure -import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm"; - -/** -* User Entity -* Represents the structure of the User table in the database -* Uses TypeORM decorators to define table columns and properties -*/ -@Entity() // Marks this class as a database table -export class User { - // Primary key - Auto-incrementing ID number - @PrimaryGeneratedColumn() - id: number; - - // User's blockchain wallet address - // unique: true means no two users can have the same address - @Column({ unique: true }) - walletAddress: string; - - // User's role in the system - // default: 'user' means new users automatically get this role - @Column({ default: 'user' }) - role: string; - - // Automatically tracks when the user record was created - @CreateDateColumn() - createdAt: Date; - - // Automatically updates whenever the user record is modified - @UpdateDateColumn() - updatedAt: Date; -======= import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('users') @@ -57,5 +22,4 @@ export class User { @UpdateDateColumn() updatedAt: Date; ->>>>>>> main } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 2524e15..e4bcdbc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,18 +1,13 @@ import 'reflect-metadata'; import express from 'express'; import AppDataSource from './config/ormconfig'; -<<<<<<< HEAD import router from './route/protected.routes'; -======= -import userRoutes from './routes/UserRoutes'; - ->>>>>>> main const app = express(); const PORT = process.env.PORT || 3000; app.use(express.json()); -app.use('/users', userRoutes); +// app.use('/users', userRoutes); AppDataSource.initialize() .then(() => { @@ -30,11 +25,7 @@ app.get('/', (req, res) => { }); -<<<<<<< HEAD app.use('/', router) console.log('JWT_SECRET:', process.env.JWT_SECRET); // Debug log (remove in production) -======= -export default app; // Export the app for testing ->>>>>>> main diff --git a/src/tests/setup.ts b/src/tests/setup.ts index fc09105..07b6ee7 100644 --- a/src/tests/setup.ts +++ b/src/tests/setup.ts @@ -56,4 +56,4 @@ afterAll(async () => { throw error; } } -}); +}); \ No newline at end of file