Skip to main content

Cosmic Randomness in Web3 Applications

This recipe demonstrates how to implement true cosmic randomness in your Web3 applications using SpaceComputer Orbitport's cTRNG (cosmic True Random Number Generator) service. The cTRNG leverages data from cosmic radiation detected via satellite instrumentation to produce unbiased, cryptographically secure random numbers.

Prerequisites

What You'll Build

A secure authentication system with Orbitport that:

  • Manages API tokens securely on the server-side
  • Fetches random seeds from the cTRNG service
  • Implements proper token expiry management
  • Provides a clean client-side interface

🏗️ Architecture Pattern Rationale

This recipe implements a Server-Side Proxy Pattern which can be used in production applications:

  • Security Layer: Your server acts as a secure proxy between clients and Orbitport
  • Token Management: Centralized authentication with encrypted storage and proactive refresh
  • Error Isolation: Client applications never directly interact with external APIs
  • Scalability: Multiple server instances can share the same authentication logic
  • Monitoring: Complete visibility into all API interactions and usage patterns

Implementation Steps

1. Environment Configuration

Set up your environment variables:

# .env.local
ORBITPORT_CLIENT_ID=your_client_id
ORBITPORT_CLIENT_SECRET=your_client_secret
ORBITPORT_AUTH_URL=https://your-auth-domain.auth0.com
ORBITPORT_API_URL=https://op.spacecomputer.io

2. Server-Side Authentication

Create a centralized authentication utility in lib/auth.ts:

🎯 Design Rationale for Production

Server-side authentication is critical for production applications because:

  • Security: API credentials are never exposed to client-side code, preventing credential theft
  • Token Management: Centralized token lifecycle management reduces security vulnerabilities
  • Rate Limiting: Server-side control allows for proper API rate limiting and abuse prevention
  • Audit Trail: All API calls can be logged and monitored from a single location
  • Scalability: Multiple server instances can share the same authentication logic
// lib/auth.ts
import { NextApiRequest, NextApiResponse } from "next";
import { parseToken, decrypt, encrypt } from "./crypto"; // You'll need to implement these

const TOKEN_EXPIRE_BUFFER = 300; // 5 minutes buffer

async function generateAccessToken(): Promise<string | null> {
const clientId = process.env.ORBITPORT_CLIENT_ID;
const clientSecret = process.env.ORBITPORT_CLIENT_SECRET;
const authUrl = process.env.ORBITPORT_AUTH_URL;

if (!clientId || !clientSecret || !authUrl) {
throw new Error("Missing Orbitport authentication configuration");
}

try {
const response = await fetch(`${authUrl}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
client_id: clientId,
client_secret: clientSecret,
audience: "https://op.spacecomputer.io/api",
grant_type: "client_credentials",
}),
});

if (!response.ok) {
throw new Error("Failed to get access token");
}

const data = await response.json();
return data.access_token;
} catch (error) {
console.error("Error getting access token:", error);
return null;
}
}

export async function getValidToken(
req: NextApiRequest,
res: NextApiResponse
): Promise<string | null> {
try {
const encryptedToken = getEncryptedTokenFromCookies(req);
let accessToken: string | null = null;

if (encryptedToken) {
const decrypted = decrypt(encryptedToken);
const parsed = parseToken(decrypted);
if (parsed) {
const now = Math.floor(Date.now() / 1000);
// Only use token if not expired and not about to expire
if (parsed.exp > now + TOKEN_EXPIRE_BUFFER) {
accessToken = parsed.access_token;
}
}
}

// If no valid token, generate a new one
if (!accessToken) {
accessToken = await generateAccessToken();
if (!accessToken) {
console.error("Failed to get new access token");
return null;
}
const parsed = parseToken(accessToken);
if (!parsed) {
console.error("Failed to parse new token");
return null;
}
setEncryptedTokenCookie(res, accessToken, parsed.exp);
}

return accessToken;
} catch (error) {
console.error("Error in getValidToken:", error);
return null;
}
}

🔐 Token Expiry Management Rationale

The TOKEN_EXPIRE_BUFFER (5-minute buffer) is essential for production because:

  • Proactive Refresh: Tokens are refreshed before they expire, preventing user-facing errors
  • Graceful Degradation: If token refresh fails, the buffer provides time for fallback mechanisms
  • Load Distribution: Staggered token refreshes prevent all users from hitting the auth endpoint simultaneously
  • Security: Reduces the window where expired tokens might be used
  • User Experience: Eliminates authentication timeouts during active user sessions

3. API Route for Random Seeds

Create an API endpoint in pages/api/random.ts:

// pages/api/random.ts
import { NextApiRequest, NextApiResponse } from "next";
import { getValidToken } from "@/lib/auth";

const ORBITPORT_API_URL = process.env.ORBITPORT_API_URL;

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
if (req.method !== "GET") {
return res.status(405).json({ message: "Method not allowed" });
}

if (!process.env.ORBITPORT_API_URL) {
return res.status(500).json({ message: "Missing Orbitport API URL" });
}

// Get valid token using our auth utility
const accessToken = await getValidToken(req, res);
if (!accessToken) {
return res.status(401).json({ message: "Authentication failed" });
}

// Call downstream API with access token
const response = await fetch(`${ORBITPORT_API_URL}/api/v1/services/trng`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

if (!response.ok) {
const errorText = await response.text();
console.error("Orbitport API error:", errorText);
return res
.status(502)
.json({ message: "Failed to get random seed", error: errorText });
}

const data = await response.json();
return res.status(200).json(data);
} catch (err) {
console.error("Error in random endpoint:", err);
return res.status(500).json({
message: "Failed to get random seed",
error: err instanceof Error ? err.message : "Unknown error",
});
}
}

🌐 API Route Design Rationale

Using a server-side API route instead of direct client-to-Orbitport calls provides:

  • Security: Access tokens never leave your server, preventing token exposure
  • Error Handling: Centralized error handling with proper HTTP status codes
  • Rate Limiting: Implement per-user rate limiting to prevent API abuse
  • Caching: Server-side caching reduces redundant API calls to Orbitport
  • Monitoring: Track usage patterns and implement alerting for production issues
  • Compliance: Meet enterprise security requirements for external API access

4. Client-Side Hook

Create a custom React hook in hooks/useOrbitport.ts:

// hooks/useOrbitport.ts
import { useCallback } from "react";

interface RandomSeedResponse {
service: string;
src: string;
data: string;
signature: {
value: string;
pk: string;
};
}

export function useOrbitport() {
const getRandomSeed = useCallback(async (): Promise<RandomSeedResponse> => {
try {
const response = await fetch("/api/random");
if (!response.ok) {
throw new Error("Failed to get random seed");
}

const data = await response.json();
return data;
} catch (error) {
console.error("Error getting random seed:", error);
throw error;
}
}, []);

return {
getRandomSeed,
};
}

⚛️ React Hook Design Rationale

Custom hooks provide production benefits through:

  • Reusability: The same hook can be used across multiple components
  • State Management: Centralized loading and error states for better UX
  • Testing: Easier to unit test business logic separately from UI components
  • Performance: useCallback prevents unnecessary re-renders and API calls
  • Error Boundaries: Consistent error handling across the application
  • Type Safety: TypeScript interfaces ensure data consistency

5. Usage Example

Here's how to use the random seed in your application:

import { useOrbitport } from "@/hooks/useOrbitport";

function RandomPlanetSelector() {
const { getRandomSeed } = useOrbitport();

const selectRandomPlanet = async () => {
try {
const seedData = await getRandomSeed();
const planets = [
"Mercury",
"Venus",
"Earth",
"Mars",
"Jupiter",
"Saturn",
"Uranus",
"Neptune",
];

const seedInt = parseInt(seedData.data.slice(0, 8), 16);
const planet = planets[seedInt % planets.length];

console.log(`Selected planet: ${planet}`);
return planet;
} catch (error) {
console.error("Failed to select random planet:", error);
}
};

return <button onClick={selectRandomPlanet}>Select Random Planet</button>;
}

🎲 Random Seed Usage Rationale

The planet selection example demonstrates production-ready patterns:

  • Deterministic Mapping: Using modulo operation ensures fair distribution across all options
  • Error Handling: Try-catch blocks prevent application crashes from API failures
  • User Feedback: Console logging provides debugging information in development
  • Extensibility: Easy to add more planets or modify the selection logic
  • Performance: Minimal computation overhead for random selection
  • Scalability: Works efficiently regardless of the number of options

Key Benefits

  • True Randomness: Leverages cosmic radiation for unbiased random numbers
  • Security: Server-side token management with encryption
  • Reliability: Automatic token refresh and expiry management
  • Performance: Efficient caching and minimal API calls

Use Cases

  • Gaming: Fair random number generation for games and lotteries
  • Cryptography: Secure key generation and cryptographic operations
  • Blockchain: Smart contract randomness and decentralized applications
  • Security: Secure communication and authentication systems

Production Considerations

🚀 Enterprise-Grade Implementation Patterns

This recipe follows production-ready patterns that are essential for enterprise applications:

Security First Approach

  • Credential Isolation: API keys are never exposed to client-side code
  • Token Encryption: Access tokens are encrypted in cookies with proper expiry management
  • Server-Side Validation: All authentication happens on your secure infrastructure
  • Audit Logging: Complete visibility into API usage and authentication events

Reliability & Performance

  • Proactive Token Refresh: Eliminates authentication timeouts during user sessions
  • Error Handling: Graceful degradation when external services are unavailable
  • Caching Strategy: Reduces redundant API calls and improves response times
  • Load Distribution: Prevents authentication stampedes across multiple users

Scalability & Monitoring

  • Centralized Logic: Single source of truth for authentication across all server instances
  • Performance Metrics: Track API response times and failure rates
  • Alerting: Proactive notifications for authentication failures or rate limit issues
  • Horizontal Scaling: Authentication logic works seamlessly across multiple servers

Compliance & Governance

  • Data Privacy: No sensitive data leaves your infrastructure
  • Access Control: Implement role-based access control for different user types
  • Audit Trails: Complete logging for compliance and security investigations
  • Rate Limiting: Prevent abuse and ensure fair usage across all users

Next Steps

  • Implement the crypto utility functions for token encryption/decryption
  • Add error handling and retry logic for production use
  • Consider implementing rate limiting for the API endpoints
  • Explore other Orbitport services like spaceTEE for secure computation

Resources