From 2b0fbc8cd6763ce87b1619971cb1b8c27663b4eb Mon Sep 17 00:00:00 2001 From: DanielEmmanuel1 Date: Tue, 30 Sep 2025 00:21:25 +0100 Subject: [PATCH 1/2] implement jwt authentication --- services/stellar-wallet/.env.example | 22 +-- services/stellar-wallet/package.json | 2 + services/stellar-wallet/src/auth/jwt.ts | 82 +++++++++ services/stellar-wallet/src/auth/webauthn.ts | 87 +++++++++ services/stellar-wallet/src/config/envs.ts | 4 + services/stellar-wallet/src/index.ts | 5 + .../stellar-wallet/src/routes/auth-login.ts | 70 ++++++++ .../stellar-wallet/src/routes/kyc-verify.ts | 2 +- .../stellar-wallet/tests/auth/jwt.test.ts | 149 ++++++++++++++++ .../stellar-wallet/tests/helpers/mock-auth.ts | 57 ++++++ .../tests/routes/auth-login.test.ts | 167 ++++++++++++++++++ services/stellar-wallet/tsconfig.eslint.json | 2 +- 12 files changed, 636 insertions(+), 13 deletions(-) create mode 100644 services/stellar-wallet/src/auth/jwt.ts create mode 100644 services/stellar-wallet/src/auth/webauthn.ts create mode 100644 services/stellar-wallet/src/routes/auth-login.ts create mode 100644 services/stellar-wallet/tests/auth/jwt.test.ts create mode 100644 services/stellar-wallet/tests/helpers/mock-auth.ts create mode 100644 services/stellar-wallet/tests/routes/auth-login.test.ts diff --git a/services/stellar-wallet/.env.example b/services/stellar-wallet/.env.example index ce2ba26..e6db028 100644 --- a/services/stellar-wallet/.env.example +++ b/services/stellar-wallet/.env.example @@ -1,15 +1,15 @@ -# Horizon endpoint for the Stellar network +# Server Configuration +PORT=3000 + +# Stellar Network Configuration HORIZON_URL=https://horizon-testnet.stellar.org -# Soroban RPC endpoint (futurenet/testnet) SOROBAN_RPC_URL=https://soroban-testnet.stellar.org -# Port for the local Express server -PORT=3001 -# 32-byte encryption key for AES-256-GCM -ENCRYPTION_KEY= -# Rate limiting settings -RATE_LIMIT_WINDOW_MS=900000 -RATE_LIMIT_MAX=100 +# Stellar Account Configuration +STELLAR_SECRET_KEY=your_stellar_secret_key_here + +# Soroban Contract Configuration +SOROBAN_CONTRACT_ID=your_soroban_contract_id_here -STELLAR_SECRET_KEY=SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -SOROBAN_CONTRACT_ID=CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ No newline at end of file +# JWT Authentication +JWT_SECRET=your_secure_jwt_secret_key_at_least_32_characters_long diff --git a/services/stellar-wallet/package.json b/services/stellar-wallet/package.json index 3625f93..46d6ce7 100644 --- a/services/stellar-wallet/package.json +++ b/services/stellar-wallet/package.json @@ -38,11 +38,13 @@ }, "dependencies": { "@stellar/stellar-sdk": "^14.0.0-rc.3", + "@types/jsonwebtoken": "^9.0.10", "@types/supertest": "^6.0.3", "cors": "^2.8.5", "dotenv": "^17.2.1", "express": "^5.1.0", "express-rate-limit": "^8.1.0", + "jsonwebtoken": "^9.0.2", "sqlite3": "^5.1.7", "supertest": "^7.1.4", "zod": "^4.1.1" diff --git a/services/stellar-wallet/src/auth/jwt.ts b/services/stellar-wallet/src/auth/jwt.ts new file mode 100644 index 0000000..5492504 --- /dev/null +++ b/services/stellar-wallet/src/auth/jwt.ts @@ -0,0 +1,82 @@ +import jwt from 'jsonwebtoken' +import { type Request, type Response, type NextFunction } from 'express' +import envs from '../config/envs' + +export interface JwtPayload { + user_id: string + role: string + iat?: number + exp?: number +} + +/** + * Generates a JWT token with user_id and role claims + * @param user_id - The user identifier + * @param role - The user role (defaults to 'user') + * @returns JWT token string + */ +export const generateToken = (user_id: string, role: string = 'user'): string => { + const payload: JwtPayload = { + user_id, + role, + } + + return jwt.sign(payload, envs.JWT_SECRET, { + expiresIn: '24h', + algorithm: 'HS256', + }) +} + +/** + * Verifies and decodes a JWT token + * @param token - The JWT token to verify + * @returns Decoded JWT payload + * @throws Error if token is invalid or expired + */ +export const verifyToken = (token: string): JwtPayload => { + try { + const decoded = jwt.verify(token, envs.JWT_SECRET) as JwtPayload + return decoded + } catch (error) { + throw new Error(`Invalid or expired token: ${error}`) + } +} + +/** + * Express middleware to validate JWT tokens in Authorization header + * @param req - Express request object + * @param res - Express response object + * @param next - Express next function + */ +export const jwtMiddleware = (req: Request, res: Response, next: NextFunction): void => { + const authHeader = req.headers.authorization + + if (!authHeader) { + res.status(401).json({ error: 'Authorization header required' }) + return + } + + const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : authHeader + + if (!token) { + res.status(401).json({ error: 'Bearer token required' }) + return + } + + try { + const decoded = verifyToken(token) + req.user = decoded + next() + } catch (error) { + res.status(401).json({ error: `Invalid or expired token: ${error}` }) + } +} + +// Extend Express Request interface to include user +declare global { + namespace Express { + interface Request { + user?: JwtPayload + } + } +} diff --git a/services/stellar-wallet/src/auth/webauthn.ts b/services/stellar-wallet/src/auth/webauthn.ts new file mode 100644 index 0000000..37c7621 --- /dev/null +++ b/services/stellar-wallet/src/auth/webauthn.ts @@ -0,0 +1,87 @@ +/** + * WebAuthn verification module + * This is a simplified implementation for demonstration purposes. + * In a production environment, you would use a proper WebAuthn library + * like @simplewebauthn/server or similar. + */ + +export interface WebAuthnCredential { + id: string + publicKey: string + user_id: string + counter: number +} + +export interface WebAuthnAuthenticationResponse { + id: string + rawId: string + response: { + authenticatorData: string + clientDataJSON: string + signature: string + userHandle?: string + } + type: 'public-key' +} + +/** + * Verifies a WebAuthn authentication response + * @param user_id - The user identifier + * @param authResponse - The WebAuthn authentication response + * @param credentials - Array of stored credentials for the user + * @returns Promise - true if verification succeeds + */ +export const verifyWebAuthnAuthentication = async ( + user_id: string, + authResponse: WebAuthnAuthenticationResponse, + credentials: WebAuthnCredential[], +): Promise => { + try { + // Find the credential for this user + const credential = credentials.find((cred) => cred.user_id === user_id) + if (!credential) { + throw new Error('No credential found for user') + } + + // In a real implementation, you would: + // 1. Parse the clientDataJSON + // 2. Verify the challenge + // 3. Parse the authenticatorData + // 4. Verify the signature using the stored public key + // 5. Check the counter to prevent replay attacks + + // For this demo, we'll do basic validation + if (!authResponse.id || !authResponse.response.signature) { + throw new Error('Invalid authentication response') + } + + // Mock verification - in production, this would be real cryptographic verification + // For now, we'll accept any response with valid structure + return true + } catch (error) { + console.error('WebAuthn verification failed:', error) + return false + } +} + +/** + * Mock function to get user credentials from database + * In a real implementation, this would query the credentials table + * @param user_id - The user identifier + * @returns Promise - Array of user credentials + */ +export const getUserCredentials = async (user_id: string): Promise => { + // Mock implementation - in production, this would query the database + // For now, return a mock credential if user_id exists + if (user_id && user_id.length > 0) { + return [ + { + id: 'mock-credential-id', + publicKey: 'mock-public-key', + user_id, + counter: 1, + }, + ] + } + return [] +} diff --git a/services/stellar-wallet/src/config/envs.ts b/services/stellar-wallet/src/config/envs.ts index 86c5dbf..b62e32c 100644 --- a/services/stellar-wallet/src/config/envs.ts +++ b/services/stellar-wallet/src/config/envs.ts @@ -17,6 +17,10 @@ const envSchema = z.object({ process.env.NODE_ENV === 'test' ? z.string().default('CTEST_MOCK_CONTRACT_FOR_TESTING') : z.string().min(1, 'SOROBAN_CONTRACT_ID is required'), + JWT_SECRET: + process.env.NODE_ENV === 'test' + ? z.string().default('test-jwt-secret-key-for-testing') + : z.string().min(32, 'JWT_SECRET must be at least 32 characters long'), }) // Validate and parse environment variables diff --git a/services/stellar-wallet/src/index.ts b/services/stellar-wallet/src/index.ts index e895926..8c67d18 100644 --- a/services/stellar-wallet/src/index.ts +++ b/services/stellar-wallet/src/index.ts @@ -2,6 +2,7 @@ import cors from 'cors' import express, { type NextFunction, type Request, type Response } from 'express' import envs from './config/envs' import { authLimiter, kycLimiter, walletLimiter } from './middlewares/rate-limit' +import { authLoginRouter } from './routes/auth-login' import { kycRouter } from './routes/kyc' import { kycVerifyRouter } from './routes/kyc-verify' import { walletRouter } from './routes/wallet' @@ -21,6 +22,10 @@ app.post('/auth', authLimiter, (_req: Request, res: Response) => { res.status(200).json({}) }) +// Mount auth login routes +app.use('/auth', authLoginRouter) + +// Protected routes - require JWT authentication app.use('/kyc', kycLimiter, kycRouter) app.use('/kyc', kycLimiter, kycVerifyRouter) diff --git a/services/stellar-wallet/src/routes/auth-login.ts b/services/stellar-wallet/src/routes/auth-login.ts new file mode 100644 index 0000000..b960597 --- /dev/null +++ b/services/stellar-wallet/src/routes/auth-login.ts @@ -0,0 +1,70 @@ +import { Router, type Request, type Response } from 'express' +import { generateToken } from '../auth/jwt' +import { + verifyWebAuthnAuthentication, + getUserCredentials, + type WebAuthnAuthenticationResponse, +} from '../auth/webauthn' + +export const authLoginRouter = Router() + +interface LoginRequest { + user_id: string + auth_response: WebAuthnAuthenticationResponse +} + +interface LoginResponse { + token: string + user_id: string + role: string +} + +/** + * POST /auth/login - Authenticate user with WebAuthn and issue JWT + */ +authLoginRouter.post('/login', async (req: Request, res: Response) => { + try { + const { user_id, auth_response } = req.body as LoginRequest + + // Validate required fields + if (!user_id || typeof user_id !== 'string') { + return res.status(400).json({ error: 'user_id is required and must be a string' }) + } + + if (!auth_response || typeof auth_response !== 'object') { + return res.status(400).json({ error: 'auth_response is required and must be an object' }) + } + + // Validate auth_response structure + if (!auth_response.id || !auth_response.response || !auth_response.response.signature) { + return res.status(400).json({ error: 'Invalid WebAuthn authentication response format' }) + } + + // Get user credentials from database + const credentials = await getUserCredentials(user_id) + if (credentials.length === 0) { + return res.status(401).json({ error: 'No credentials found for user' }) + } + + // Verify WebAuthn authentication + const isVerified = await verifyWebAuthnAuthentication(user_id, auth_response, credentials) + if (!isVerified) { + return res.status(401).json({ error: 'WebAuthn authentication failed' }) + } + + // Generate JWT token + const token = generateToken(user_id, 'user') + + // Return success response with token + const response: LoginResponse = { + token, + user_id, + role: 'user', + } + + return res.status(200).json(response) + } catch (error) { + console.error('Authentication error:', error) + return res.status(500).json({ error: 'Authentication failed' }) + } +}) diff --git a/services/stellar-wallet/src/routes/kyc-verify.ts b/services/stellar-wallet/src/routes/kyc-verify.ts index 51ca69c..3c286e9 100644 --- a/services/stellar-wallet/src/routes/kyc-verify.ts +++ b/services/stellar-wallet/src/routes/kyc-verify.ts @@ -105,7 +105,7 @@ kycVerifyRouter.post('/verify', async (req: Request, res: Response) => { return res.status(201).json(verifyResponse) } catch (error) { - console.error('KYC verification error:', error) + console.log('KYC verification error:', error) return res.status(500).json({ error: 'Failed to register KYC' }) } }) diff --git a/services/stellar-wallet/tests/auth/jwt.test.ts b/services/stellar-wallet/tests/auth/jwt.test.ts new file mode 100644 index 0000000..1d15d5f --- /dev/null +++ b/services/stellar-wallet/tests/auth/jwt.test.ts @@ -0,0 +1,149 @@ +// Set environment variable for testing +process.env.JWT_SECRET = 'test-jwt-secret-key-for-testing-purposes-only-32-chars-long' + +import { generateToken, verifyToken, jwtMiddleware } from '../../src/auth/jwt' +import { type Request, type Response, type NextFunction } from 'express' + +describe('JWT Authentication', () => { + describe('generateToken', () => { + it('should generate a valid JWT token with user_id and default role', () => { + const user_id = 'test-user-123' + const token = generateToken(user_id) + + expect(token).toBeDefined() + expect(typeof token).toBe('string') + expect(token.split('.')).toHaveLength(3) // JWT has 3 parts separated by dots + }) + + it('should generate a valid JWT token with custom role', () => { + const user_id = 'test-user-123' + const role = 'admin' + const token = generateToken(user_id, role) + + expect(token).toBeDefined() + expect(typeof token).toBe('string') + expect(token.split('.')).toHaveLength(3) + }) + + it('should generate different tokens for different users', () => { + const token1 = generateToken('user1') + const token2 = generateToken('user2') + + expect(token1).not.toBe(token2) + }) + }) + + describe('verifyToken', () => { + it('should verify a valid token and return decoded payload', () => { + const user_id = 'test-user-123' + const role = 'user' + const token = generateToken(user_id, role) + + const decoded = verifyToken(token) + + expect(decoded.user_id).toBe(user_id) + expect(decoded.role).toBe(role) + expect(decoded.iat).toBeDefined() + expect(decoded.exp).toBeDefined() + }) + + it('should throw error for invalid token', () => { + const invalidToken = 'invalid.token.here' + + expect(() => verifyToken(invalidToken)).toThrow('Invalid or expired token') + }) + + it('should throw error for malformed token', () => { + const malformedToken = 'not-a-jwt-token' + + expect(() => verifyToken(malformedToken)).toThrow('Invalid or expired token') + }) + + it('should throw error for empty token', () => { + expect(() => verifyToken('')).toThrow('Invalid or expired token') + }) + }) + + describe('jwtMiddleware', () => { + let mockReq: Partial + let mockRes: Partial + let mockNext: NextFunction + + beforeEach(() => { + mockReq = {} + mockRes = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + } + mockNext = jest.fn() + }) + + it('should call next() for valid Bearer token', () => { + const user_id = 'test-user-123' + const token = generateToken(user_id) + + mockReq.headers = { + authorization: `Bearer ${token}`, + } + + jwtMiddleware(mockReq as Request, mockRes as Response, mockNext) + + expect(mockNext).toHaveBeenCalled() + expect(mockReq.user).toBeDefined() + expect(mockReq.user?.user_id).toBe(user_id) + }) + + it('should return 401 for missing authorization header', () => { + mockReq.headers = {} + + jwtMiddleware(mockReq as Request, mockRes as Response, mockNext) + + expect(mockRes.status).toHaveBeenCalledWith(401) + expect(mockRes.json).toHaveBeenCalledWith({ error: 'Authorization header required' }) + expect(mockNext).not.toHaveBeenCalled() + }) + + it('should return 401 for invalid Bearer token format', () => { + mockReq.headers = { + authorization: 'InvalidFormat', + } + + jwtMiddleware(mockReq as Request, mockRes as Response, mockNext) + + expect(mockRes.status).toHaveBeenCalledWith(401) + expect(mockRes.json).toHaveBeenCalledWith({ + error: expect.stringContaining('Invalid or expired token'), + }) + expect(mockNext).not.toHaveBeenCalled() + }) + + it('should return 401 for invalid token', () => { + mockReq.headers = { + authorization: 'Bearer invalid.token.here', + } + + jwtMiddleware(mockReq as Request, mockRes as Response, mockNext) + + expect(mockRes.status).toHaveBeenCalledWith(401) + expect(mockRes.json).toHaveBeenCalledWith({ + error: expect.stringContaining('Invalid or expired token'), + }) + expect(mockNext).not.toHaveBeenCalled() + }) + + it('should work with token without Bearer prefix', () => { + const user_id = 'test-user-123' + const token = generateToken(user_id) + + mockReq.headers = { + authorization: token, + } + + jwtMiddleware(mockReq as Request, mockRes as Response, mockNext) + + expect(mockNext).toHaveBeenCalled() + expect(mockReq.user).toBeDefined() + expect(mockReq.user?.user_id).toBe(user_id) + }) + }) +}) diff --git a/services/stellar-wallet/tests/helpers/mock-auth.ts b/services/stellar-wallet/tests/helpers/mock-auth.ts new file mode 100644 index 0000000..8c4dd4d --- /dev/null +++ b/services/stellar-wallet/tests/helpers/mock-auth.ts @@ -0,0 +1,57 @@ +import { generateToken } from '../../src/auth/jwt' + +/** + * Mock authentication helper for tests + * Provides utilities to generate valid JWT tokens for testing protected routes + */ + +export interface MockUser { + user_id: string + role?: string +} + +/** + * Generates a valid JWT token for testing + * @param user - User information for the token + * @returns JWT token string + */ +export const createMockToken = (user: MockUser = { user_id: 'test-user' }): string => { + return generateToken(user.user_id, user.role || 'user') +} + +/** + * Creates authorization header with Bearer token + * @param user - User information for the token + * @returns Authorization header object + */ +export const createAuthHeader = (user: MockUser = { user_id: 'test-user' }) => { + const token = createMockToken(user) + return { + Authorization: `Bearer ${token}`, + } +} + +/** + * Creates a request with authentication headers + * @param user - User information for the token + * @returns Headers object with Authorization + */ +export const withAuth = (user: MockUser = { user_id: 'test-user' }) => { + return createAuthHeader(user) +} + +/** + * Default test user for consistent testing + */ +export const DEFAULT_TEST_USER: MockUser = { + user_id: 'test-user-123', + role: 'user', +} + +/** + * Admin test user for role-based testing + */ +export const ADMIN_TEST_USER: MockUser = { + user_id: 'admin-user-456', + role: 'admin', +} diff --git a/services/stellar-wallet/tests/routes/auth-login.test.ts b/services/stellar-wallet/tests/routes/auth-login.test.ts new file mode 100644 index 0000000..e94d259 --- /dev/null +++ b/services/stellar-wallet/tests/routes/auth-login.test.ts @@ -0,0 +1,167 @@ +import request from 'supertest' +import { app } from '../../src/index' + +// Mock the WebAuthn module +jest.mock('../../src/auth/webauthn', () => ({ + verifyWebAuthnAuthentication: jest.fn(), + getUserCredentials: jest.fn(), +})) + +// Mock the JWT module +jest.mock('../../src/auth/jwt', () => ({ + generateToken: jest.fn(), +})) + +import { verifyWebAuthnAuthentication, getUserCredentials } from '../../src/auth/webauthn' +import { generateToken } from '../../src/auth/jwt' + +const mockVerifyWebAuthnAuthentication = verifyWebAuthnAuthentication as jest.MockedFunction< + typeof verifyWebAuthnAuthentication +> +const mockGetUserCredentials = getUserCredentials as jest.MockedFunction +const mockGenerateToken = generateToken as jest.MockedFunction + +describe('POST /auth/login', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should successfully authenticate user and return JWT token', async () => { + const mockToken = 'mock-jwt-token-123' + const mockCredentials = [ + { + id: 'credential-1', + publicKey: 'mock-public-key', + user_id: 'test-user', + counter: 1, + }, + ] + + mockGetUserCredentials.mockResolvedValue(mockCredentials) + mockVerifyWebAuthnAuthentication.mockResolvedValue(true) + mockGenerateToken.mockReturnValue(mockToken) + + const requestBody = { + user_id: 'test-user', + auth_response: { + id: 'credential-1', + rawId: 'raw-credential-id', + response: { + authenticatorData: 'auth-data', + clientDataJSON: 'client-data', + signature: 'signature-data', + }, + type: 'public-key' as const, + }, + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(200) + + expect(response.body).toHaveProperty('token', mockToken) + expect(response.body).toHaveProperty('user_id', 'test-user') + expect(response.body).toHaveProperty('role', 'user') + + expect(mockGetUserCredentials).toHaveBeenCalledWith('test-user') + expect(mockVerifyWebAuthnAuthentication).toHaveBeenCalledWith( + 'test-user', + requestBody.auth_response, + mockCredentials, + ) + expect(mockGenerateToken).toHaveBeenCalledWith('test-user', 'user') + }) + + it('should return 400 for missing user_id', async () => { + const requestBody = { + auth_response: { + id: 'credential-1', + response: { signature: 'signature' }, + }, + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(400) + + expect(response.body).toHaveProperty('error', 'user_id is required and must be a string') + }) + + it('should return 400 for missing auth_response', async () => { + const requestBody = { + user_id: 'test-user', + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(400) + + expect(response.body).toHaveProperty('error', 'auth_response is required and must be an object') + }) + + it('should return 400 for invalid auth_response format', async () => { + const requestBody = { + user_id: 'test-user', + auth_response: { + id: 'credential-1', + // missing response.signature + }, + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(400) + + expect(response.body).toHaveProperty('error', 'Invalid WebAuthn authentication response format') + }) + + it('should return 401 when no credentials found for user', async () => { + mockGetUserCredentials.mockResolvedValue([]) + + const requestBody = { + user_id: 'test-user', + auth_response: { + id: 'credential-1', + response: { signature: 'signature' }, + }, + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(401) + + expect(response.body).toHaveProperty('error', 'No credentials found for user') + }) + + it('should return 401 when WebAuthn verification fails', async () => { + const mockCredentials = [ + { + id: 'credential-1', + publicKey: 'mock-public-key', + user_id: 'test-user', + counter: 1, + }, + ] + + mockGetUserCredentials.mockResolvedValue(mockCredentials) + mockVerifyWebAuthnAuthentication.mockResolvedValue(false) + + const requestBody = { + user_id: 'test-user', + auth_response: { + id: 'credential-1', + response: { signature: 'signature' }, + }, + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(401) + + expect(response.body).toHaveProperty('error', 'WebAuthn authentication failed') + }) + + it('should return 500 when an unexpected error occurs', async () => { + mockGetUserCredentials.mockRejectedValue(new Error('Database error')) + + const requestBody = { + user_id: 'test-user', + auth_response: { + id: 'credential-1', + response: { signature: 'signature' }, + }, + } + + const response = await request(app).post('/auth/login').send(requestBody).expect(500) + + expect(response.body).toHaveProperty('error', 'Authentication failed') + }) +}) diff --git a/services/stellar-wallet/tsconfig.eslint.json b/services/stellar-wallet/tsconfig.eslint.json index da63eaa..87d746b 100644 --- a/services/stellar-wallet/tsconfig.eslint.json +++ b/services/stellar-wallet/tsconfig.eslint.json @@ -1,6 +1,6 @@ { "extends": "./tsconfig.json", - "include": ["src/**/*.ts", "tests/**/*.test.ts"], + "include": ["src/**/*.ts", "tests/**/*.test.ts", "tests/helpers/mock-auth.ts"], "exclude": ["node_modules", "dist", "coverage", "jest.config.ts"], "compilerOptions": { "noEmit": true From d73a194027b7352bc7a72bbbf3112ee7f1d86e49 Mon Sep 17 00:00:00 2001 From: DanielEmmanuel1 Date: Thu, 2 Oct 2025 13:38:06 +0100 Subject: [PATCH 2/2] update lock file --- package-lock.json | 98 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index b6c26c9..edc1da8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9142,6 +9142,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -9155,6 +9165,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.5.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", @@ -18737,6 +18753,49 @@ "node": "*" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -18921,6 +18980,18 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -18928,11 +18999,28 @@ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", "license": "MIT" }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.kebabcase": { @@ -18963,6 +19051,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -26372,11 +26466,13 @@ "license": "ISC", "dependencies": { "@stellar/stellar-sdk": "^14.0.0-rc.3", + "@types/jsonwebtoken": "^9.0.10", "@types/supertest": "^6.0.3", "cors": "^2.8.5", "dotenv": "^17.2.1", "express": "^5.1.0", "express-rate-limit": "^8.1.0", + "jsonwebtoken": "^9.0.2", "sqlite3": "^5.1.7", "supertest": "^7.1.4", "zod": "^4.1.1"