Skip to content

Commit 1c684af

Browse files
feat: Allow API keys in request body (#6)
This change allows both the `X-MASTER-KEY` and `X-API-KEY` to be sent in the request body for POST requests, providing more flexibility for users. The key extraction logic has been centralized into a new helper function to improve code maintainability. The Swagger documentation, Postman collection, and README have all been updated to reflect these changes. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 51bed95 commit 1c684af

7 files changed

Lines changed: 355 additions & 245 deletions

File tree

‎README.md‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,17 @@ All endpoints are prefixed with `/api`.
168168
169169
### **Authentication**
170170
171-
This API uses a two-key system for security and session management:
171+
This API uses a two-key system for security and session management. Both keys can be provided in either the request header or the request body for `POST` requests, giving you more flexibility. For `GET` requests, they must be in the header.
172172
173173
1. **Master Key (`X-MASTER-KEY`)**:
174-
- This is a global key that grants access to the API server.
175-
- It must be included in the header of **every single request**.
174+
- This is a global key that grants access to the entire API server.
175+
- It can be included in the `X-MASTER-KEY` header or as a field in the JSON request body. The header will always take precedence if both are provided.
176176
- This is the key you set in your `.env` file.
177177
178178
2. **Session Key (`X-API-KEY`)**:
179179
- This key identifies a specific WhatsApp session (i.e., a specific phone number).
180+
- For `POST` requests, it can be in the `X-API-KEY` header or a field in the JSON/form-data body.
181+
- For `GET` requests, it must be in the `X-API-KEY` header.
180182
- You can invent any unique string for each session (e.g., `user1_phone`, `work_account`, a random hash, etc.).
181183
- The first time a new `X-API-KEY` is used with the `/connect` endpoint, a new session will be created for it.
182184
@@ -266,12 +268,12 @@ Once connected, the server will save the session data in the `./sessions` folder
266268
#### 5. **Send Text Message (POST)**
267269
268270
- **Endpoint**: `POST /send-message`
269-
- **Headers**:
270-
- `X-MASTER-KEY: your_global_master_key_here`
271-
- `X-API-KEY: your_unique_session_key`
271+
- **Headers**: `X-MASTER-KEY: your_global_master_key_here`
272+
- **Description**: Sends a plain text message. The `X-API-KEY` can be in the header or, as shown below, in the request body.
272273
- **Payload**: `application/json`
273274
```json
274275
{
276+
"X-API-KEY": "your_unique_session_key",
275277
"to": "+1234567890",
276278
"message": "Hello from the API!"
277279
}
@@ -281,24 +283,24 @@ Once connected, the server will save the session data in the `./sessions` folder
281283
282284
- **Endpoint**: `POST /send-attachment`
283285
- **Description**: Sends an attachment to a specified number. This endpoint supports three methods: direct file upload, from a URL, or from a Base64 string.
284-
- **Headers**:
285-
- `X-MASTER-KEY: your_global_master_key_here`
286-
- `X-API-KEY: your_unique_session_key`
286+
- **Headers**: `X-MASTER-KEY: your_global_master_key_here`
287287
288288
---
289289
290290
##### **Method 1: Direct File Upload**
291291
292292
- **Content-Type**: `multipart/form-data`
293+
- **Description**: The `X-API-KEY` can be in the header or, as shown below, as a form field.
293294
- **Body Fields**:
295+
- `X-API-KEY`: Your unique session key.
294296
- `to`: The recipient's phone number.
295297
- `file`: The file to be sent.
296298
- `caption` (optional): A caption for the file.
297299
- **Example `curl` Request**:
298300
```bash
299301
curl -X POST http://localhost:3000/api/send-attachment \
300302
-H "X-MASTER-KEY: your_global_master_key_here" \
301-
-H "X-API-KEY: your_unique_session_key" \
303+
-F "X-API-KEY=your_unique_session_key" \
302304
-F "to=+1234567890" \
303305
-F "file=@/path/to/your/document.pdf" \
304306
-F "caption=Here is the document you requested."
@@ -309,9 +311,11 @@ Once connected, the server will save the session data in the `./sessions` folder
309311
##### **Method 2: From URL or Base64**
310312
311313
- **Content-Type**: `application/json`
314+
- **Description**: The `X-API-KEY` can be in the header or, as shown below, in the request body.
312315
- **Payload**:
313316
```json
314317
{
318+
"X-API-KEY": "your_unique_session_key",
315319
"to": "+1234567890",
316320
"file": "url_or_base64_string",
317321
"type": "image/png", // Required only for Base64
@@ -326,8 +330,7 @@ Once connected, the server will save the session data in the `./sessions` folder
326330
curl -X POST http://localhost:3000/api/send-attachment \
327331
-H "Content-Type: application/json" \
328332
-H "X-MASTER-KEY: your_global_master_key_here" \
329-
-H "X-API-KEY: your_unique_session_key" \
330-
-d '{"to": "+1234567890", "file": "https://i.imgur.com/some-image.jpeg", "caption": "From a URL"}'
333+
-d '{"X-API-KEY": "your_unique_session_key", "to": "+1234567890", "file": "https://i.imgur.com/some-image.jpeg", "caption": "From a URL"}'
331334
```
332335
333336
---

‎src/controllers/authController.js‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
const qrcode = require('qrcode');
22
const { getStatus, initializeClient } = require('../services/sessionManager');
3-
4-
/**
5-
* Extracts the session ID from the request headers.
6-
* @param {import('express').Request} req - The Express request object.
7-
* @returns {string|null} The session ID or null if not found.
8-
*/
9-
const getSessionId = (req) => {
10-
return req.get('X-API-KEY');
11-
};
3+
const { getSessionId } = require('../utils/apiKeyExtractor');
124

135
/**
146
* Handles the /connect endpoint.

‎src/controllers/messageController.js‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
const { sendMessage, sendAttachment } = require('../services/sessionManager');
2-
3-
/**
4-
* Extracts the session ID from the request headers.
5-
* @param {import('express').Request} req - The Express request object.
6-
* @returns {string|null} The session ID or null if not found.
7-
*/
8-
const getSessionId = (req) => {
9-
return req.get('X-API-KEY');
10-
};
2+
const { getSessionId } = require('../utils/apiKeyExtractor');
113

124
/**
135
* Handles the /send-message endpoint.

‎src/middleware/masterAuthMiddleware.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ const MASTER_API_KEY = process.env.MASTER_API_KEY;
44

55
/**
66
* Middleware to protect all API routes with a Master API key.
7+
* The key can be provided in the 'X-MASTER-KEY' header or in the request body.
78
*/
89
const masterApiKeyAuth = (req, res, next) => {
9-
const masterKey = req.get('X-MASTER-KEY');
10+
// Check for the key in the header first, then in the body.
11+
const masterKey = req.get('X-MASTER-KEY') || (req.body ? req.body['X-MASTER-KEY'] : undefined);
1012

1113
if (!MASTER_API_KEY) {
1214
// If the master key is not set in the environment, deny all requests.

‎src/routes/api.js‎

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,19 @@ const upload = require('../middleware/uploadMiddleware');
2424
* get:
2525
* summary: Get QR code as a string
2626
* tags: [Authentication]
27-
* description: Establishes a new WhatsApp session and returns the QR code as a string for authentication.
27+
* description: Establishes a new WhatsApp session and returns the QR code as a string for authentication. Requires a session key.
28+
* parameters:
29+
* - in: header
30+
* name: X-API-KEY
31+
* schema:
32+
* type: string
33+
* required: true
34+
* description: Your unique session key.
2835
* responses:
2936
* 200:
30-
* description: QR code string.
37+
* description: QR code string or session status.
38+
* 400:
39+
* description: Missing X-API-KEY header.
3140
* 500:
3241
* description: Server error.
3342
*/
@@ -39,7 +48,14 @@ router.get('/connect', getQrCodeString);
3948
* get:
4049
* summary: Get QR code as an image
4150
* tags: [Authentication]
42-
* description: Establishes a new WhatsApp session and returns the QR code as a PNG image.
51+
* description: Establishes a new WhatsApp session and returns the QR code as a PNG image. Requires a session key.
52+
* parameters:
53+
* - in: header
54+
* name: X-API-KEY
55+
* schema:
56+
* type: string
57+
* required: true
58+
* description: Your unique session key.
4359
* responses:
4460
* 200:
4561
* description: QR code image.
@@ -48,6 +64,10 @@ router.get('/connect', getQrCodeString);
4864
* schema:
4965
* type: string
5066
* format: binary
67+
* 400:
68+
* description: Missing X-API-KEY header.
69+
* 404:
70+
* description: QR code not available.
5171
* 500:
5272
* description: Server error.
5373
*/
@@ -59,24 +79,38 @@ router.get('/connect/image', getQrCodeImage);
5979
* post:
6080
* summary: Send a text message
6181
* tags: [Messaging]
62-
* security:
63-
* - ApiKeyAuth: []
82+
* description: Sends a text message from a specific session. The session is identified by the `X-API-KEY`, which can be passed either in the request header or in the request body. The header takes precedence.
83+
* parameters:
84+
* - in: header
85+
* name: X-API-KEY
86+
* schema:
87+
* type: string
88+
* required: false
89+
* description: Your unique session key (can be in header or body).
6490
* requestBody:
6591
* required: true
6692
* content:
6793
* application/json:
6894
* schema:
6995
* type: object
7096
* properties:
71-
* number:
97+
* X-API-KEY:
98+
* type: string
99+
* description: Your unique session key (if not provided in header).
100+
* to:
72101
* type: string
102+
* description: The recipient's phone number.
73103
* message:
74104
* type: string
105+
* description: The text message to send.
106+
* required:
107+
* - to
108+
* - message
75109
* responses:
76110
* 200:
77111
* description: Message sent successfully.
78112
* 400:
79-
* description: Bad request.
113+
* description: Bad request (e.g., missing parameters or session key).
80114
*/
81115
router.post('/send-message', sendTextMessage);
82116

@@ -86,27 +120,59 @@ router.post('/send-message', sendTextMessage);
86120
* post:
87121
* summary: Send a message with an attachment
88122
* tags: [Messaging]
89-
* security:
90-
* - ApiKeyAuth: []
123+
* description: Sends a file attachment from a specific session. The session is identified by the `X-API-KEY`, which can be passed either in the request header or in the request body. The header takes precedence. This endpoint supports `multipart/form-data` for direct uploads and `application/json` for sending from a URL or Base64 string.
124+
* parameters:
125+
* - in: header
126+
* name: X-API-KEY
127+
* schema:
128+
* type: string
129+
* required: false
130+
* description: Your unique session key (can be in header or body).
91131
* requestBody:
92132
* required: true
93133
* content:
94134
* multipart/form-data:
95135
* schema:
96136
* type: object
97137
* properties:
98-
* number:
138+
* X-API-KEY:
139+
* type: string
140+
* description: Your unique session key (if not provided in header).
141+
* to:
99142
* type: string
100143
* caption:
101144
* type: string
102145
* file:
103146
* type: string
104147
* format: binary
148+
* required:
149+
* - to
150+
* - file
151+
* application/json:
152+
* schema:
153+
* type: object
154+
* properties:
155+
* X-API-KEY:
156+
* type: string
157+
* description: Your unique session key (if not provided in header).
158+
* to:
159+
* type: string
160+
* file:
161+
* type: string
162+
* description: A public URL to the file or a Base64 encoded string.
163+
* type:
164+
* type: string
165+
* description: The MIME type of the file (required for Base64).
166+
* caption:
167+
* type: string
168+
* required:
169+
* - to
170+
* - file
105171
* responses:
106172
* 200:
107173
* description: Attachment sent successfully.
108174
* 400:
109-
* description: Bad request.
175+
* description: Bad request (e.g., missing parameters or session key).
110176
*/
111177
router.post('/send-attachment', upload.single('file'), sendAttachmentMessage);
112178

@@ -116,8 +182,7 @@ router.post('/send-attachment', upload.single('file'), sendAttachmentMessage);
116182
* post:
117183
* summary: Upload a file
118184
* tags: [File Upload]
119-
* security:
120-
* - ApiKeyAuth: []
185+
* description: Uploads a file to the server and returns a temporary URL. This endpoint does not require a session key (`X-API-KEY`).
121186
* requestBody:
122187
* required: true
123188
* content:
@@ -142,9 +207,14 @@ router.post('/upload', upload.single('file'), uploadFile);
142207
* get:
143208
* summary: Send a message via GET request
144209
* tags: [Messaging]
145-
* security:
146-
* - ApiKeyAuth: []
210+
* description: A simple GET request to send a text message or an attachment via URL. Requires a session key.
147211
* parameters:
212+
* - in: header
213+
* name: X-API-KEY
214+
* schema:
215+
* type: string
216+
* required: true
217+
* description: Your unique session key.
148218
* - in: query
149219
* name: number
150220
* schema:
@@ -155,13 +225,19 @@ router.post('/upload', upload.single('file'), uploadFile);
155225
* name: message
156226
* schema:
157227
* type: string
158-
* required: true
159-
* description: The message content.
228+
* required: false
229+
* description: The text message to send (used as caption if `attachmentUrl` is present).
230+
* - in: query
231+
* name: attachmentUrl
232+
* schema:
233+
* type: string
234+
* required: false
235+
* description: A URL to a file to send as an attachment.
160236
* responses:
161237
* 200:
162238
* description: Message sent successfully.
163239
* 400:
164-
* description: Bad request.
240+
* description: Bad request (e.g., missing parameters or session key).
165241
*/
166242
router.get('/send', sendFromApi);
167243

‎src/utils/apiKeyExtractor.js‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Extracts the session ID (API key) from the request.
3+
* It checks the 'X-API-KEY' header first, then falls back to the request body.
4+
*
5+
* @param {import('express').Request} req - The Express request object.
6+
* @returns {string|null} The session ID or null if not found.
7+
*/
8+
const getSessionId = (req) => {
9+
const fromHeader = req.get('X-API-KEY');
10+
if (fromHeader) {
11+
return fromHeader;
12+
}
13+
14+
if (req.body && req.body['X-API-KEY']) {
15+
return req.body['X-API-KEY'];
16+
}
17+
18+
return null;
19+
};
20+
21+
module.exports = {
22+
getSessionId,
23+
};

0 commit comments

Comments
 (0)