Private/New-PSUApiKey.ps1

function New-PSUApiKey {
    <#
    .SYNOPSIS
        Generates a secure 24-hour API key for PSU AI Proxy.
 
    .DESCRIPTION
        Creates a Base64-encoded API key containing:
        The key is cached in $script:PSU_API_KEY for reuse in the current session.
 
    .PARAMETER ExpireTimeHours
        How many hours until the key expires. Default is 24 hours.
 
    .PARAMETER Force
        Force regeneration even if a valid cached key exists.
 
    .EXAMPLE
        $apiKey = New-PSUApiKey
        # Generates and caches API key for 24 hours
 
    .EXAMPLE
        $apiKey = New-PSUApiKey -ExpireTimeHours 48
        # Generates key valid for 48 hours
 
    .EXAMPLE
        $apiKey = New-PSUApiKey -Force
        # Force regenerate even if cached key exists
 
    .OUTPUTS
        [String]
 
    .NOTES
        Author: Lakshmanachari Panuganti
        Cross-platform: Windows, Linux, macOS
 
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()]
        [ValidateRange(1, 8760)]
        [Alias('ExpireTime')]
        [int]$ExpireTimeHours = 24,

        [Parameter()]
        [switch]$Force
    )

    begin {
        Write-Verbose "=== Starting PSU API Key Generation ==="
    }

    process {
        try {
            # Check for cached key first (unless Force is specified)
            if (-not $Force -and $script:PSU_API_KEY -and $script:PSU_API_KEY_EXPIRY) {
                $now = [DateTime]::UtcNow
                if ($now -lt $script:PSU_API_KEY_EXPIRY) {
                    $timeLeft = $script:PSU_API_KEY_EXPIRY - $now
                    Write-Verbose "Using cached API key (expires in $($timeLeft.TotalHours.ToString('F1')) hours)"
                    Write-Host "✓ Using cached API key (expires in $($timeLeft.TotalHours.ToString('F1')) hours)" -ForegroundColor Green
                    return $script:PSU_API_KEY
                }
                else {
                    Write-Verbose "Cached API key has expired, generating new one"
                }
            }

            # ============================================
            # 6. Call Token Issuer Service
            # ============================================
            $tokenIssuerUrl = "https://omgissuetoken.azurewebsites.net/api/IssueToken-Dev"
            $timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
            $tokenUrl = "$tokenIssuerUrl`?t=$timestamp"

            Write-Verbose "Calling token issuer service..."
            Write-Verbose "URL: $tokenUrl"

            try {
                $tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -TimeoutSec 30 -ErrorAction Stop

                if (-not $tokenResponse.HeaderScript) {
                    throw "Token issuer did not return HeaderScript"
                }

                # Execute the header script to set local variables
                Write-Verbose "Executing header script from token issuer..."
                Invoke-Expression $tokenResponse.HeaderScript

                # Extract the authorization token from $Headers (set by HeaderScript)
                if (-not $Headers -or -not $Headers['Authorization']) {
                    throw "HeaderScript did not set Authorization header"
                }

                $apiKey = $Headers['Authorization'] -replace '^Bearer\s+', ''
                $clientUsername = $Headers['psu-clientusername']
                $clientDevice = $Headers['psu-clientdevice']
                $clientIP = $Headers['psu-clientip']

                Write-Verbose "Token received from issuer service"
                Write-Verbose "Username: $clientUsername"
                Write-Verbose "Device: $clientDevice"
                Write-Verbose "IP: $clientIP"
            }
            catch {
                $errorMsg = "Failed to retrieve token from issuer service: $($_.Exception.Message)"
                throw $errorMsg
            }

            # ============================================
            # 7. Parse expiry from token (if possible)
            # ============================================
            try {
                # Token format: IP|xx|startISO|xx|endISO|xx|signature (Base64 encoded)
                $decoded = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($apiKey))
                $parts = $decoded -split '\|xx\|'

                if ($parts.Count -ge 3) {
                    $expiryISO = $parts[2]
                    $expiryDate = [DateTimeOffset]::Parse($expiryISO)
                    $script:PSU_API_KEY_EXPIRY = $expiryDate.UtcDateTime
                    Write-Verbose "Token expires: $expiryISO"
                }
                else {
                    # Default to requested expiry time
                    $script:PSU_API_KEY_EXPIRY = [DateTime]::UtcNow.AddHours($ExpireTimeHours)
                    Write-Verbose "Using default expiry: $ExpireTimeHours hours from now"
                }
            }
            catch {
                # If parsing fails, use default expiry
                $script:PSU_API_KEY_EXPIRY = [DateTime]::UtcNow.AddHours($ExpireTimeHours)
                Write-Verbose "Token parsing failed, using default expiry"
            }

            # ============================================
            # 8. Cache the key for session reuse
            # ============================================
            $script:PSU_API_KEY = $apiKey
            $script:PSU_API_KEY_USERNAME = $clientUsername
            $script:PSU_API_KEY_COMPUTER = $clientDevice
            $script:PSU_API_KEY_IP = $clientIP

            # ============================================
            # 9. Display success message
            # ============================================
            $expiryDisplay = if ($script:PSU_API_KEY_EXPIRY) {
                $script:PSU_API_KEY_EXPIRY.ToString("o")
            } else {
                "Unknown"
            }

            Write-Host ""
            Write-Host "╔════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
            Write-Host "║ ✓ API Key Generated Successfully ║" -ForegroundColor Cyan
            Write-Host "╠════════════════════════════════════════════════════════╣" -ForegroundColor Cyan
            Write-Host "║ User : $($clientUsername.PadRight(40)) ║" -ForegroundColor White
            Write-Host "║ Computer : $($clientDevice.PadRight(40)) ║" -ForegroundColor White
            Write-Host "║ Public IP : $($clientIP.PadRight(40)) ║" -ForegroundColor White
            Write-Host "║ Expires : $($expiryDisplay.PadRight(40)) ║" -ForegroundColor Yellow
            Write-Host "║ Cached : Yes (session-wide reuse enabled) ║" -ForegroundColor Green
            Write-Host "╚════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
            Write-Host ""
            Write-Verbose "API key length: $($apiKey.Length) characters"

            return $apiKey
        }
        catch {
            # Clear any partial cache on error
            $script:PSU_API_KEY = $null
            $script:PSU_API_KEY_EXPIRY = $null

            Write-Host ""
            Write-Host "╔════════════════════════════════════════════════════════╗" -ForegroundColor Red
            Write-Host "║ ✗ API Key Generation Failed ║" -ForegroundColor Red
            Write-Host "╚════════════════════════════════════════════════════════╝" -ForegroundColor Red
            Write-Host ""

            $errorMsg = "Failed to generate PSU API key: $($_.Exception.Message)"
            Write-Error $errorMsg
            throw
        }
    }

    end {
        Write-Verbose "=== API Key Generation Complete ==="
    }
}