Skip to content

Authentication & Security

Security is critical for wallet integrations. Game Provider employs a dual-layer security model.

1. Signature Validation (HMAC-SHA256)

All requests from Game Provider to your Wallet API are signed. You must validate the signature on every inbound request.

Property Value
Algorithm HMAC-SHA256
Output encoding Base64 (standard, not URL-safe)
Headers sent X-Public-Key, X-Signature
Secret source Shared secret provided during onboarding, one per operator/environment
Payload that is signed The exact raw request body bytes sent over the wire (UTF-8 encoded JSON)
Secret key encoding UTF-8 bytes of the secret string

How to Validate

  1. Read the X-Public-Key header to identify the tenant and look up the corresponding secret key on your side.
  2. Read the X-Signature header.
  3. Compute Base64(HMAC_SHA256(secret_key_utf8, raw_request_body_bytes)).
  4. Compare with the header value using a constant-time comparison (e.g. MessageDigest.isEqual in Java, hmac.compare_digest in Python).

[!WARNING] Do not re-serialize the JSON body before computing the HMAC. The signature is computed over the exact bytes on the wire; any reformatting (key reordering, whitespace change, number formatting) will break the signature. Read the raw bytes from the HTTP request input stream, validate HMAC first, then parse JSON.

[!WARNING] If the signatures do not match, reject the request with HTTP 401 and do not process it. Never log the secret key or the signature value at non-debug levels.

2. IP Whitelisting

For additional defense-in-depth, you may whitelist Game Provider's outbound IP ranges. Contact support for the current production IP list; addresses vary per environment.

3. Code Samples (Generation & Validation)

Wallet API: You must validate the signature (Inbound). Gift API: You must generate a signature (Outbound).

Below are examples of how to do both.

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class SignatureHelper {

    // Use this to VALIDATE requests from Game Provider (Wallet API)
    public static boolean isValid(String secretKey, String rawBody, String headerSignature) {
        String computed = sign(secretKey, rawBody);
        return computed.equals(headerSignature);
    }

    // Use this to GENERATE signatures for requests to Game Provider (Gift API)
    public static String sign(String secretKey, String message) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(
                secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            mac.init(secretKeySpec);
            byte[] rawHmac = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(rawHmac);
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC", e);
        }
    }
}
using System;
using System.Security.Cryptography;
using System.Text;

public class SignatureHelper {
    // Use this to VALIDATE requests from Game Provider (Wallet API)
    public static bool IsValid(string secretKey, string rawBody, string headerSignature) {
        string computed = Sign(secretKey, rawBody);
        return computed == headerSignature;
    }

    // Use this to GENERATE signatures for requests to Game Provider (Gift API)
    public static string Sign(string secretKey, string message) {
        var encoding = new UTF8Encoding();
        byte[] keyBytes = encoding.GetBytes(secretKey);
        byte[] messageBytes = encoding.GetBytes(message);
        using (var hmac = new HMACSHA256(keyBytes)) {
            byte[] hashMessage = hmac.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashMessage);
        }
    }
}
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
)

// Use this to VALIDATE requests from Game Provider (Wallet API)
func IsValid(secretKey, rawBody, headerSignature string) bool {
    computed := Sign(secretKey, rawBody)
    return computed == headerSignature
}

// Use this to GENERATE signatures for requests to Game Provider (Gift API)
func Sign(secretKey, message string) string {
    key := []byte(secretKey)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(message))
    return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
import hmac
import hashlib
import base64

# Use this to VALIDATE requests from Game Provider (Wallet API)
def is_valid(secret_key, raw_body, header_signature):
    computed = sign(secret_key, raw_body)
    return computed == header_signature

# Use this to GENERATE signatures for requests to Game Provider (Gift API)
def sign(secret_key, message):
    key = bytes(secret_key, 'utf-8')
    msg = bytes(message, 'utf-8')
    raw_hmac = hmac.new(key, msg, hashlib.sha256).digest()
    return base64.b64encode(raw_hmac).decode('utf-8')