Providers/Okta/Public/Get-OktaServiceApp.ps1
|
function Get-OktaServiceApp { <# .SYNOPSIS Shows where the service app credential is stored and how it is protected .DESCRIPTION Answers "where did the key actually go, and is it encrypted?" without having to open a JSON file and interpret it. The private key is deliberately NOT returned by default. Everything this module does with the key it does internally - Get-OktaAccessToken signs an assertion and hands back a token - so there is no ordinary workflow that needs the raw key in a variable, and a function that returns one by default is a function that puts one in transcripts and scrollback. -IncludePrivateKey is there for the case where you genuinely need to move the key into another tool, and it asks first. .PARAMETER CredentialPath Path to the credential file. Defaults to the per-user location for the connected org. .PARAMETER OrgUrl Org whose credential to look up. Defaults to the connected org. .PARAMETER VaultPassword Password used to unlock the SecretStore vault, when the credential is stored in one .PARAMETER IncludePrivateKey Also return the private JWK. Prompts for confirmation, because the returned object then contains a live admin credential. .OUTPUTS PSCustomObject describing the credential .EXAMPLE Get-OktaServiceApp Shows the client id, scopes, storage location and protection method .EXAMPLE Get-OktaServiceApp -IncludePrivateKey -Confirm:$false | Select-Object -Expand PrivateJwk Extracts the raw key, for moving it into another tool .NOTES Author: Jeffrey Stuhr Version: 1.0.0 Last Updated: 2026-08-07 Protection values: - DPAPI encrypted into the file, bound to this user and machine (the default) - SecretStore encrypted in a vault; the file holds only a pointer - None unprotected, guarded only by file permissions .LINK New-OktaServiceApp Get-OktaAccessToken #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'CredentialPath', Justification = 'A file path, not a credential. The key it points at never appears here.')] [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [OutputType([PSCustomObject])] param( [Parameter()] [string]$CredentialPath, [Parameter()] [string]$OrgUrl, [Parameter()] [System.Security.SecureString]$VaultPassword, [Parameter()] [switch]$IncludePrivateKey ) if (-not $CredentialPath) { $connection = Get-OktaConnection -AllowNone $resolvedOrg = if ($OrgUrl) { $OrgUrl } elseif ($connection) { $connection.OrgUrl } else { $null } if (-not $resolvedOrg) { throw ('Specify -CredentialPath or -OrgUrl, or connect first, so the credential ' + 'file can be located.') } $CredentialPath = Get-OktaCredentialPath -OrgUrl $resolvedOrg } $importArgs = @{ Path = $CredentialPath } if ($VaultPassword) { $importArgs.VaultPassword = $VaultPassword } $credential = Import-OktaAppCredential @importArgs $result = [PSCustomObject]@{ OrgUrl = $credential.orgUrl ClientId = $credential.clientId AppId = $credential.appId Label = $credential.label Scopes = @($credential.scopes) Protection = $credential.protection Encrypted = ($credential.protection -in @('DPAPI', 'SecretStore')) VaultName = $credential.vaultName SecretName = $credential.secretName CredentialPath = $CredentialPath CreatedUtc = $credential.createdUtc } if ($credential.protection -eq 'None') { Write-Warning ("The private key for $($credential.clientId) is stored UNENCRYPTED at " + "'$CredentialPath'. Re-run New-OktaServiceApp -Force to replace it, adding " + '-UseSecretStore if this platform has no DPAPI.') } if ($IncludePrivateKey) { if ($PSCmdlet.ShouldProcess($credential.clientId, 'Return the private key in plain form')) { $result | Add-Member -NotePropertyName PrivateJwk -NotePropertyValue $credential.privateJwk } } return $result } |