> For the complete documentation index, see [llms.txt](https://apps.docs.rootflo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://apps.docs.rootflo.ai/authentication/server-to-server-auth.md).

# Server to Server Auth

### 📋 Overview

This document provides a complete guide for implementing HMAC authentication in your client applications. The authentication uses a nonce-based approach where you sign `nonce:timestamp` combinations for optimal security and performance.

### 🔐 Authentication Flow

#### Step 1: Set Up Credentials

Use your provided client key and client secret (hardcoded for now during development).

#### Step 2: Generate Authentication Headers

For each API request:

1. Generate a secure nonce
2. Get current timestamp
3. Create HMAC signature from `nonce:timestamp`
4. Include required headers in your request

#### Step 3: Make API Requests

Send your requests with proper authentication headers and nonce in the request body.

***

### 🚀 Getting Started

#### Client Credentials

For development purposes, use these hardcoded credentials:

```
const CLIENT_KEY = "<provided by admin>";
const CLIENT_SECRET = "provided by admin";
```

⚠️ **Important**: In production, these credentials should be provided securely and not hardcoded in your source code.

***

### 💻 Implementation

### Step 1: Generate the Signature.

Generate a random 32 length nonce (minimum length 32), and get the current timestamp in seconds. Now create `nonce:timestamp` string and generate HMAC using CLIENT\_SECRET

#### JavaScript Implementation

```
const crypto = require('crypto');
/**
 * Generates HMAC signature and timestamp for authentication
 * Note: nonce and clientSecret must be hex strings with at least 32 characters
 * @param {string} nonce - Random nonce value
 * @param {string} clientSecret - Client secret key
 * @returns {Object} Object containing signature and timestamp
 */
function generateSignature(nonce, clientSecret) {
    // Generate current timestamp is seconds
    const timestamp = Math.floor(Date.now() / 1000);;
    // Create message string
    const message = nonce + ":" + timestamp
    
    // Convert message to bytes
    const messageBytes = Buffer.from(message, 'utf8');
    
    // Generate HMAC signature
    const signature = crypto
        .createHmac('sha256', clientSecret)
        .update(messageBytes)
        .digest('hex');
    
    return {
        signature,
        timestamp
    };
}

// Example usage:
const nonce = crypto.randomBytes(16).toString('hex') // 32 hex characters
const clientSecret = "<CLIENT_SECRET>"; // 40 hex characters

const result = generateSignature(nonce, clientSecret);
console.log('Signature:', result.signature);
console.log('Timestamp:', result.timestamp);
console.log('Message format:', `${nonce}:${result.timestamp}`);
```

***

#### Python Implementation

```
import hashlib
import hmac
import time
import secrets


def generate_signature(nonce, client_secret):
    """
    Generates HMAC signature and timestamp for authentication
    Note: nonce and client_secret must be hex strings with at least 32 characters
    
    Args:
        nonce (str): Random nonce value
        client_secret (str): Client secret key
    
    Returns:
        dict: Dictionary containing signature and timestamp
    """
    # Generate current timestamp in seconds
    timestamp = int(time.time())
    
    # Create message string
    message = f"{nonce}:{timestamp}"
    
    # Convert message to bytes
    message_bytes = message.encode('utf-8')
    
    # Convert client_secret to bytes
    client_secret_bytes = client_secret.encode('utf-8')
    
    # Generate HMAC signature
    signature = hmac.new(
        client_secret_bytes,
        message_bytes,
        hashlib.sha256
    ).hexdigest()
    
    return {
        'signature': signature,
        'timestamp': timestamp
    }

# Example usage:
if __name__ == "__main__":
    nonce =  secrets.token_hex(32 // 2) # 32 hex characters
    client_secret = "<CLIENT_SECRET>"; # 40 hex characters
    
    result = generate_signature(nonce, client_secret)
    print(f"Nonce: {nonce}")
    print(f"Signature: {result['signature']}")
    print(f"Timestamp: {result['timestamp']}")
    print(f"Message format: {nonce}:{result['timestamp']}")
```

**Java Implementation**

```
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

public class HMACSignatureGenerator {
    
    /**
     * Generates a random hexadecimal nonce of specified length
     * @param length Length of the hex string (must be even)
     * @return Random hex string
     */
    public static String generateHexNonce(int length) {
        byte[] bytes = new byte[length / 2];
        new SecureRandom().nextBytes(bytes);
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    
    /**
     * Generates HMAC signature and timestamp for authentication
     * Note: nonce and clientSecret must be hex strings with at least 32 characters
     * 
     * @param nonce Random nonce value
     * @param clientSecret Client secret key
     * @return Map containing signature and timestamp
     * @throws NoSuchAlgorithmException if HMAC-SHA256 algorithm is not available
     * @throws InvalidKeyException if the client secret is invalid
     */
    public static Map<String, Object> generateSignature(String nonce, String clientSecret) 
            throws NoSuchAlgorithmException, InvalidKeyException {
        
        // Generate current timestamp
        long timestamp = System.currentTimeMillis() / 1000;
        
        // Create message string
        String message = nonce + ":" + timestamp;
        
        // Convert message to bytes
        byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        
        // Convert client secret to bytes
        byte[] clientSecretBytes = clientSecret.getBytes(StandardCharsets.UTF_8);
        
        // Create HMAC-SHA256 instance
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecretBytes, "HmacSHA256");
        mac.init(secretKeySpec);
        
        // Generate HMAC signature
        byte[] signatureBytes = mac.doFinal(messageBytes);
        
        // Convert signature to hex string
        String signature = bytesToHex(signatureBytes);
        
        // Create result map
        Map<String, Object> result = new HashMap<>();
        result.put("signature", signature);
        result.put("timestamp", timestamp);
        
        return result;
    }
    
    /**
     * Converts byte array to hexadecimal string
     * @param bytes Byte array to convert
     * @return Hexadecimal string representation
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
    
    // Example usage
    public static void main(String[] args) {
        try {
            String nonce = generateHexNonce(32); // 32 hex characters
            String clientSecret = "<CLIENT_SECRET>"; // 64 hex characters
            
            Map<String, Object> result = generateSignature(nonce, clientSecret);
            
            System.out.println("Nonce: " + nonce);
            System.out.println("Signature: " + result.get("signature"));
            System.out.println("Timestamp: " + result.get("timestamp"));
            System.out.println("Message format: " + nonce + ":" + result.get("timestamp"));
            
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
}
```

***

### 📝Step 2: Add the generated signatures to Headers and make request

When making any authenticated API request, you **must** include these headers:

**Content-Type:** `application/json` **X-Signature:** HMAC signature of nonce:timestamp, which is generated above**X-Timestamp:** Current Unix timestamp in **secondsX-Client-Key:** Your client key, provided by admin

#### Header Example:

```
'Content-Type': 'application/json'
'X-Rootflo-Signature': 'calculated_signature_here'
'X-Rootflo-Timestamp': 'timestamp_in_seconds'
'X-Rootflo-Key': 'client_key'
'X-Rootflo-Nonce': 'nonce' # optional, can be part of the body too
```

***

### 🎯 Request Body Format

Every authenticated request must include a `nonce` field in the request body:

```
{ 
  "nonce": "your_generated_nonce_here", # optional, can be placed in the header as well
  "image": "data:image/png;base64,{__image__}" 
  "metadata": {
     "loan_id" "--",
     ... other fields
  }
}
```

&#x20;

**Note**: The **nonce** can either be sent in the **header or in the body**

### 🧪 Testing Your Implementation

#### Test Checklist

* ✅ Use provided hardcoded credentials for development
* ✅ Generate unique nonce for each request (64-character hex)
* ✅ Create correct HMAC signature from `nonce:timestamp`
* ✅ Include all required headers in requests
* ✅ Include nonce in request body
* ✅ Handle authentication errors gracefully

#### Example Curl Request

```
curl -X POST https://staging-app-gold.rootflo.ai/floware/v1/image/analyse \
-H "Content-Type: application/json" \
-H "X-Rootflo-Signature: your_calculated_signature" \
-H "X-Rootflo-Timestamp: 1752139352" \
-H "X-Rootflo-Key: client_key" \
-d '{"nonce": "d800547cc75e4ed0a871421f9cb297f1...", "image": "image123", "metadata": {"customer": "abc"}}'
```

***

### ⚠️ Important Security Notes

#### Best Practices

* ✅ **Never hardcode credentials** in your source code
* ✅ **Store credentials securely** using environment variables or secure vaults
* ✅ **Generate fresh nonce** for every request
* ✅ **Use current timestamps** (not cached values)
* ✅ **Validate response status** before processing data
* ✅ **Use HTTPS** for all API communications

#### Common Mistakes to Avoid

* ❌ Don't reuse nonces across requests
* ❌ Don't cache timestamps for multiple requests
* ❌ Don't expose credentials in client-side JavaScript
* ❌ Don't ignore authentication errors
* ❌ Don't use predictable nonce values

***

### 📞 Support

If you encounter issues with authentication:

1. **Verify credentials:** Ensure you're using the correct hardcoded client key and secret
2. **Check nonce uniqueness:** Each request must use a fresh, unique nonce
3. **Validate signature:** Ensure you're signing `nonce:timestamp` with the correct client secret
4. **Confirm headers:** All four required headers must be present and correctly formatted
5. **Test timestamp:** Ensure your system clock is synchronized

***

*This guide provides everything you need to implement secure HMAC authentication for our API. Follow the examples and best practices for reliable, secure integration.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://apps.docs.rootflo.ai/authentication/server-to-server-auth.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
