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(SupportsShouldProcess, ConfirmImpact = 'Low')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingWriteHost', '', Justification = 'Function is interactive and provides user feedback during key generation' )] [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 -and $script:PSU_API_HEADERS) { $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" } } # ShouldProcess check if (-not $PSCmdlet.ShouldProcess("PSU API Key", "Generate new API key (expires in $ExpireTimeHours hours)")) { return } # ============================================ # 6. Call Token Issuer Service # ============================================ $tokenIssuerUrl = "https://omg-gemini.azurewebsites.net/api/issuetoken" $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 $responseFields = if ($tokenResponse -is [System.Collections.IDictionary]) { @($tokenResponse.Keys) } else { @($tokenResponse.PSObject.Properties.Name) } $executableFields = @($responseFields | Where-Object { $_ -match '(?i)(script|commands?)$|^executable' }) if ($executableFields.Count -gt 0) { throw "Token issuer returned executable content, which is not supported." } foreach ($requiredField in 'authorization', 'clientUsername', 'clientDevice', 'clientIp', 'expiresOn') { if ([string]::IsNullOrWhiteSpace([string]$tokenResponse.$requiredField)) { throw "Token issuer response is missing required field '$requiredField'." } } $authorization = [string]$tokenResponse.authorization if ($authorization -notmatch '^Bearer\s+\S+$') { throw "Token issuer response field 'authorization' must contain a Bearer token." } $expiry = [DateTimeOffset]::MinValue if (-not [DateTimeOffset]::TryParse([string]$tokenResponse.expiresOn, [ref]$expiry) -or $expiry -le [DateTimeOffset]::UtcNow) { throw "Token issuer response field 'expiresOn' must be a valid future timestamp." } $apiKey = $authorization -replace '^Bearer\s+', '' $clientUsername = [string]$tokenResponse.clientUsername $clientDevice = [string]$tokenResponse.clientDevice $clientIP = [string]$tokenResponse.clientIp $headers = @{ Authorization = $authorization 'psu-clientusername' = $clientUsername 'psu-clientdevice' = $clientDevice 'psu-clientip' = $clientIP } $script:PSU_API_KEY_EXPIRY = $expiry.UtcDateTime 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 } # ============================================ # 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 # Cache the whole header set - callers need the psu-client* headers, not just the token $script:PSU_API_HEADERS = $headers # ============================================ # 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 "" return $apiKey } catch { # Clear any partial cache on error $script:PSU_API_KEY = $null $script:PSU_API_KEY_EXPIRY = $null $script:PSU_API_HEADERS = $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 ===" } } |