Authentication
- YGG Staking APIs provide read-only access to staking data via HTTP RESTful APIs.
- Partners must use HMAC signature verification to ensure data integrity and authenticity.
- Because it's read-only, so request signature is 1-way only:
- Request signature is not required
- Response signature is included in the response headers
- Response signature is needed to verify data integrity and authenticity
HMAC Signature Verification
All API responses from YGG include an HMAC signature. Partners must verify this signature to ensure data hasn't been tampered with.
Request Headers (sent by Partner):
| Header | Description | Example Value |
|---|---|---|
X-API-KEY | Partner API key | your_api_key |
Content-Type | Request content type | application/json |
Response Headers (received from YGG):
| Header | Description | Example Value |
|---|---|---|
X-API-SIGNATURE | HMAC Signature (YGG generated) | 92aa703cc483ea2cc90488c05e699179... |
Important Security Notes
Keep these secure:
SHARED_SECRET_KEY: Provided by YGG, used for HMAC signature generation & verificationX-API-KEY: Provided by YGG, shared securely during integration
Key Components
X-API-SIGNATURE: HMAC signature generated by YGG, covering method, path, and response body. Any modification to the response invalidates the signature.
Read-Only API with Response Signature
Important: For Staking APIs, YGG generates the signature in the response, and Partners verify it to ensure data integrity.
The Staking API authentication flow:
- Partner → YGG: Partner makes request with
X-API-KEY - YGG → Partner: YGG returns data with
X-API-SIGNATUREin response headers - Partner verifies: Partner verifies the signature to ensure data hasn't been tampered with
This approach ensures:
- Data integrity: Partner can verify the response came from YGG
- Tamper detection: Any modification to the response invalidates the signature
- Read-only security: Appropriate for query operations
HMAC Signature Verification (Partner Side)
When YGG responds to your API request, it includes an X-API-SIGNATURE header. You must verify this signature to ensure the response is authentic and hasn't been tampered with.
Step 1: Extract Response Components
From the YGG API response, extract:
const method = 'GET' // The HTTP method you used
const path = '/api/staking/snapshots' // The request path
const responseBody = response.data // The response JSON
const receivedSignature = response.headers['x-api-signature'] // YGG's signatureStep 2: Create Canonical Payload
Build the same payload that YGG used to generate the signature:
payload = method + path + JSON.stringify(responseBody)Payload components:
- HTTP Method: The method you used in the request (
GET) - Request Path:
/api/staking/snapshots - Response Body: JSON stringified using standard
JSON.stringify()
The response body must be stringified using the standard JSON.stringify() method. No need to sort keys.
Step 3: Verify HMAC Signature
// Generate expected signature using shared secret
const expectedSignature = hmac_sha256(SHARED_SECRET_KEY, payload)
// Compare with received signature (use constant-time comparison)
const isValid = constantTimeCompare(expectedSignature, receivedSignature)Complete Example
Here's a full implementation example showing how to verify YGG's response signature:
const crypto = require('crypto')
// Configuration
const API_KEY = 'your_api_key'
const SHARED_SECRET_KEY = 'shared_secret_key_from_ygg'
const API_BASE_URL = 'YGG_API_BASE_URL'
function verifyResponseSignature(method, path, responseBody, receivedSignature) {
// Step 1: Create canonical payload (same as YGG used)
const payload = method + path + JSON.stringify(responseBody)
// Step 2: Generate expected signature
const expectedSignature = crypto.createHmac('sha256', SHARED_SECRET_KEY)
.update(payload)
.digest('hex')
// Step 3: Compare signatures (constant-time comparison)
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
)
}
async function getStakeSnapshotsWithVerification(walletAddress, lastDays = 7) {
const method = 'GET'
const path = '/api/staking/snapshots'
const queryParams = `?walletAddress=${walletAddress}&lastDays=${lastDays}`
const response = await axios.get(`${YGG_API_BASE_URL}${path}${queryParams}`, {
method: method,
headers: {
'X-API-KEY': API_KEY,
'Content-Type': 'application/json'
}
})
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`)
}
// Get response data and signature
const responseBody = await response.json()
const receivedSignature = response.headers.get('x-api-signature')
// ✅ CRITICAL: Verify YGG's signature before using the data
if (!receivedSignature) {
throw new Error('Response missing X-API-SIGNATURE header')
}
const isValid = verifyResponseSignature(
method,
path,
responseBody,
receivedSignature
)
if (!isValid) {
throw new Error('Invalid response signature - data may have been tampered with!')
}
console.log('✅ Response signature verified successfully')
return responseBody
}
// Usage
const stakingData = await getStakeSnapshotsWithVerification('0x123456...', 7)
console.log(`Average staked: ${stakingData.averageStakedAmount}`)How It Works
- Partner makes request with
X-API-KEYheader - YGG processes request and generates HMAC signature for the response
- YGG returns response with data and
X-API-SIGNATUREheader - Partner verifies signature using shared secret key to ensure data integrity
Getting Your API Key
API keys are provided by YGG during the integration process. Contact the YGG team to receive your production API key.
Development vs Production
You will receive separate API keys for different environments:
- Development/Staging: For testing and integration
- Production: For live game environment
Security Best Practices
Protect Your API Key
Keep your API key secure:
- Never commit API keys to version control
- Store keys in environment variables
- Use secret management services (AWS Secrets Manager, HashiCorp Vault, etc.)
- Rotate keys periodically
Server-Side Only
Never expose API keys in client-side code. Always make API calls from your game server.
Implementation Examples
const crypto = require('crypto')
const axios = require('axios')
// Store credentials in environment variables
const YGG_API_KEY = process.env.YGG_API_KEY
const YGG_SHARED_SECRET = process.env.YGG_SHARED_SECRET
const YGG_API_BASE_URL = 'YGG_API_BASE_URL'
function verifySignature(method, path, responseBody, receivedSignature) {
const payload = method + path + JSON.stringify(responseBody)
const expectedSignature = crypto.createHmac('sha256', YGG_SHARED_SECRET)
.update(payload)
.digest('hex')
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
)
}
async function getStakeSnapshots(walletAddress, lastDays = 7) {
try {
const method = 'GET'
const path = '/api/staking/snapshots'
const queryParams = `?walletAddress=${walletAddress}&lastDays=${lastDays}`
const response = await axios.get(`${YGG_API_BASE_URL}${path}${queryParams}`, {
headers: {
'X-API-KEY': YGG_API_KEY,
'Content-Type': 'application/json'
},
data: body
})
// Verify YGG's response signature
const receivedSignature = response.headers['x-api-signature']
if (!receivedSignature) {
throw new Error('Response missing signature')
}
const isValid = verifySignature(method, path, response.data, receivedSignature)
if (!isValid) {
throw new Error('Invalid response signature!')
}
console.log('✅ Signature verified')
return response.data
} catch (error) {
console.error('Failed to fetch staking data:', error)
throw error
}
}Error Responses
When authentication fails, you'll receive an error response:
401 Unauthorized - Missing or Invalid API Key
{
"success": false,
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}Common causes:
- Incorrect shared secret key
- Payload components in wrong order
- Wrong HTTP method used in signature
- Incorrect path in signature
403 Forbidden
Valid API key but insufficient permissions:
{
"success": false,
"code": "FORBIDDEN",
"message": "Access denied"
}Debugging HMAC Signature Verification
If signature verification is failing, follow these steps to debug:
1. Verify Response Components
Print each component extracted from YGG's response:
console.log('Method:', method) // Should be: GET
console.log('Path:', path) // Should be: /api/staking/snapshots
console.log('Response Body:', responseBody) // Full JSON response
console.log('Received Signature:', receivedSignature) // From X-API-SIGNATURE header
console.log('Body (stringified):', JSON.stringify(responseBody))2. Check JSON Stringification
Ensure the response body is stringified correctly:
// ✅ Correct - use standard JSON.stringify
JSON.stringify(responseBody)
// Output: {"walletAddress":"0x123","averageStakedAmount":285.71,...}3. Verify Shared Secret
Make sure you're using the correct shared secret provided by YGG:
// The shared secret should match exactly what YGG provided
console.log('Using shared secret:', SHARED_SECRET_KEY)4. Test Signature Verification
Create a test case with known values:
const method = 'GET'
const path = '/api/staking/snapshots'
const responseBody = {
"averageStakedAmount": 285.71,
"requestedAt": "2025-11-08T08:05:00Z",
// ... rest of response
}
const bodyStr = JSON.stringify(responseBody)
const payload = method + path + bodyStr
console.log('Payload for verification:', payload)
const expectedSignature = crypto.createHmac('sha256', SHARED_SECRET_KEY)
.update(payload)
.digest('hex')
console.log('Expected Signature:', expectedSignature)
console.log('Received Signature:', receivedSignature)
console.log('Match:', expectedSignature === receivedSignature)5. Common Mistakes
| Issue | Problem | Solution |
|---|---|---|
| Wrong method | Using 'POST' but should be 'GET' | Use the same method as your request |
| Wrong path | Path includes query params or host | Use path only: /api/staking/snapshots |
| Wrong encoding | Using base64 instead of hex | Use .digest('hex') not .digest('base64') |
| Missing headers | Response headers not captured | Ensure you capture response headers |
Implementation Checklist
When implementing HMAC signature verification, ensure you:
Security & Credentials
- Store API keys in environment variables or secret management service
- Store shared secret key securely (never commit to version control)
- Never expose API keys or secrets in client-side code
- Make all API calls from your server
- Use separate keys for development and production
Request Headers
- Include
X-API-KEYheader in all requests - Include
Content-Type: application/jsonheader
Response Verification
- Extract
X-API-SIGNATUREfrom YGG response headers - Create canonical payload:
method + path + JSON.stringify(responseBody) - Use standard
JSON.stringify()for body serialization - Generate expected HMAC SHA256 signature using shared secret
- Compare expected signature with received signature
- Reject response if signature verification fails
Error Handling
- Handle authentication errors gracefully
- Log signature verification failures for debugging
Important: For Staking APIs, YGG generates the signature in the response, and you verify it to ensure data integrity and authenticity.