first commit
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
/\*\*
|
||||
|
||||
- HMAC Authentication Implementation Guide
|
||||
- For Fitness Tracking App API
|
||||
-
|
||||
- This document explains how to generate HMAC signatures for API requests
|
||||
\*/
|
||||
|
||||
# HMAC Request Signing Process
|
||||
|
||||
## Overview
|
||||
|
||||
All API requests must include HMAC-SHA256 signatures for authentication and integrity verification.
|
||||
|
||||
## Required Headers
|
||||
|
||||
```
|
||||
X-API-Key: <user_api_key>
|
||||
X-Signature: <hmac_signature>
|
||||
X-Timestamp: <unix_timestamp>
|
||||
```
|
||||
|
||||
## Signature Generation Algorithm
|
||||
|
||||
### 1. Create the message to sign
|
||||
|
||||
```
|
||||
message = timestamp + "|" + api_key + "|" + raw_request_body
|
||||
```
|
||||
|
||||
### 2. Generate HMAC-SHA256
|
||||
|
||||
```
|
||||
signature = HMAC-SHA256(message, api_secret)
|
||||
```
|
||||
|
||||
### 3. Encode as hex string
|
||||
|
||||
```
|
||||
hex_signature = hex_encode(signature)
|
||||
```
|
||||
|
||||
## PHP Implementation Example
|
||||
|
||||
```php
|
||||
$api_key = 'your_api_key_here';
|
||||
$api_secret = 'your_api_secret_here';
|
||||
$timestamp = time();
|
||||
$request_body = json_encode([
|
||||
'workout_type' => 'running',
|
||||
'distance_meters' => 5000,
|
||||
'duration_seconds' => 1800,
|
||||
// ... other fields
|
||||
]);
|
||||
|
||||
// Generate signature
|
||||
$message = $timestamp . '|' . $api_key . '|' . $request_body;
|
||||
$signature = hash_hmac('sha256', $message, $api_secret);
|
||||
|
||||
// Make request with headers
|
||||
$headers = [
|
||||
'Content-Type: application/json',
|
||||
'X-API-Key: ' . $api_key,
|
||||
'X-Signature: ' . $signature,
|
||||
'X-Timestamp: ' . $timestamp
|
||||
];
|
||||
|
||||
$ch = curl_init('https://your-api.com/api/v1/workouts');
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $request_body);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($ch);
|
||||
```
|
||||
|
||||
## Dart/Flutter Implementation Example
|
||||
|
||||
```dart
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
String generateHmacSignature(
|
||||
String apiKey,
|
||||
String apiSecret,
|
||||
String payload,
|
||||
int timestamp,
|
||||
) {
|
||||
final message = '$timestamp|$apiKey|$payload';
|
||||
final hmac = Hmac(sha256, utf8.encode(apiSecret));
|
||||
final digest = hmac.convert(utf8.encode(message));
|
||||
return digest.toString();
|
||||
}
|
||||
|
||||
// Usage:
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
final payload = jsonEncode({
|
||||
'workout_type': 'running',
|
||||
'distance_meters': 5000,
|
||||
// ...
|
||||
});
|
||||
|
||||
final signature = generateHmacSignature(
|
||||
apiKey,
|
||||
apiSecret,
|
||||
payload,
|
||||
timestamp,
|
||||
);
|
||||
|
||||
// Send request with headers
|
||||
final response = await http.post(
|
||||
Uri.parse('https://your-api.com/api/v1/workouts'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': apiKey,
|
||||
'X-Signature': signature,
|
||||
'X-Timestamp': '$timestamp',
|
||||
},
|
||||
body: payload,
|
||||
);
|
||||
```
|
||||
|
||||
## Payload Format
|
||||
|
||||
### Workout Submission Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"workout_type": "running|walking",
|
||||
"distance_meters": 5000,
|
||||
"duration_seconds": 1800,
|
||||
"elevation_gain_meters": 150,
|
||||
"elevation_loss_meters": 100,
|
||||
"calories_burned": 350.5,
|
||||
"max_speed_mps": 4.5,
|
||||
"route_polyline": "encoded_polyline_string",
|
||||
"start_time": "2026-04-21T14:30:00.000Z",
|
||||
"end_time": "2026-04-21T14:45:00.000Z",
|
||||
"weather_condition": "sunny",
|
||||
"temperature_celsius": 22.5,
|
||||
"notes": "Great workout!",
|
||||
"is_public": false,
|
||||
"segments": [
|
||||
{
|
||||
"duration_seconds": 600,
|
||||
"distance_meters": 1500,
|
||||
"index_in_polyline": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Timestamp Validation**: Requests must be within 5 minutes of server time
|
||||
2. **Timing-Safe Comparison**: Signatures are verified using constant-time comparison
|
||||
3. **Replay Attack Prevention**: Each request timestamp is validated
|
||||
4. **HTTPS Only**: Always use HTTPS in production
|
||||
5. **Secret Rotation**: Implement API secret rotation periodically
|
||||
6. **Rate Limiting**: Consider implementing rate limits per API key
|
||||
|
||||
## Error Responses
|
||||
|
||||
### 401 Unauthorized
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Invalid API key",
|
||||
"timestamp": "2026-04-21T14:45:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 400 Bad Request
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Missing required authentication headers",
|
||||
"timestamp": "2026-04-21T14:45:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 422 Unprocessable Entity
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "Validation failed: distance_meters must be between 100 and 100000",
|
||||
"timestamp": "2026-04-21T14:45:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Success Response (201 Created)
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"workout_id": 123,
|
||||
"workout_uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"message": "Workout submitted successfully",
|
||||
"timestamp": "2026-04-21T14:45:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Rate Limits
|
||||
|
||||
- 100 requests per minute per API key
|
||||
- 5000 requests per day per API key
|
||||
- Rate limit headers included in response:
|
||||
- `X-RateLimit-Limit: 100`
|
||||
- `X-RateLimit-Remaining: 95`
|
||||
- `X-RateLimit-Reset: 1629907200`
|
||||
|
||||
## Testing HMAC Signature Generation
|
||||
|
||||
### Using cURL with debugging:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
API_KEY="your_api_key"
|
||||
API_SECRET="your_api_secret"
|
||||
TIMESTAMP=$(date +%s)
|
||||
ENDPOINT="https://your-api.com/api/v1/workouts"
|
||||
PAYLOAD='{"workout_type":"running","distance_meters":5000,"duration_seconds":1800,"elevation_gain_meters":0,"elevation_loss_meters":0,"calories_burned":350,"max_speed_mps":4.5,"route_polyline":"abc123","start_time":"2026-04-21T14:00:00Z","end_time":"2026-04-21T14:30:00Z"}'
|
||||
|
||||
MESSAGE="${TIMESTAMP}|${API_KEY}|${PAYLOAD}"
|
||||
SIGNATURE=$(echo -n "$MESSAGE" | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $NF}')
|
||||
|
||||
echo "Timestamp: $TIMESTAMP"
|
||||
echo "Signature: $SIGNATURE"
|
||||
echo "Payload: $PAYLOAD"
|
||||
|
||||
curl -X POST "$ENDPOINT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
-H "X-Signature: $SIGNATURE" \
|
||||
-H "X-Timestamp: $TIMESTAMP" \
|
||||
-d "$PAYLOAD"
|
||||
```
|
||||
|
||||
## API Key Management
|
||||
|
||||
### Getting Your API Credentials
|
||||
|
||||
Users receive their API credentials upon registration:
|
||||
|
||||
- API Key (64 character hex string)
|
||||
- API Secret (64 character hex string)
|
||||
|
||||
### Regenerating Credentials
|
||||
|
||||
API credentials can be regenerated from the user settings panel (invalidates old credentials immediately).
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. Store credentials securely (use OS keychain/secure storage)
|
||||
2. Never commit credentials to version control
|
||||
3. Use environment variables or secure configuration
|
||||
4. Rotate credentials periodically
|
||||
5. Monitor API logs for suspicious activity
|
||||
6. Use separate credentials for different apps/devices
|
||||
Reference in New Issue
Block a user