Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ MONGODB_URI=mongodb://<USER>:<PASSWORD>@localhost:27017/transaction_ledger?authS
# keep: Keep transactions after account deletion
# cascade: Delete transactions after account deletion
# deny: Deny deletion of the account
TRANSACTION_DELETE_POLICY=keep
TRANSACTION_DELETE_POLICY=keep

# API keys for authentication
API_KEYS=your_api_key_1,your_api_key_2
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ http://localhost:<PORT>/docs

Replace `<PORT>` with the configured port in your `.env` file.

## API Authentication

The API is secured using API keys. Each request must include a valid API key in the `x-api-key` header. The API keys are configured in the environment variable `API_KEYS` as a comma-separated list.

### Example Request

Include the `x-api-key` header in your request:

```bash
curl -X GET "http://localhost:<PORT>/transactions" -H "x-api-key: YOUR_API_KEY"
```

Replace `<PORT>` with the configured port in your `.env` file and `YOUR_API_KEY` with a valid API key.

## Debugging with Visual Studio Code

To launch and debug the API using Visual Studio Code, follow these steps:
Expand Down
1 change: 1 addition & 0 deletions src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default () => {
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
MONGODB_URI: z.string().url(),
TRANSACTION_DELETE_POLICY: z.enum(['keep', 'cascade', 'deny']).default('keep'),
API_KEYS: z.string().optional(),
});

const env = envSchema.safeParse(process.env);
Expand Down
21 changes: 21 additions & 0 deletions src/middleware/apiKeyValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Request, Response, NextFunction } from 'express';

const apiKeyValidator = (req: Request, res: Response, next: NextFunction) => {
const apiKey = req.header('x-api-key');

if (!process.env.API_KEYS) {
res.status(500).json({ error: 'API keys are not configured.' });
return;
}

const allowedKeys = process.env.API_KEYS.split(',');

Comment on lines +11 to +12
Copy link

Copilot AI Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider trimming the API keys after splitting to handle any extra spaces, e.g., change to: process.env.API_KEYS.split(',').map(key => key.trim()).

Suggested change
const allowedKeys = process.env.API_KEYS.split(',');
const allowedKeys = process.env.API_KEYS.split(',').map(key => key.trim());

Copilot uses AI. Check for mistakes.
if (!apiKey || !allowedKeys.includes(apiKey)) {
res.status(401).json({ error: 'Unauthorized: Invalid API key.' });
return;
}

next();
};

export default apiKeyValidator;
2 changes: 2 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import express from 'express';
import apiKeyValidator from '@/middleware/apiKeyValidator';
import accountRoutes from './account';
import transactionRoutes from './transaction';

const app = express();

app.use(apiKeyValidator);
app.use('/transactions', transactionRoutes);
app.use('/accounts', accountRoutes);

Expand Down
14 changes: 13 additions & 1 deletion swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,18 @@
}
}
}
},
"securitySchemes": {
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "x-api-key"
}
}
},
"security": [
{
"ApiKeyAuth": []
}
}
]
}