Skip to content

conductor-oss/httpbin

Repository files navigation

HttpBin Service

Overview

HttpBin is a microservice designed for testing HTTP-related scenarios, network resilience, and simulating various service conditions. It provides endpoints that help developers test error handling, latency, and dependency interactions.

Prerequisites

  • Java JDK 17+
  • Gradle

Build & Run

Build Command

./gradlew build

Run Service

java -jar build/libs/httpbin-0.0.1-SNAPSHOT.jar

Swagger Documentation

JSON Format (Default)

- /api-docs -> Public API documentation without authentication
- /api-docs/secured -> Bearer token required `**Bearer test-token-123**`
- /api-docs/multi-server -> 4 servers (dev/staging/prod/local)
- /api-docs/staging - 2 servers (staging/local)

YAML Format

- /yaml/api-docs.yaml -> Public API documentation in YAML format
- /yaml/api-docs/secured.yaml -> Bearer token required `**Bearer test-token-123**` (YAML format)
- /yaml/api-docs/multi-server.yaml -> 4 servers (dev/staging/prod/local) (YAML format)
- /yaml/api-docs/staging.yaml -> 2 servers (staging/local) (YAML format)

Endpoints

- GET /api/hello?name=Alice
- GET /api/hello/with-delay?name=Alice
- GET /api/hello/intermittent-failures?name=Alice
- GET /api/hello/service-down?name=Alice
- GET /api/hello/overloaded?name=Alice
- GET /api/hello/failure-types?name=Alice
- GET /api/hello/external-dependency?name=Alice

Protected Endpoints (Require Basic Auth with testuser/testpass):

GET /api/auth/basic - Main basic auth test endpoint
GET /api/auth/userinfo - User information
POST /api/auth/data - POST endpoint with auth
GET /api/auth/protected - Another protected resource

OAuth2 Protected Endpoints (Require Okta JWT Bearer Token):

GET /api/oauth/protected - Main OAuth2 protected endpoint (returns token details)
GET /api/oauth/userinfo - OAuth2 user information and JWT claims
POST /api/oauth/data - POST endpoint with OAuth2 authentication
GET /api/oauth/validate - Simple token validation endpoint

OAuth2 Configuration

Getting an OAuth2 Token

curl -X POST https://trial-5299397.okta.com/oauth2/default/v1/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=0oavy0g8zojZWpY3X697" \
  -d "client_secret=jHIhlwWz0uOWhpJGzOc1hukJF1s2RpLVtpC8bIyL8lzG6xxChtPeXMunazyCQvdt" \
  -d "scope=openid"

Testing OAuth2 Endpoint

# Get token
TOKEN=$(curl -X POST https://trial-5299397.okta.com/oauth2/default/v1/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=0oavy0g8zojZWpY3X697&client_secret=jHIhlwWz0uOWhpJGzOc1hukJF1s2RpLVtpC8bIyL8lzG6xxChtPeXMunazyCQvdt&scope=openid" \
  | jq -r '.access_token')

# Call protected endpoint
curl -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/oauth/protected

HMAC Authentication Endpoints

The service provides production-like HMAC-SHA256 authentication endpoints that simulate real-world API authentication patterns similar to AWS Signature v4. These endpoints are useful for testing HMAC-based authentication in Orkes Conductor workflows.

HMAC Endpoints

GET  /api/auth/hmac/validate     - Validate HMAC signature (GET request)
POST /api/auth/hmac/validate     - Validate HMAC signature with request body (POST request)
GET  /api/auth/hmac/credentials  - Get test credentials
GET  /api/auth/hmac/helper       - Helper to generate signatures for testing

HMAC Configuration

  • Algorithm: HMAC-SHA256
  • Signing Key: test-signing-key-12345
  • Access Key: test-access-key-123
  • Timestamp Tolerance: 300 seconds (5 minutes)
  • Required Headers:
    • Authorization: HMAC-SHA256 AccessKey=<key>, Signature=<signature>
    • X-Timestamp: <unix-timestamp-seconds>
    • Content-Type: application/json
    • Host: localhost:8081

Canonical Request Format

The signature is calculated using a canonical request that includes:

  1. HTTP Method (GET, POST, etc.)
  2. Request URI (the path)
  3. Canonical Query String (sorted query parameters)
  4. Canonical Headers (content-type and host, lowercase, sorted)
  5. Signed Headers (list of header names: "content-type;host")
  6. Payload Hash (SHA-256 hash of request body, empty string for GET)

String to Sign Format:

AccessKey + Timestamp + CanonicalRequest

Testing HMAC Authentication

Step 1: Get Test Credentials

curl http://localhost:8081/api/auth/hmac/credentials

Step 2: Use Helper Endpoint to Generate Signature

# For GET request
curl "http://localhost:8081/api/auth/hmac/helper?method=GET&path=/api/auth/hmac/validate"

# For GET request with query parameters
curl "http://localhost:8081/api/auth/hmac/helper?method=GET&path=/api/auth/hmac/validate&queryParams=param1=value1%26param2=value2"

# For POST request with body
curl "http://localhost:8081/api/auth/hmac/helper?method=POST&path=/api/auth/hmac/validate&body=%7B%22test%22:%22data%22%7D"

Step 3: Test HMAC Validation

# Example GET request (copy the example_curl from helper response)
TIMESTAMP=$(date +%s)
# Use the signature generated by helper endpoint
curl -H "Authorization: HMAC-SHA256 AccessKey=test-access-key-123, Signature=<your-signature>" \
     -H "X-Timestamp: $TIMESTAMP" \
     -H "Content-Type: application/json" \
     -H "Host: localhost:8081" \
     http://localhost:8081/api/auth/hmac/validate

# Example POST request with body
curl -X POST \
     -H "Authorization: HMAC-SHA256 AccessKey=test-access-key-123, Signature=<your-signature>" \
     -H "X-Timestamp: $TIMESTAMP" \
     -H "Content-Type: application/json" \
     -H "Host: localhost:8081" \
     -d '{"test":"data"}' \
     http://localhost:8081/api/auth/hmac/validate

HMAC Implementation Details

The HMAC implementation follows production patterns similar to AWS Signature v4:

  • Actual request data: Uses real request path, headers, query parameters, and body
  • Query parameter sorting: Parameters are sorted alphabetically before signing
  • Body hashing: Request body is hashed with SHA-256 for POST/PUT requests
  • Replay protection: Timestamp validation prevents replay attacks (5-minute window)
  • Detailed error messages: Returns expected vs received signatures for debugging

Example: Calculating HMAC Signature Manually

import hmac
import hashlib
import base64
import time

# Credentials
access_key = "test-access-key-123"
signing_key = "test-signing-key-12345"

# Request details
method = "GET"
path = "/api/auth/hmac/validate"
query_string = ""  # Empty for no query params
timestamp = str(int(time.time()))

# Build canonical request
canonical_request = f"{method}\n{path}\n{query_string}\ncontent-type:application/json\nhost:localhost:8081\n\ncontent-type;host\n{base64.b64encode(hashlib.sha256(b'').digest()).decode()}"

# Build string to sign
string_to_sign = access_key + timestamp + canonical_request

# Calculate signature
signature = base64.b64encode(
    hmac.new(
        signing_key.encode(),
        string_to_sign.encode(),
        hashlib.sha256
    ).digest()
).decode()

print(f"Authorization: HMAC-SHA256 AccessKey={access_key}, Signature={signature}")
print(f"X-Timestamp: {timestamp}")

AWS Signature v4 Authentication Endpoints

Production-like implementation of AWS Signature Version 4 (AWS SigV4) authentication, similar to AWS API authentication. Perfect for testing AWS-style authentication in Orkes Conductor workflows.

AWS Signature v4 Endpoints

GET  /api/auth/aws/validate     - Validate AWS Signature v4 (GET request)
POST /api/auth/aws/validate     - Validate AWS Signature v4 with request body (POST request)
GET  /api/auth/aws/credentials  - Get test credentials
GET  /api/auth/aws/helper       - Helper to generate AWS signatures for testing

AWS Signature v4 Configuration

  • Algorithm: AWS4-HMAC-SHA256
  • Access Key ID: AKIAIOSFODNN7EXAMPLE
  • Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  • Region: us-east-1
  • Service: httpbin
  • Timestamp Tolerance: 900 seconds (15 minutes)
  • Required Headers:
    • Authorization: AWS4-HMAC-SHA256 Credential=<access-key>/<date>/<region>/<service>/aws4_request, SignedHeaders=<headers>, Signature=<signature>
    • X-Amz-Date: <ISO8601-timestamp> (format: YYYYMMDDTHHMMSSZ)

AWS Signature v4 Format

The signature follows AWS Signature Version 4 signing process:

  1. Canonical Request: Method + URI + Query String + Headers + Signed Headers + Payload Hash
  2. String to Sign: Algorithm + Timestamp + Credential Scope + Hash(Canonical Request)
  3. Signing Key: Derived from secret key, date, region, and service
  4. Signature: HMAC-SHA256(String to Sign, Signing Key)

Credential Scope Format: YYYYMMDD/region/service/aws4_request

Testing AWS Signature v4

Step 1: Get Test Credentials

curl http://localhost:8081/api/auth/aws/credentials

Step 2: Use Helper Endpoint to Generate Signature

# For GET request
curl "http://localhost:8081/api/auth/aws/helper?method=GET&path=/api/auth/aws/validate"

# For POST request with body
curl "http://localhost:8081/api/auth/aws/helper?method=POST&path=/api/auth/aws/validate&body=%7B%22test%22:%22data%22%7D"

Step 3: Test AWS Signature Validation

# Copy the example_curl from helper response
# Example:
curl -X GET \
  -H "Authorization: AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20241016/us-east-1/httpbin/aws4_request, SignedHeaders=host;x-amz-date, Signature=<signature>" \
  -H "X-Amz-Date: 20241016T120000Z" \
  -H "Host: localhost:8081" \
  http://localhost:8081/api/auth/aws/validate

Example: AWS Signature v4 Calculation

import hmac
import hashlib
from datetime import datetime

# Credentials
access_key_id = "AKIAIOSFODNN7EXAMPLE"
secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
region = "us-east-1"
service = "httpbin"

# Request details
method = "GET"
uri = "/api/auth/aws/validate"
amz_date = datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
date_stamp = amz_date[:8]

# Step 1: Create canonical request
canonical_request = f"{method}\n{uri}\n\nhost:localhost:8081\nx-amz-date:{amz_date}\n\nhost;x-amz-date\n{hashlib.sha256(b'').hexdigest()}"

# Step 2: Create string to sign
credential_scope = f"{date_stamp}/{region}/{service}/aws4_request"
string_to_sign = f"AWS4-HMAC-SHA256\n{amz_date}\n{credential_scope}\n{hashlib.sha256(canonical_request.encode()).hexdigest()}"

# Step 3: Calculate signing key
def sign(key, msg):
    return hmac.new(key, msg.encode(), hashlib.sha256).digest()

k_date = sign(("AWS4" + secret_access_key).encode(), date_stamp)
k_region = sign(k_date, region)
k_service = sign(k_region, service)
k_signing = sign(k_service, "aws4_request")

# Step 4: Calculate signature
signature = hmac.new(k_signing, string_to_sign.encode(), hashlib.sha256).hexdigest()

print(f"Authorization: AWS4-HMAC-SHA256 Credential={access_key_id}/{credential_scope}, SignedHeaders=host;x-amz-date, Signature={signature}")
print(f"X-Amz-Date: {amz_date}")

RSA Signature Authentication Endpoints

Production-like implementation of RSA signature authentication (SHA256withRSA), commonly used in webhooks, API callbacks, and JWT. Perfect for testing asymmetric cryptography-based authentication.

RSA Signature Endpoints

GET  /api/auth/rsa/validate  - Validate RSA signature (GET request)
POST /api/auth/rsa/validate  - Validate RSA signature with request body (POST request)
GET  /api/auth/rsa/keys      - Get public key for verification
GET  /api/auth/rsa/helper    - Helper to generate RSA signatures for testing

RSA Signature Configuration

  • Algorithm: SHA256withRSA
  • Key Size: 2048 bits
  • Timestamp Tolerance: 300 seconds (5 minutes)
  • Required Headers:
    • X-Signature: <base64-encoded-rsa-signature>
    • X-Timestamp: <unix-timestamp-seconds>

RSA Signature Format

The signature is calculated over: METHOD + URI + TIMESTAMP + BODY

  1. Concatenate: HTTP method + request URI + timestamp + request body (if any)
  2. Sign with RSA private key using SHA256withRSA
  3. Base64 encode the signature
  4. Send in X-Signature header

Testing RSA Signature

Step 1: Get Public Key

curl http://localhost:8081/api/auth/rsa/keys

Step 2: Use Helper Endpoint to Generate Signature

# For GET request
curl "http://localhost:8081/api/auth/rsa/helper?method=GET&path=/api/auth/rsa/validate"

# For POST request with body
curl "http://localhost:8081/api/auth/rsa/helper?method=POST&path=/api/auth/rsa/validate&body=%7B%22test%22:%22data%22%7D"

Step 3: Test RSA Signature Validation

# Copy the example_curl from helper response
# Example:
TIMESTAMP=$(date +%s)
curl -X GET \
  -H "X-Signature: <base64-signature>" \
  -H "X-Timestamp: $TIMESTAMP" \
  http://localhost:8081/api/auth/rsa/validate

Example: RSA Signature Calculation

from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
import base64
import time

# Load keys (get public key from /api/auth/rsa/keys endpoint)
# Private key is kept on server, use /helper endpoint for testing

# For signing (server-side or testing):
method = "GET"
path = "/api/auth/rsa/validate"
timestamp = str(int(time.time()))
body = ""  # Empty for GET

# Build data to sign
data_to_sign = method + path + timestamp + body

# Sign with private key (use helper endpoint in practice)
# signature = sign_with_private_key(data_to_sign)
# signature_base64 = base64.b64encode(signature).decode()

# For verification (client-side):
# public_key = RSA.import_key(public_key_pem)
# h = SHA256.new(data_to_sign.encode())
# pkcs1_15.new(public_key).verify(h, base64.b64decode(signature_base64))

RSA Use Cases

RSA signature authentication is commonly used for:

  • Webhook Verification: Validate incoming webhooks (GitHub, Stripe, etc.)
  • API Callbacks: Verify callbacks from external services
  • JWT Alternatives: Non-JWT token-based authentication
  • Service-to-Service: Secure service communication without shared secrets

Circuit Breaker Testing Endpoint

The /api/degradation endpoint simulates a time-based service lifecycle that demonstrates the complete circuit breaker pattern, including the recovery phase. This endpoint is ideal for testing resilience patterns like circuit breakers in a realistic environment.

How It Works

The endpoint simulates a service that cycles through four distinct phases:

  1. NORMAL Phase: Service operates with minimal delay
  2. DEGRADING Phase: Service gradually slows down (response times increase)
  3. FAILING Phase: Service consistently fails with 500 errors (triggers circuit breaker open)
  4. RECOVERING Phase: Service gradually returns to normal (allows circuit breaker to transition from open to half-open to closed)

The service automatically cycles through these phases based on configured time periods, regardless of request frequency.

Usage

GET /api/degradation?normalPeriod=30&degradationPeriod=60&failurePeriod=30&recoveryPeriod=60

Parameters

Parameter Description Default
normalPeriod Duration of normal operation (seconds) 30
degradationPeriod Duration of progressive degradation (seconds) 60
failurePeriod Duration of failure state (seconds) 30
recoveryPeriod Duration of recovery phase (seconds) 60
initialDelay Starting response time (milliseconds) 50
degradationRate Rate at which delay increases during degradation 100
failureThreshold Delay threshold at which service fails (ms) 2000

Contribution

Feel free to extend this service with more failure scenarios or testing endpoints.

Bearer Token Authentication Endpoints

The service provides Bearer Token authentication with refresh capability, simulating OAuth2-style token management for testing Orkes authentication configurations.

Bearer Token Endpoints

POST /api/auth/bearer/token      - Request a new bearer token
POST /api/auth/bearer/refresh    - Refresh an existing bearer token
GET  /api/auth/bearer/validate   - Validate a bearer token
GET  /api/auth/bearer/protected  - Protected endpoint requiring bearer token
GET  /api/auth/bearer/stats      - Token statistics (for testing)

Bearer Token Configuration

  • Client ID: test_client
  • Client Secret: test_secret
  • Token TTL: 3600 seconds (1 hour)
  • Token Format: bearer_<uuid>
  • Refresh Token Format: refresh_<uuid>

Getting a Bearer Token

Step 1: Request Initial Token

curl -X POST http://localhost:8081/api/auth/bearer/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "test_client",
    "client_secret": "test_secret"
  }'

# Response:
{
  "access_token": "bearer_<uuid>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "refresh_<uuid>",
  "scope": "read write"
}

Step 2: Refresh Token (Before Expiry - Returns Same Token)

curl -X POST http://localhost:8081/api/auth/bearer/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "refresh_<uuid>",
    "client_secret": "test_secret"
  }'

# Response (if token not expired):
{
  "access_token": "bearer_<same_uuid>",  # Same token returned
  "token_type": "Bearer",
  "expires_in": 2400,  # Remaining TTL
  "refresh_token": "refresh_<uuid>",
  "scope": "read write",
  "refresh_count": 0
}

Step 3: Use Bearer Token

# Validate token
curl http://localhost:8081/api/auth/bearer/validate \
  -H "Authorization: Bearer bearer_<uuid>"

# Access protected endpoint
curl http://localhost:8081/api/auth/bearer/protected \
  -H "Authorization: Bearer bearer_<uuid>"

Orkes Authentication Config for Bearer Token

{
  "secretName": "httpbin_bearer_auth_secret",
  "version": 1,
  "secretConfig": {
    "type": "BEARER_TOKEN",
    "accessToken": "<bearer_token_from_response>",
    "refreshToken": "<refresh_token_from_response>",
    "clientSecret": "test_secret",
    "clientId": "test_client",
    "refreshEndpoint": "http://localhost:8081/api/auth/bearer/refresh",
    "refreshMethod": "POST",
    "refreshHeaders": {
      "Content-Type": "application/json"
    },
    "refreshBodyTemplate": "{\"refresh_token\":\"{{refresh_token}}\",\"client_secret\":\"{{client_secret}}\"}",
    "accessTokenPath": "$.access_token",
    "refreshTokenPath": "$.refresh_token",
    "expiresInPath": "$.expires_in",
    "autoRefresh": true,
    "refreshThresholdSec": 300
  }
}

API Key Authentication Endpoints

The service provides API Key authentication with refresh capability, simulating services like Stripe that support API key rotation.

API Key Endpoints

POST /api/auth/apikey/create           - Create a new API key
POST /api/auth/apikey/refresh          - Refresh an existing API key
GET  /api/auth/apikey/validate         - Validate an API key
GET  /api/auth/apikey/protected        - Protected endpoint requiring API key
POST /api/auth/apikey/protected/data   - POST endpoint requiring API key
GET  /api/auth/apikey/stats            - API key statistics (for testing)

API Key Configuration

  • Account ID: test_account
  • Account Secret: test_account_secret
  • Key TTL: 2592000 seconds (30 days)
  • Key Format: sk_test_<uuid>
  • Refresh Secret Format: refresh_<uuid>
  • Supports multiple header formats: X-API-Key, Authorization, Authorization: Bearer, Authorization: ApiKey

Getting an API Key

Step 1: Create Initial API Key

curl -X POST http://localhost:8081/api/auth/apikey/create \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "test_account",
    "account_secret": "test_account_secret",
    "header_name": "Authorization",
    "header_prefix": "Bearer "
  }'

# Response:
{
  "api_key": "sk_test_<uuid>",
  "refresh_secret": "refresh_<uuid>",
  "expires_in": 2592000,
  "expires_at": "2025-11-19T...",
  "header_name": "Authorization",
  "header_prefix": "Bearer "
}

Step 2: Refresh API Key (Before Expiry - Returns Same Key)

curl -X POST http://localhost:8081/api/auth/apikey/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "sk_test_<uuid>",
    "refresh_secret": "refresh_<uuid>"
  }'

# Response (if key not expired):
{
  "api_key": "sk_test_<same_uuid>",  # Same key returned
  "expires_in": 2000000,  # Remaining TTL
  "expires_at": "2025-11-19T...",
  "refresh_count": 0,
  "message": "API key is still valid"
}

Step 3: Use API Key

# Validate with X-API-Key header
curl http://localhost:8081/api/auth/apikey/validate \
  -H "X-API-Key: sk_test_<uuid>"

# Validate with Authorization header (Bearer prefix)
curl http://localhost:8081/api/auth/apikey/validate \
  -H "Authorization: Bearer sk_test_<uuid>"

# Access protected endpoint
curl http://localhost:8081/api/auth/apikey/protected \
  -H "Authorization: Bearer sk_test_<uuid>"

# POST to protected endpoint
curl -X POST http://localhost:8081/api/auth/apikey/protected/data \
  -H "Authorization: Bearer sk_test_<uuid>" \
  -H "Content-Type: application/json" \
  -d '{"data": "test payload"}'

Orkes Authentication Config for API Key

{
  "secretName": "httpbin_apikey_auth_secret",
  "version": 1,
  "secretConfig": {
    "type": "API_KEY",
    "apiKey": "<api_key_from_response>",
    "refreshSecret": "<refresh_secret_from_response>",
    "headerName": "Authorization",
    "headerPrefix": "Bearer ",
    "refreshEndpoint": "http://localhost:8081/api/auth/apikey/refresh",
    "refreshMethod": "POST",
    "refreshBodyTemplate": "{\"api_key\":\"{{current_api_key}}\",\"refresh_secret\":\"{{refresh_secret}}\"}",
    "accessTokenPath": "$.api_key",
    "expiresInPath": "$.expires_in",
    "autoRefresh": true,
    "refreshThresholdSec": 600
  }
}

Token Lifecycle Behavior

Both Bearer Token and API Key implementations follow the Orkes specification:

  1. Token Not Expired: When refresh is called before expiry, the same token is returned with updated TTL
  2. Token Expired: When refresh is called after expiry, a new token is generated
  3. Refresh Count: Tracks how many times a token has been refreshed (useful for testing)
  4. Auto-Refresh: Orkes can use refreshThresholdSec to refresh tokens proactively before they expire

Testing Token Refresh Behavior

# 1. Create a token/key with short TTL for testing
# 2. Check remaining TTL
curl http://localhost:8081/api/auth/bearer/validate -H "Authorization: Bearer <token>"

# 3. Wait until close to expiry (within refreshThresholdSec)
# 4. Refresh - should get same token if not expired
# 5. Wait until after expiry
# 6. Refresh - should get new token

# Check stats to see active tokens
curl http://localhost:8081/api/auth/bearer/stats
curl http://localhost:8081/api/auth/apikey/stats

Contribution

Feel free to extend this service with more failure scenarios or testing endpoints.

About

Test server for all things HTTP

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors