Authentication
How SDK request authentication works, including headers and signature verification.
Overview
All SDK requests are authenticated using cryptographic signatures. Each request includes headers that the backend verifies before processing.
Request Headers
Every SDK request includes these headers:
| Header | Description |
|---|---|
x-api-key | Merchant's public API key |
x-nonce | Unique cryptographic nonce |
x-timestamp | Unix timestamp of request |
x-signature | HMAC-SHA256 signature |
x-fingerprint | Server fingerprint hash |
// Headers attached to each request
{
'x-api-key': 'npk_live_abc123',
'x-nonce': 'a1b2c3d4e5f6g7h8',
'x-timestamp': '1743500000',
'x-signature': 'sha256=...',
'x-fingerprint': 'linux-x64-node-v20',
}Signature Algorithm
The signature is computed as:
signature = HMAC-SHA256(
key: apiSecret,
message: requestBody + nonce + timestamp
)Example Implementation
import { createHmac, randomBytes } from 'crypto';
function signRequest(
body: string,
apiSecret: string,
timestamp: number
): { nonce: string; signature: string } {
const nonce = randomBytes(16).toString('hex');
const message = body + nonce + timestamp.toString();
const signature = createHmac('sha256', apiSecret)
.update(message)
.digest('hex');
return { nonce, signature: `sha256=${signature}` };
}Backend Verification
When a request arrives, the backend verifies:
- Timestamp check: Request timestamp within acceptable window (5 minutes)
- Nonce uniqueness: Nonce not previously used (replay protection)
- API key validation: Key exists and is active
- Signature match: Computed signature matches provided signature
- IP whitelist: Request from allowed IP if configured
- Rate limit: Within API rate limits
Verification Flow
Request received
│
▼
┌──────────────────┐
│ Check timestamp │──Invalid──▶ Reject (401)
└────────┬─────────┘
│ Valid
▼
┌──────────────────┐
│ Check nonce │──Used──▶ Reject (401)
└────────┬─────────┘
│ Unique
▼
┌──────────────────┐
│ Validate API key │──Invalid──▶ Reject (401)
└────────┬─────────┘
│ Valid
▼
┌──────────────────┐
│ Verify signature │──Mismatch──▶ Reject (401)
└────────┬─────────┘
│ Match
▼
┌──────────────────┐
│ Check rate limit │──Exceeded──▶ Reject (429)
└────────┬─────────┘
│ OK
▼
Process requestSDK Initialization
The SDK handles all authentication automatically:
import { createNilePay } from '@nilepay/sdk';
const nilePay = createNilePay({
environment: 'live',
apiKey: 'npk_live_your_key',
apiSecret: 'nps_live_your_secret',
});Custom Integrations
If building a custom integration without the SDK, implement the signature algorithm exactly as shown above.
Custom Request Example
async function makeAuthenticatedRequest(
url: string,
body: object,
apiKey: string,
apiSecret: string
): Promise<Response> {
const timestamp = Math.floor(Date.now() / 1000);
const bodyString = JSON.stringify(body);
const { nonce, signature } = signRequest(bodyString, apiSecret, timestamp);
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'x-nonce': nonce,
'x-timestamp': timestamp.toString(),
'x-signature': signature,
},
body: bodyString,
});
}Security Best Practices
- Never expose secret keys client-side: Use only in server-side code
- Rotate keys regularly: Generate new keys periodically
- Use HTTPS only: All requests must use TLS
- Validate timestamps: Reject requests outside acceptable window
- Store nonces: Maintain nonce history for replay protection
- Log authentication failures: Monitor for brute force attempts