How OpenWA Validates API Requests Using the X-API-Key Header

OpenWA validates API requests by extracting the key from the X-API-Key header (or Authorization: Bearer fallback), hashing it with SHA-256, and verifying it against the database while checking expiration dates, IP whitelists, and role permissions through the ApiKeyGuard and AuthService components.

The OpenWA platform secures its HTTP endpoints through a robust API key authentication system that validates every request using the X-API-Key header. When a client sends a request to any protected endpoint, the authentication flow orchestrated by NestJS guards ensures only authorized access through a multi-step validation process. According to the rmyndharis/OpenWA source code, this mechanism combines cryptographic hashing, database lookups, and granular permission checks to protect the WhatsApp automation API.

The Three-Component Validation Architecture

OpenWA's authentication flow relies on three core components working in sequence to validate requests with the X-API-Key header. The ApiKeyGuard (src/modules/auth/guards/api-key.guard.ts) acts as the entry point, extracting the header and resolving client context. The AuthService (src/modules/auth/auth.service.ts) performs the cryptographic verification and business logic checks. Finally, the AuthValidateController (src/modules/auth/auth-validate.controller.ts) exposes a diagnostic endpoint for testing key validity.

ApiKeyGuard intercepts every incoming request to protected routes (skipping those marked with @Public()). It extracts the raw key via extractApiKey, resolves the client IP address, and forwards the data to the authentication service. If the route requires specific permissions, the guard checks @RequiredRole() metadata against the key's authorization level.

AuthService.validateApiKey contains the core validation logic. It hashes the incoming raw key, queries the ApiKey entity, and enforces security constraints including expiration dates, IP whitelisting, and session restrictions. The service updates usage counters and timestamps for audit purposes upon successful validation.

AuthValidateController provides a POST /auth/validate endpoint that simply forwards received keys to validateApiKey and returns the resolved metadata, useful for debugging or UI-based key verification.

Step-by-Step API Key Validation Process

The validation of the X-API-Key header follows a strict sequence to ensure security and traceability. Each step is designed to prevent unauthorized access while maintaining performance.

Header Extraction and Fallback Handling

In src/modules/auth/guards/api-key.guard.ts, the extractApiKey method first inspects request.headers['x-api-key']. If this header is absent, it falls back to parsing the Authorization header for a Bearer <key> scheme. This dual-support ensures compatibility with standard HTTP client configurations while prioritizing the dedicated X-API-Key header.

Cryptographic Hashing for Secure Storage

Before any database comparison occurs, AuthService.hashKey computes a SHA-256 digest of the raw API key using Node.js's createHash('sha256').update(rawKey).digest('hex'). Only this hash is persisted in the api_keys table; the plaintext key never leaves the validation layer, preventing secret leakage in logs or database dumps.

Database Validation and Revocation Checks

The service queries the ApiKey entity using the hash as the lookup key. It verifies the isActive flag to detect revoked keys and checks the optional expiresAt timestamp against the current time. If either check fails, the service throws an UnauthorizedException, immediately rejecting the request.

IP Whitelisting and Network Constraints

If the API key defines allowedIps, the service invokes isIpAllowed to validate the request's remote IP address. This function supports both exact IP matching and CIDR range notation (e.g., 192.168.1.0/24), allowing flexible network-based access control for enterprise deployments.

Session-Specific Authorization

When allowedSessions is populated on the key entity, the guard extracts the sessionId from the route parameters and passes it to the service. The validation ensures the key is explicitly authorized for that specific WhatsApp session, preventing cross-session key reuse in multi-tenant environments.

Role-Based Permission Enforcement

After successful key validation, the guard checks @RequiredRole() metadata using AuthService.hasPermission. The system enforces a hierarchy of VIEWER, OPERATOR, and ADMIN roles, rejecting requests where the key's role lacks sufficient privileges for the endpoint.

Request Enrichment and Usage Auditing

Upon passing all checks, the validated ApiKey object is attached to the request as request.apiKey, allowing downstream controllers to access metadata without re-validating. The service increments usageCount and updates lastUsedAt, creating a complete audit trail for security monitoring.

Implementation Code Examples

Authenticating with cURL and X-API-Key Headers

Send a request to any protected endpoint by including the key in the header:

curl -X GET "http://localhost:2785/api/v1/contacts" \
     -H "Content-Type: application/json" \
     -H "X-API-Key: your-api-key"

If the key is valid, the API returns the requested data; otherwise, it returns a 401 Unauthorized status.

JavaScript SDK Integration

The OpenWA SDK automatically injects the X-API-Key header from configuration:

import OpenWA from 'openwa';

const client = new OpenWA({
  baseUrl: 'http://localhost:2785',
  apiKey: 'your-api-key',
});

await client.get('/api/v1/contacts');

Validating Keys via the Diagnostic Endpoint

Test key permissions and restrictions without hitting production data:

curl -X POST "http://localhost:2785/auth/validate" \
     -H "Content-Type: application/json" \
     -H "X-API-Key: your-api-key"

This returns the full ApiKey entity—including role, IP restrictions, and usage statistics—if the key passes all validation checks.

Summary

  • Dual header support: OpenWA accepts API keys via X-API-Key or Authorization: Bearer headers, extracted in ApiKeyGuard.
  • SHA-256 hashing: Raw keys are hashed before database comparison, ensuring secrets are never stored or transmitted in plaintext.
  • Multi-layer validation: The AuthService checks expiration, IP whitelists (including CIDR), session restrictions, and role hierarchies.
  • Audit trail: Successful validations update usageCount and lastUsedAt timestamps for security monitoring.
  • Request enrichment: Validated keys attach to the request object as request.apiKey for downstream access.

Frequently Asked Questions

What happens if the X-API-Key header is missing?

If the X-API-Key header is absent, OpenWA attempts to extract the key from the Authorization: Bearer <key> header as a fallback. If neither header contains a valid key format, the ApiKeyGuard throws an UnauthorizedException and returns a 401 status code, blocking access to the endpoint.

How does OpenWA store API keys securely?

OpenWA never stores the plaintext API key. In src/modules/auth/auth.service.ts, the hashKey method computes a SHA-256 digest of the raw key, and only this hexadecimal hash is persisted in the database. When validating requests, the incoming key is hashed using the same algorithm and compared against the stored hash.

Can I restrict API keys to specific IP addresses?

Yes, the ApiKey entity supports an allowedIps array that enforces IP-based restrictions during validation. The isIpAllowed method in AuthService validates client IPs against this list, supporting both exact matches and CIDR range notation (such as 10.0.0.0/8), allowing fine-grained network access control.

What is the difference between OPERATOR and ADMIN roles?

OpenWA implements a role hierarchy where ADMIN has full system access, OPERATOR can perform actions but may lack destructive or configuration-level permissions, and VIEWER has read-only access. The AuthService.hasPermission method compares the key's role against @RequiredRole() metadata on controllers, rejecting requests where the key's authorization level is insufficient.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →