TL;DR
This guide shows how two independent apps can securely verify each other’s identity before exchanging data. We’ll use API keys and a simple challenge-response system.
Step 1: Generate API Keys for Each App
Each app needs its own unique API key. Think of these like passwords, but for apps communicating with each other. Don’t share them!
- App A Key Generation: Use a secure random string generator. For example:
openssl rand -base64 32This will output something like aBcDeFgHiJkLmNoPqRsTuVwXyZ12345
- App B Key Generation: Repeat the process for App B:
openssl rand -base64 32This will output something different, like zYxWvUtSrQpOnMlKjIhGfEdCbA9876
Store these keys securely within each app’s configuration.
Step 2: App A Initiates the Handshake
- App A sends a request to App B, including its API key. The endpoint on App B should be specifically for authentication (e.g., `/authenticate`).
- The request might look like this (using JSON):
POST /authenticate { "apiKey": "aBcDeFgHiJkLmNoPqRsTuVwXyZ12345" }
Step 3: App B Validates App A’s Key
- App B receives the request and checks if the provided API key matches its stored list of valid keys.
- If the key is invalid, return an error (e.g., HTTP 401 Unauthorized).
Step 4: App B Generates a Challenge
To further verify authenticity and prevent replay attacks, App B generates a random challenge.
- Challenge Generation: Create a unique string or number. A timestamp combined with a random value is good.
import time import uuid challenge = str(time.time()) + uuid.uuid4()
Step 5: App B Sends the Challenge to App A
App B sends the generated challenge back to App A.
- The response might look like this:
{ "challenge": "1678886400.12345abcde" }
Step 6: App A Signs the Challenge
- App A receives the challenge and signs it using its API key. This means combining the challenge with the key and applying a hashing function (like SHA-256).
- Signing Example (Python):
import hashlib import hmac apiKey = "aBcDeFgHiJkLmNoPqRsTuVwXyZ12345" challenge = "1678886400.12345abcde" message = challenge.encode('utf-8') key = apiKey.encode('utf-8') hmac_obj = hmac.new(key, message, hashlib.sha256) digested = hmac_obj.hexdigest() print(digested)This will produce a hash like e5b9d430c87a1f8763456789abcdef0123456789
Step 7: App A Sends the Signed Challenge Back to App B
App A sends the signed challenge back to App B.
- The request might look like this:
POST /verify_challenge { "apiKey": "aBcDeFgHiJkLmNoPqRsTuVwXyZ12345", "signedChallenge": "e5b9d430c87a1f8763456789abcdef0123456789" }
Step 8: App B Verifies the Signature
- App B receives the signed challenge and verifies it using App A’s API key. It performs the same hashing process as in Step 6, then compares the result to the received signature.
- If the signatures match, authentication is successful! If not, return an error.
Important Considerations
- HTTPS: Always use HTTPS for all communication to encrypt data in transit.
- Key Storage: Protect API keys like passwords – never hardcode them directly into your code. Use environment variables or secure configuration files.
- Rate Limiting: Implement rate limiting on the authentication endpoint to prevent brute-force attacks.
- Challenge Expiration: Make challenges expire after a short period (e.g., 30 seconds) to reduce the risk of replay attacks.

