SpaceComputer

Cosmic Cipher Password Generator

Learn how to build a secure password generator using SpaceComputer Orbitport's cosmic randomness for enterprise-grade security

This recipe demonstrates how to build a secure password generator using SpaceComputer Orbitport's cTRNG (cosmic True Random Number Generator) service. Unlike traditional password generators that rely on pseudo-random algorithms, this implementation uses genuine cosmic randomness from satellite instrumentation to create truly unpredictable passwords.

Video Walkthrough

Follow along with the video walkthrough of this recipe: Generate a Secure Password Using Cosmic Entropy

Prerequisites

  • TypeScript/JavaScript knowledge
  • Next.js or React application
  • API access to SpaceComputer Orbitport (Get your API access key here)
  • Understanding of password security requirements

What You'll Build

A secure password generator that:

  • Uses cosmic randomness from satellite instrumentation for true entropy
  • Implements flexible password generation with customizable character sets
  • Provides client-side password generation with server-side randomness
  • Includes fallback mechanisms for offline resilience
  • Offers verifiable randomness with seed display

Architecture Pattern Rationale

This recipe implements a Hybrid Randomness Pattern which is ideal for password generation:

  • Cosmic Entropy: Server-side retrieval of truly random seeds from space
  • Local Generation: Client-side password crafting for flexibility and privacy
  • Fallback Resilience: Node.js crypto fallback when cosmic randomness is unavailable
  • Verifiable Randomness: Users can verify the cosmic origin of their passwords
  • Customizable Logic: Easy to modify character sets and generation rules

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://auth.spacecomputer.io
ORBITPORT_API_URL=https://op.spacecomputer.io

2. Authentication Setup

Design Rationale for Production

Server-side authentication is critical for password generation 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 password generation requests can be logged and monitored
  • Scalability: Multiple server instances can share the same authentication logic

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

// 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 with fallback support:

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

const ORBITPORT_API_URL = process.env.ORBITPORT_API_URL;

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  let usedFallback = false;
  let data: any = null;

  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);
      throw new Error(`API request failed: ${response.status}`);
    }

    data = await response.json();
  } catch (error) {
    console.warn("Using fallback random generation:", error);
    usedFallback = true;

    // Fallback to Node.js crypto for offline resilience
    const fallbackSeed = crypto.randomBytes(32).toString("hex");
    data = {
      service: "fallback",
      src: "node-crypto",
      data: fallbackSeed,
      signature: {
        value: "fallback-signature",
        pk: "fallback-key",
      },
    };
  }

  return res.status(200).json({
    ...data,
    usedFallback,
  });
}

API Route Design Rationale

Using a server-side API route with fallback provides:

  • Security: Access tokens never leave your server, preventing token exposure
  • Error Handling: Centralized error handling with proper HTTP status codes
  • Fallback Resilience: Node.js crypto ensures password generation works even when cosmic randomness is unavailable
  • Rate Limiting: Implement per-user rate limiting to prevent API abuse
  • Monitoring: Track usage patterns and implement alerting for production issues
  • Compliance: Meet enterprise security requirements for external API access

4. Password Generation Logic

Create the core password generation logic in lib/password-generator.ts:

// lib/password-generator.ts

interface GenerateParams {
  length: number;
  minUpper: number;
  minLower: number;
  minNumbers: number;
  minSymbols: number;
  includeUpper: boolean;
  includeLower: boolean;
  includeNumbers: boolean;
  includeSymbols: boolean;
}

const CHARSETS = {
  uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  lowercase: "abcdefghijklmnopqrstuvwxyz",
  numbers: "0123456789",
  symbols: "!@#$%^&*+-=",
};

// Creates a deterministic random generator from cosmic seed
function createRandomGenerator(seed: string) {
  const seedBytes = new Uint8Array(Buffer.from(seed, "hex"));
  let position = 0;
  return (length: number): Uint8Array => {
    const result = new Uint8Array(length);
    for (let i = 0; i < length; i++) {
      result[i] = seedBytes[position % seedBytes.length];
      position++;
    }
    return result;
  };
}

export function generatePasswordFromSeed(
  seed: string,
  params: GenerateParams
): string {
  const randomValues = createRandomGenerator(seed);
  const {
    length,
    minUpper,
    minLower,
    minNumbers,
    minSymbols,
    includeUpper,
    includeLower,
    includeNumbers,
    includeSymbols,
  } = params;

  // Build character sets based on user selection
  const availableCharsets: { [key: string]: string } = {};
  if (includeUpper) availableCharsets.uppercase = CHARSETS.uppercase;
  if (includeLower) availableCharsets.lowercase = CHARSETS.lowercase;
  if (includeNumbers) availableCharsets.numbers = CHARSETS.numbers;
  if (includeSymbols) availableCharsets.symbols = CHARSETS.symbols;

  // Generate password meeting minimum requirements
  let password = "";
  if (includeUpper) {
    for (let i = 0; i < minUpper; i++) {
      password +=
        availableCharsets.uppercase[
          randomValues(1)[0] % availableCharsets.uppercase.length
        ];
    }
  }
  if (includeLower) {
    for (let i = 0; i < minLower; i++) {
      password +=
        availableCharsets.lowercase[
          randomValues(1)[0] % availableCharsets.lowercase.length
        ];
    }
  }
  if (includeNumbers) {
    for (let i = 0; i < minNumbers; i++) {
      password +=
        availableCharsets.numbers[
          randomValues(1)[0] % availableCharsets.numbers.length
        ];
    }
  }
  if (includeSymbols) {
    for (let i = 0; i < minSymbols; i++) {
      password +=
        availableCharsets.symbols[
          randomValues(1)[0] % availableCharsets.symbols.length
        ];
    }
  }

  // Fill remaining length with random chars
  const allAvailableChars = Object.values(availableCharsets).join("");
  const remainingLength = length - password.length;
  for (let i = 0; i < remainingLength; i++) {
    password +=
      allAvailableChars[randomValues(1)[0] % allAvailableChars.length];
  }

  // Fisher-Yates shuffle using cosmic seed for randomness
  const passwordArray = password.split("");
  for (let i = passwordArray.length - 1; i > 0; i--) {
    const j = randomValues(1)[0] % (i + 1);
    [passwordArray[i], passwordArray[j]] = [passwordArray[j], passwordArray[i]];
  }

  return passwordArray.join("");
}

Password Generation Rationale

The password generation logic provides production benefits through:

  • Cosmic Entropy: Uses genuine randomness from space, not predictable algorithms
  • Deterministic Mapping: Same seed always produces the same password for verification
  • Requirement Fulfillment: Guarantees minimum character requirements are met
  • Fair Distribution: Fisher-Yates shuffle ensures uniform character distribution
  • Extensibility: Easy to add new character sets or modify generation rules
  • Client-Side Privacy: Password generation happens locally, not on external servers

5. 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;
  };
  usedFallback: boolean;
}

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

6. Usage Example

Here's how to use the password generator in your application:

import { useOrbitport } from "@/hooks/useOrbitport";
import { generatePasswordFromSeed } from "@/lib/password-generator";

function PasswordGenerator() {
  const { getRandomSeed } = useOrbitport();
  const [password, setPassword] = useState<string>("");
  const [seed, setSeed] = useState<string>("");
  const [usedFallback, setUsedFallback] = useState<boolean>(false);

  const handleGenerate = async () => {
    try {
      // Fetch cosmic seed from API
      const seedResult = await getRandomSeed();

      // Generate password client-side using the seed
      const generatedPassword = generatePasswordFromSeed(seedResult.data, {
        length: 16,
        minUpper: 2,
        minLower: 2,
        minNumbers: 2,
        minSymbols: 2,
        includeUpper: true,
        includeLower: true,
        includeNumbers: true,
        includeSymbols: true,
      });

      setPassword(generatedPassword);
      setSeed(seedResult.data);
      setUsedFallback(seedResult.usedFallback);
    } catch (error) {
      console.error("Failed to generate password:", error);
    }
  };

  return (
    <div>
      <button onClick={handleGenerate}>Generate Cosmic Password</button>
      {password && (
        <div>
          <p>
            <strong>Password:</strong> {password}
          </p>
          <p>
            <strong>Cosmic Seed:</strong> {seed}
          </p>
          {usedFallback && (
            <p>
              <em>Note: Used fallback randomness</em>
            </p>
          )}
        </div>
      )}
    </div>
  );
}

Password Generation Usage Rationale

The password generator example demonstrates production-ready patterns:

  • Verifiable Randomness: Users can see the cosmic seed that generated their password
  • Fallback Transparency: Clear indication when fallback randomness is used
  • Error Handling: Try-catch blocks prevent application crashes from API failures
  • User Feedback: Clear display of generated password and source randomness
  • Extensibility: Easy to modify character requirements or add new features
  • Security: Password generation happens client-side for privacy

Key Benefits

  • True Randomness: Leverages cosmic radiation for unbiased password generation
  • Security: Server-side token management with client-side password generation
  • Reliability: Fallback mechanisms ensure password generation always works
  • Verifiability: Users can verify the cosmic origin of their passwords
  • Flexibility: Customizable character sets and generation rules
  • Privacy: Password generation happens locally, not on external servers

Use Cases

  • Enterprise Security: Generate secure passwords for corporate accounts
  • Personal Use: Create strong passwords for personal accounts
  • Developer Tools: Integrate into password management systems
  • Security Audits: Generate test passwords with verifiable randomness
  • Compliance: Meet security requirements for password generation

Production Considerations

Enterprise-Grade Password Security

This recipe follows production-ready patterns essential for secure password generation:

Security First Approach

  • Cosmic Entropy: Uses genuine randomness from space, not predictable algorithms
  • Credential Isolation: API keys are never exposed to client-side code
  • Token Encryption: Access tokens are encrypted in cookies with proper expiry management
  • Client-Side Generation: Passwords are generated locally for privacy

Reliability & Performance

  • Fallback Resilience: Node.js crypto ensures password generation works offline
  • Proactive Token Refresh: Eliminates authentication timeouts during user sessions
  • Error Handling: Graceful degradation when external services are unavailable
  • Performance: Efficient password generation with minimal computational overhead

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
  • Audit Trails: Complete logging for compliance and security investigations
  • Rate Limiting: Prevent abuse and ensure fair usage across all users
  • Verifiable Randomness: Users can verify the cosmic origin of their passwords

Next Steps

  • Implement the crypto utility functions for token encryption/decryption
  • Add password strength validation and entropy calculation
  • Consider implementing password history and reuse prevention
  • Explore other Orbitport services like spaceTEE for secure computation
  • Add support for passphrase generation and mnemonic phrases

Resources

On this page