Public/Secrets/Get-IDBridgeSecret.ps1

<#
.SYNOPSIS
Retrieve a named IDBridge secret from the IDBridge secret vault.

.DESCRIPTION
Centralizes how IDBridge reads secrets (API keys, tokens, nonces, the Google service-account
key). Secrets live as encrypted JSON envelope files (<Name>.secret.json) in the vault folder
(Paths.VaultRoot, <Root>\Vault); there is no external-module dependency and no unlock step.
Use Set-IDBridgeSecret to add or change secrets.

Each envelope records the provider that protected it, so reads work for any mix of providers:

  - Cms : decrypted with Unprotect-CmsMessage; requires the 'IDBridge Secrets'
              certificate private key on this machine (see New-IDBridgeSecretCertificate).
  - DpapiNG : decrypted with DPAPI-NG; requires running as a principal named in the
              protection descriptor (e.g. the gMSA) on a domain-joined host.

Exception: when Secrets.Provider is 'AzKeyVault' there are no local envelopes — the secret
is fetched from Azure Key Vault over REST (Secrets.AzKeyVault config block) under the same
name.

Returns a [SecureString] by default. Use -AsPlainText to get the unprotected string.

.PARAMETER Name
The secret name (e.g. 'ApiKey-Passphrase').

.PARAMETER AsPlainText
Return the secret as a plain [string] instead of a [SecureString].

.EXAMPLE
$token = Get-IDBridgeSecret -Name 'ApiKey-Passphrase'

.EXAMPLE
$secret = Get-IDBridgeSecret -Name 'ApiKey-SkywardSMS' -AsPlainText

.NOTES
   Created by: Sam Cattanach
   Modified: 2026-07-01
#>

function Get-IDBridgeSecret {
    [CmdletBinding()]
    [OutputType([securestring], [string])]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,

        [Parameter()]
        [switch]$AsPlainText
    )

    $IDConfig = Get-IDBridgeConfig
    $Provider = if ($IDConfig.Secrets -and $IDConfig.Secrets.Provider) { $IDConfig.Secrets.Provider } else { 'Cms' }

    if ($Provider -eq 'AzKeyVault') {
        # Remote provider: no local envelope, fetch straight from Azure Key Vault
        $context = Get-IDBridgeAzKeyVaultContext

        Write-Log -Message "Secret: Retrieving '$Name' from Azure Key Vault '$($context.VaultUri)'." -Level Trace

        try {
            $plainValue = (Invoke-RestMethod -Headers $context.Headers -Uri "$($context.VaultUri)secrets/${Name}?api-version=$($context.ApiVersion)" -Method Get -ErrorAction Stop).value
        }
        catch {
            if ($_.Exception.Response.StatusCode -eq 404) {
                throw "IDBridge secret '$Name' was not found in Azure Key Vault '$($context.VaultUri)'. Add it with: Set-IDBridgeSecret -Name '$Name'."
            }
            throw "IDBridge secret '$Name' could not be read from Azure Key Vault '$($context.VaultUri)' ($_). Check the app registration's Key Vault access (e.g. the 'Key Vault Secrets User' role)."
        }
    }
    else {
        $vaultPath = $IDConfig.Paths.VaultRoot
        $envelopePath = Join-Path $vaultPath "$Name.secret.json"

        if (-not (Test-Path $envelopePath)) {
            throw "IDBridge secret '$Name' was not found in vault '$vaultPath'. Add it with: Set-IDBridgeSecret -Name '$Name'."
        }

        try { $envelope = Get-Content -Path $envelopePath -Raw | ConvertFrom-Json }
        catch { throw "IDBridge secret '$Name' has an unreadable envelope file at '$envelopePath' ($_)." }

        Write-Log -Message "Secret: Retrieving '$Name' from vault '$vaultPath' (provider $($envelope.provider))." -Level Trace

        try {
            switch ($envelope.provider) {
                'Cms' {
                    $plainValue = Unprotect-CmsMessage -Content $envelope.data -ErrorAction Stop
                }
                'DpapiNG' {
                    Import-IDBridgeDpapiNGType
                    $bytes = [IDBridge.DpapiNG]::Unprotect([System.Convert]::FromBase64String($envelope.data))
                    $plainValue = [System.Text.Encoding]::UTF8.GetString($bytes)
                }
                default { throw "unknown provider '$($envelope.provider)' in the envelope" }
            }
        }
        catch {
            throw "IDBridge secret '$Name' could not be decrypted ($_). Cms secrets need the 'IDBridge Secrets' certificate private key on this machine; DpapiNG secrets must be read as a principal named in the protection descriptor '$($envelope.protectedTo)'."
        }
    }

    if ($AsPlainText) {
        return $plainValue
    }

    return (ConvertTo-SecureString -String $plainValue -AsPlainText -Force)
}