Oauth Integration

What is OAuth 2.0?

OAuth 2.0 is a secure authorization protocol that allows third-party applications to access your HytaleMetrics data without sharing your password. When you authorize an application, it receives a token that's tied to a specific server you select.

Getting Started

Step 1: Create a Developer Application

1. Navigate to Developer Portal in your dashboard
2. Click "Create New Application"
3. Enter your application name and callback URL
4. Copy your Client ID and Client Secret - the secret is only shown once!

Authorization Flow

The OAuth flow follows these steps (click each to expand):

Create a code verifier and challenge for enhanced security:

// Generate random code verifier (43-128 characters)
$codeVerifier = bin2hex(random_bytes(32));

// Create SHA256 hash and base64 encode
$codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');

Send the user to the authorization endpoint:

https://hytalemetrics.net/oauth/authorize?
    client_id=YOUR_CLIENT_ID
    &redirect_uri=https://yourapp.com/callback
    &response_type=code
    &scope=read:server write:events read:players read:analytics
    &state=RANDOM_STATE_STRING
    &code_challenge=CODE_CHALLENGE
    &code_challenge_method=S256

The user will:

  • Log in (if not already logged in)
  • Select which server to grant access to
  • Review the requested permissions
  • Approve or deny the request

User is redirected back to your callback URL with a code:

https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE_STRING

Make a POST request to exchange the code for tokens:

POST https://hytalemetrics.net/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&redirect_uri=https://yourapp.com/callback
&code=AUTHORIZATION_CODE
&code_verifier=CODE_VERIFIER

Response:

{
  "token_type": "Bearer",
  "expires_in": 31536000,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refresh_token": "def50200a8c5b..."
}

Available Scopes

Request only the scopes your application needs:

read:server - Read server information and status
write:events - Submit gameplay events and metrics
read:players - Access player data and statistics
read:analytics - View analytics and reports

API Endpoints

All API requests must include your access token in the Authorization header:

Authorization: Bearer YOUR_ACCESS_TOKEN

GET /api/v2/example - Test endpoint with example data
Scope: read:server

curl -X GET https://hytalemetrics.net/api/v2/example \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"

GET /api/v2/status - Get server status
Scope: read:server

{
  "success": true,
  "server": {
    "id": 123,
    "name": "My Server",
    "game": "Hytale",
    "game_type": "survival",
    "status": "online"
  }
}

POST /api/v2/event - Submit gameplay events
Scope: write:events

curl -X POST https://hytalemetrics.net/api/v2/event \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_type": "player_join",
        "player": "PlayerName",
        "timestamp": "2026-01-18 12:00:00"
      }
    ]
  }'

GET /api/v2/players - Get player data
Scope: read:players

{
  "success": true,
  "players": {
    "online": 42,
    "status": "online"
  }
}

GET /api/v2/analytics - Get analytics data
Scope: read:analytics

{
  "success": true,
  "analytics": {
    "server_name": "My Server",
    "period": "last_7_days",
    "metrics": [...]
  }
}

Code Examples

Select your language to view implementation examples:

<?php
// Step 1: Generate PKCE
$codeVerifier = bin2hex(random_bytes(32));
$codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');
$state = bin2hex(random_bytes(16));

$_SESSION['code_verifier'] = $codeVerifier;
$_SESSION['oauth_state'] = $state;

// Step 2: Redirect to authorization
$authUrl = 'https://hytalemetrics.net/oauth/authorize?' . http_build_query([
    'client_id' => 'YOUR_CLIENT_ID',
    'redirect_uri' => 'https://yourapp.com/callback',
    'response_type' => 'code',
    'scope' => 'read:server write:events',
    'state' => $state,
    'code_challenge' => $codeChallenge,
    'code_challenge_method' => 'S256',
]);
header('Location: ' . $authUrl);

// Step 3: Handle callback
if ($_GET['state'] !== $_SESSION['oauth_state']) {
    die('Invalid state');
}

// Step 4: Exchange code for token
$ch = curl_init('https://hytalemetrics.net/oauth/token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'grant_type' => 'authorization_code',
    'client_id' => 'YOUR_CLIENT_ID',
    'client_secret' => 'YOUR_CLIENT_SECRET',
    'redirect_uri' => 'https://yourapp.com/callback',
    'code' => $_GET['code'],
    'code_verifier' => $_SESSION['code_verifier'],
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$tokens = json_decode(curl_exec($ch), true);

// Step 5: Use the access token
$ch = curl_init('https://hytalemetrics.net/api/v2/status');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $tokens['access_token'],
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$serverData = json_decode(curl_exec($ch), true);
?>

const crypto = require('crypto');
const axios = require('axios');

// Step 1: Generate PKCE
const codeVerifier = crypto.randomBytes(32).toString('hex');
const codeChallenge = crypto
  .createHash('sha256')
  .update(codeVerifier)
  .digest('base64url');
const state = crypto.randomBytes(16).toString('hex');

// Step 2: Redirect to authorization
const authUrl = `https://hytalemetrics.net/oauth/authorize?` + new URLSearchParams({
  client_id: 'YOUR_CLIENT_ID',
  redirect_uri: 'https://yourapp.com/callback',
  response_type: 'code',
  scope: 'read:server write:events',
  state: state,
  code_challenge: codeChallenge,
  code_challenge_method: 'S256',
});

// res.redirect(authUrl);

// Step 3: Handle callback
app.get('/callback', async (req, res) => {
  if (req.query.state !== state) {
    return res.status(400).send('Invalid state');
  }

  // Step 4: Exchange code for token
  const tokenResponse = await axios.post('https://hytalemetrics.net/oauth/token', {
    grant_type: 'authorization_code',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    redirect_uri: 'https://yourapp.com/callback',
    code: req.query.code,
    code_verifier: codeVerifier,
  });

  const { access_token } = tokenResponse.data;

  // Step 5: Use the access token
  const serverData = await axios.get('https://hytalemetrics.net/api/v2/status', {
    headers: {
      Authorization: `Bearer ${access_token}`,
    },
  });

  res.json(serverData.data);
});

import hashlib
import secrets
import base64
import requests

# Step 1: Generate PKCE
code_verifier = secrets.token_urlsafe(32)
code_challenge = base64.urlsafe_b64encode(
    hashlib.sha256(code_verifier.encode()).digest()
).decode().rstrip('=')
state = secrets.token_hex(16)

# Step 2: Redirect to authorization
auth_url = f"https://hytalemetrics.net/oauth/authorize?" + "&".join([
    f"client_id=YOUR_CLIENT_ID",
    f"redirect_uri=https://yourapp.com/callback",
    f"response_type=code",
    f"scope=read:server write:events",
    f"state={state}",
    f"code_challenge={code_challenge}",
    f"code_challenge_method=S256",
])

# return redirect(auth_url)

# Step 3: Handle callback
@app.route('/callback')
def callback():
    if request.args.get('state') != state:
        return 'Invalid state', 400

    # Step 4: Exchange code for token
    token_response = requests.post('https://hytalemetrics.net/oauth/token', data={
        'grant_type': 'authorization_code',
        'client_id': 'YOUR_CLIENT_ID',
        'client_secret': 'YOUR_CLIENT_SECRET',
        'redirect_uri': 'https://yourapp.com/callback',
        'code': request.args.get('code'),
        'code_verifier': code_verifier,
    })

    tokens = token_response.json()

    # Step 5: Use the access token
    server_data = requests.get('https://hytalemetrics.net/api/v2/status', headers={
        'Authorization': f"Bearer {tokens['access_token']}",
    })

    return server_data.json()

import java.net.http.*;
import java.security.*;
import java.util.*;

public class OAuthClient {
    public static void main(String[] args) throws Exception {
        // Step 1: Generate PKCE
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[32];
        random.nextBytes(bytes);
        String codeVerifier = Base64.getUrlEncoder()
            .withoutPadding()
            .encodeToString(bytes);

        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(codeVerifier.getBytes());
        String codeChallenge = Base64.getUrlEncoder()
            .withoutPadding()
            .encodeToString(hash);

        String state = UUID.randomUUID().toString();

        // Step 2: Build authorization URL
        String authUrl = "https://hytalemetrics.net/oauth/authorize?" +
            "client_id=YOUR_CLIENT_ID&" +
            "redirect_uri=https://yourapp.com/callback&" +
            "response_type=code&" +
            "scope=read:server+write:events&" +
            "state=" + state + "&" +
            "code_challenge=" + codeChallenge + "&" +
            "code_challenge_method=S256";

        // Step 4: Exchange code for token
        HttpClient client = HttpClient.newHttpClient();
        String tokenBody = "grant_type=authorization_code&" +
            "client_id=YOUR_CLIENT_ID&" +
            "client_secret=YOUR_CLIENT_SECRET&" +
            "redirect_uri=https://yourapp.com/callback&" +
            "code=AUTHORIZATION_CODE&" +
            "code_verifier=" + codeVerifier;

        HttpRequest tokenRequest = HttpRequest.newBuilder()
            .uri(URI.create("https://hytalemetrics.net/oauth/token"))
            .header("Content-Type", "application/x-www-form-urlencoded")
            .POST(HttpRequest.BodyPublishers.ofString(tokenBody))
            .build();

        HttpResponse<String> tokenResponse = client.send(
            tokenRequest,
            HttpResponse.BodyHandlers.ofString()
        );

        // Step 5: Use access token
        HttpRequest apiRequest = HttpRequest.newBuilder()
            .uri(URI.create("https://hytalemetrics.net/api/v2/status"))
            .header("Authorization", "Bearer ACCESS_TOKEN")
            .GET()
            .build();

        HttpResponse<String> apiResponse = client.send(
            apiRequest,
            HttpResponse.BodyHandlers.ofString()
        );

        System.out.println(apiResponse.body());
    }
}

package main

import (
    "crypto/rand"
    "crypto/sha256"
    "encoding/base64"
    "encoding/hex"
    "encoding/json"
    "net/http"
    "net/url"
)

func main() {
    // Step 1: Generate PKCE
    verifier := make([]byte, 32)
    rand.Read(verifier)
    codeVerifier := hex.EncodeToString(verifier)

    hash := sha256.Sum256([]byte(codeVerifier))
    codeChallenge := base64.URLEncoding.WithPadding(base64.NoPadding).
        EncodeToString(hash[:])

    stateBytes := make([]byte, 16)
    rand.Read(stateBytes)
    state := hex.EncodeToString(stateBytes)

    // Step 2: Build authorization URL
    authUrl := "https://hytalemetrics.net/oauth/authorize?" +
        url.Values{
            "client_id":              {"YOUR_CLIENT_ID"},
            "redirect_uri":           {"https://yourapp.com/callback"},
            "response_type":          {"code"},
            "scope":                  {"read:server write:events"},
            "state":                  {state},
            "code_challenge":         {codeChallenge},
            "code_challenge_method":  {"S256"},
        }.Encode()

    // Step 4: Exchange code for token
    tokenResp, _ := http.PostForm("https://hytalemetrics.net/oauth/token",
        url.Values{
            "grant_type":    {"authorization_code"},
            "client_id":     {"YOUR_CLIENT_ID"},
            "client_secret": {"YOUR_CLIENT_SECRET"},
            "redirect_uri":  {"https://yourapp.com/callback"},
            "code":          {"AUTHORIZATION_CODE"},
            "code_verifier": {codeVerifier},
        })

    var tokens map[string]interface{}
    json.NewDecoder(tokenResp.Body).Decode(&tokens)

    // Step 5: Use access token
    req, _ := http.NewRequest("GET", "https://hytalemetrics.net/api/v2/status", nil)
    req.Header.Set("Authorization", "Bearer "+tokens["access_token"].(string))

    client := &http.Client{}
    resp, _ := client.Do(req)

    var serverData map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&serverData)
}

Testing Your Integration

Use the built-in OAuth Test Console to test your OAuth flow without writing code. It simulates a third-party application and shows you each step of the process.

Security Best Practices

- Never share your client secret publicly
- Always use HTTPS for your callback URLs
- Validate the state parameter to prevent CSRF attacks
- Use PKCE (code challenge/verifier) for enhanced security
- Store access tokens securely
- Request only the scopes you need
- Tokens expire after 1 year - use refresh tokens to get new ones

Rate Limits

API endpoints are rate limited to 100 requests per minute per access token. If you exceed this limit, you'll receive a 429 Too Many Requests response.

Need Help?

- View your applications: Developer Portal
- Test OAuth flow: OAuth Test Console
- Check server status: Dashboard