src/_Invoke-CciCredentialProvider.ps1

function _Invoke-CciCredentialProvider {
<#
.SYNOPSIS
    Acquires a feed token by running the Azure Artifacts Credential Provider
    in stand-alone mode (interactive device-code flow).
.DESCRIPTION
    On a fresh machine with no Azure CLI session and no cached provider token,
    PSResourceGet invokes the credential provider in PLUGIN mode, which cannot
    surface an interactive prompt — it just fails (exit code 2). Running the
    provider directly in stand-alone mode does present the device-code flow and
    populates its session token cache.
 
    Returns the acquired token (string) on success, $null on failure. Device-code
    instructions are written by the provider to stderr, which is left attached to
    the console so the user can see and act on them; the token itself comes back
    on stdout as JSON and is never printed.
.PARAMETER FeedUrl
    Package source URI to authenticate against.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$FeedUrl
    )

    $exe = Join-Path $env:USERPROFILE '.nuget\plugins\netcore\CredentialProvider.Microsoft\CredentialProvider.Microsoft.exe'
    if (-not (Test-Path $exe)) {
        $found = Get-ChildItem (Join-Path $env:USERPROFILE '.nuget\plugins') -Recurse -Filter 'CredentialProvider.Microsoft.exe' -ErrorAction SilentlyContinue |
            Select-Object -First 1
        if (-not $found) {
            Write-Verbose "cciget: credential provider executable not found; cannot run interactive sign-in."
            return $null
        }
        $exe = $found.FullName
    }

    Write-Host "cciget: signing in to the feed (device code follows; complete it in a browser)..."

    # Native tools write progress/instructions to stderr. Under
    # $ErrorActionPreference = 'Stop' the FIRST such line becomes a terminating
    # error before the exit code can be inspected, so relax it locally and gate
    # strictly on $LASTEXITCODE.
    $previousEap = $ErrorActionPreference
    $ErrorActionPreference = 'Continue'
    try {
        # -C false : force device-code flow instead of a UI dialog (no broker on
        # a freshly built machine, and OOBE shells are often headless)
        # -I : do not return possibly-stale cached credentials
        # -F Json : token on stdout; human instructions (incl. the device code)
        # go to stderr, which stays attached to the console
        $stdout = & $exe -U $FeedUrl -C false -I -V Information -F Json
        $exit = $LASTEXITCODE
    } finally {
        $ErrorActionPreference = $previousEap
    }

    if ($exit -ne 0) {
        Write-Verbose "cciget: credential provider exited with code $exit."
        return $null
    }

    $joined = ($stdout | Out-String).Trim()
    if (-not $joined) { return $null }
    try {
        $parsed = $joined | ConvertFrom-Json
    } catch {
        Write-Verbose "cciget: could not parse credential provider output as JSON."
        return $null
    }
    $passwordProp = $parsed.PSObject.Properties['Password']
    if (-not $passwordProp -or -not $passwordProp.Value) { return $null }
    return $passwordProp.Value
}