English
English
Appearance
English
English
Appearance
HMAC Authentication v1 ensures secure communication between external systems using cryptographic signatures to verify request integrity and authenticity.
HMAC (Hash-based Message Authentication Code) is a mechanism for verifying both the data integrity and authenticity of a message using a shared secret key.
In this integration:
| Feature | Description |
|---|---|
| Integrity | Detects any tampering with request data |
| Authenticity | Proves the request comes from authorized source |
| Replay Protection | Timestamp window + unique request ID prevents replay |
| No Password Transmission | Secret key never sent over the network |
✅ Service-to-Service Communication - Backend systems authenticating with each other
✅ API Gateway Integration - Secure routing between internal services
✅ Multi-Tenant Systems - Credential isolation per tenant/organization
┌────────────┐ ┌────────────────┐
│ OLT Mgmt │ │ External API │
│ (Client) │ │ (Server) │
└─────┬──────┘ └──────┬─────────┘
│ │
│ 1. Prepare request │
│ (method, path, headers, body) │
│ │
│ 2. Generate canonical request │
│ (standardized format) │
│ │
│ 3. Create string to sign │
│ (HMAC-SHA256 + timestamp + hash) │
│ │
│ 4. Sign with secret key │
│ (HMAC-SHA256 signature) │
│ │
│ 5. Send request with signature │
├────────────────────────────────────────────────>│
│ │
│ 6. Check timestamp │
│ (within 5 min) │
│ │
│ 7. Verify signature │
│ (recalculate & │
│ compare) │
│ │
│ 8. Response (success / error) │
│<────────────────────────────────────────────────┤
│ │Service Authentication:
Authorization: HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, SignedHeaders=x-date;x-request-id;x-client-type, Signature=3c92776b...
X-Date: 2025-10-14T03:52:12Z
X-Request-Id: bf6c5652-9656-4167-b1d4-10c690a72102
X-Client-Type: serviceUser Authentication:
Authorization: HMAC-SHA256 CredentialType=user, CredentialId=admin@telkom.com, SignedHeaders=x-date;x-tenant-id;x-request-id;x-client-type, Signature=5d83ac7f...
X-Date: 2025-10-14T03:52:12Z
X-Request-Id: af7d4523-8765-4278-a2e5-21d801b83456
X-Client-Type: web
X-Tenant-Id: isp-telkom| Type | Format | Usage |
|---|---|---|
| Service | CredentialType=service, CredentialId=external-system-client | System-to-system authentication |
| User | CredentialType=user, CredentialId=john@example.com | User-initiated requests (requires X-Tenant-Id) |
Service Authentication:
POST
/api/v1/users
filter=active&sort=name
x-client-type:service
x-date:2025-10-14T03:52:12Z
x-request-id:bf6c5652-9656-4167-b1d4-10c690a72102
x-date;x-request-id;x-client-type
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855User Authentication:
GET
/api/v1/onu/unconfigured
x-client-type:web
x-date:2025-10-14T03:52:12Z
x-request-id:af7d4523-8765-4278-a2e5-21d801b83456
x-tenant-id:isp-telkom
x-date;x-tenant-id;x-request-id;x-client-type
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855import crypto from 'crypto';
interface SignatureParams {
method: string;
url: string;
headers: Record<string, string>;
body: string;
secretKey: string;
isUserAuth?: boolean;
}
function calculateSignature(params: SignatureParams): string {
const { method, url, headers, body, secretKey, isUserAuth = false } = params;
const u = new URL(url);
const path = u.pathname;
const queryString = u.search.slice(1);
// Hash body
const bodyHash = crypto
.createHash('sha256')
.update(body || '')
.digest('hex');
// Determine signed headers based on auth type
const signedHeaderNames = isUserAuth
? ['x-client-type', 'x-date', 'x-request-id', 'x-tenant-id']
: ['x-client-type', 'x-date', 'x-request-id'];
// Build canonical headers (lowercase, sorted, with trailing newline)
const canonicalHeaders = signedHeaderNames
.map(name => `${name}:${headers[name]}`)
.join('\n') + '\n';
const signedHeaders = signedHeaderNames.join(';');
// Build canonical request
const canonicalRequest = [
method.toUpperCase(),
path,
queryString,
canonicalHeaders,
signedHeaders,
bodyHash
].join('\n');
// Create string to sign
const stringToSign = [
'HMAC-SHA256',
headers['x-date'],
crypto.createHash('sha256').update(canonicalRequest).digest('hex')
].join('\n');
// Calculate signature
return crypto
.createHmac('sha256', secretKey)
.update(stringToSign)
.digest('hex');
}
// Example Usage - Service Authentication
const serviceSignature = calculateSignature({
method: 'POST',
url: 'https://olt.remala.local/api/v1/users?filter=active&sort=name',
headers: {
'x-date': '2025-10-14T03:52:12Z',
'x-request-id': 'bf6c5652-9656-4167-b1d4-10c690a72102',
'x-client-type': 'service'
},
body: '',
secretKey: 'your-service-secret-key'
});
// Example Usage - User Authentication
const userSignature = calculateSignature({
method: 'GET',
url: 'https://olt.remala.local/api/v1/onu/unconfigured',
headers: {
'x-date': '2025-10-14T03:52:12Z',
'x-request-id': 'af7d4523-8765-4278-a2e5-21d801b83456',
'x-client-type': 'web',
'x-tenant-id': 'isp-telkom'
},
body: '',
secretKey: 'tenant-secret-key',
isUserAuth: true
});// 401 Unauthorized - Missing or invalid Authorization header
{
"error": "unauthorized",
"message": "Missing Authorization header"
}
// 401 Unauthorized - Invalid format
{
"error": "unauthorized",
"message": "Invalid Authorization format"
}
// 401 Unauthorized - Signature mismatch
{
"error": "unauthorized",
"message": "Invalid signature"
}
// 401 Unauthorized - Expired timestamp
{
"error": "unauthorized",
"message": "Signature expired"
}
// 401 Unauthorized - Missing required headers
{
"error": "unauthorized",
"message": "Missing required headers"
}
// 400 Bad Request - Malformed header
{
"error": "bad_request",
"message": "Invalid X-Date format. Expected ISO 8601 (UTC)"
}Check:
\n)CredentialType and CredentialId, not Credential=type:id)Solutions:
Common Issue:
❌ Wrong (old format):
Authorization: HMAC-SHA256 Credential=service:external-system-client, ...
✅ Correct (new format):
Authorization: HMAC-SHA256 CredentialType=service, CredentialId=external-system-client, ...❌ Using uppercase in header names (in canonical request) ❌ Wrong SignedHeaders order ❌ Missing trailing newline in canonical headers ❌ Excluding query string from canonical request ❌ Including Authorization header in SignedHeaders ❌ Wrong body hash algorithm (must be SHA256) ❌ Timezone mismatch in timestamp (must be UTC) ❌ Using old Authorization format (Credential=type:id)
| Feature | Service Authentication | User Authentication |
|---|---|---|
| Credential Format | CredentialType=service, CredentialId=slug | CredentialType=user, CredentialId=email |
| X-Tenant-Id Header | Not required | Required |
| Signed Headers | x-date;x-request-id;x-client-type | x-date;x-tenant-id;x-request-id;x-client-type |
| Use Case | Service-to-service (register ISP) | Tenant operations (ONU management) |
| Secret Key Source | Integration secret | Partner/tenant secret |
| Method | Pros | Cons | Use Case |
|---|---|---|---|
| HMAC | No password in transit, replay protection | Requires time sync, complex implementation | Service-to-service |
| JWT | Stateless, includes claims | Token can be stolen if not secured | User sessions |
| API Key | Simple | Key in every request, no integrity check | Public APIs |
| OAuth 2.0 | Industry standard, token refresh | Complex setup, requires auth server | Third-party access |