Skip to content

Commit cc1f226

Browse files
committed
Merged PR 750974: Fix npm authentication for local builds
- Reruns the artifacts cred provider with the -I argument if the credential provided by the artifacts cred provider is invalid for npm. - Not necessary for Linux builds right now because they will always return a PAT, and nuget should call the cred provider with -I when necessary. Related work items: #1969322
1 parent ba137e4 commit cc1f226

File tree

1 file changed

+66
-21
lines changed

1 file changed

+66
-21
lines changed

Shared/Scripts/bxl.ps1

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,50 @@ if ($isRunningOnADO) {
766766
[string[]]$DominoArguments = @($DominoArguments | % { $_.Replace("#singlequote#", "'").Replace("#openparens#", "(").Replace("#closeparens#", ")"); })
767767
[string[]]$DominoArguments = $AdditionalBuildXLArguments + $DominoArguments;
768768

769+
function getTokenFromCredentialProvider() {
770+
[OutputType([string])]
771+
Param (
772+
[parameter(Mandatory=$true)]
773+
[string]
774+
$credProvider,
775+
776+
[parameter(Mandatory=$true)]
777+
[string]
778+
$internalFeed,
779+
780+
[parameter(Mandatory=$true)]
781+
[boolean]
782+
$isRetry
783+
)
784+
785+
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
786+
$processInfo.FileName = $credProvider
787+
$processInfo.RedirectStandardError = $true
788+
$processInfo.RedirectStandardOutput = $true
789+
$processInfo.UseShellExecute = $false
790+
$processInfo.CreateNoWindow = $true
791+
$processInfo.Arguments = "-U $internalFeed -V Information -C -F Json"
792+
if ($isRetry) {
793+
$processInfo.Arguments += " -I"
794+
}
795+
# tells the artifacts cred provider to generate a PAT instead of a self-describing token
796+
if ($processInfo.EnvironmentVariables.ContainsKey("NUGET_CREDENTIALPROVIDER_VSTS_TOKENTYPE")) {
797+
$processInfo.EnvironmentVariables["NUGET_CREDENTIALPROVIDER_VSTS_TOKENTYPE"] = "Compact"
798+
}
799+
else {
800+
$processInfo.EnvironmentVariables.Add("NUGET_CREDENTIALPROVIDER_VSTS_TOKENTYPE", "Compact")
801+
}
802+
$p = New-Object System.Diagnostics.Process
803+
$p.StartInfo = $processInfo
804+
$p.Start() | Out-Null
805+
$p.WaitForExit()
806+
$stdout = $p.StandardOutput.ReadToEnd()
807+
808+
# parse token from output
809+
$tokenMatches = $stdout | Select-String -Pattern '.*\{"Username":"[a-zA-Z0-9]*","Password":"(.*)"\}.*'
810+
return $tokenMatches.Matches.Groups[1].Value
811+
}
812+
769813
# The MS internal build needs authentication. When not running on ADO use the configured cred provider
770814
# to prompt for credentials as a way to guarantee the auth token will be cached for the subsequent build.
771815
# This may prompt an interactive pop-up/console. ADO pipelines already configure the corresponding env vars
@@ -810,26 +854,29 @@ if ($isMicrosoftInternal -and (-not $isRunningOnADO)) {
810854
# Set the cached credential into the user npmrc
811855
# we don't run vsts-npm-auth here because it requires us to have npm installed first
812856
# the code below will essentially duplicate what vsts-npm-auth performs
813-
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
814-
$processInfo.FileName = $credProvider
815-
$processInfo.RedirectStandardError = $true
816-
$processInfo.RedirectStandardOutput = $true
817-
$processInfo.UseShellExecute = $false
818-
$processInfo.CreateNoWindow = $true
819-
$processInfo.Arguments = "-U $internalFeed -V Information -C -F Json"
820-
# tells the artifacts cred provider to generate a PAT instead of a self-describing token
821-
if ($processInfo.EnvironmentVariables.ContainsKey("NUGET_CREDENTIALPROVIDER_VSTS_TOKENTYPE")) {
822-
$processInfo.EnvironmentVariables["NUGET_CREDENTIALPROVIDER_VSTS_TOKENTYPE"] = "Compact"
857+
$token = getTokenFromCredentialProvider $credProvider $internalFeed $false;
858+
859+
# verify whether the provided PAT is valid
860+
$auth = "username" + ':' + $token
861+
$encoded = [System.Text.Encoding]::UTF8.GetBytes($auth)
862+
$authorizationInfo = [System.Convert]::ToBase64String($encoded)
863+
$headers = @{"Authorization"="Basic $($authorizationInfo)"}
864+
$statusCode = 0;
865+
866+
try {
867+
$response = Invoke-WebRequest -Uri "https://feeds.dev.azure.com/cloudbuild/CloudBuild/_apis/packaging/feeds?api-version=7.1-preview.1" -Method GET -Headers $headers
868+
$statusCode = $response.StatusCode
823869
}
824-
else {
825-
$processInfo.EnvironmentVariables.Add("NUGET_CREDENTIALPROVIDER_VSTS_TOKENTYPE", "Compact")
870+
catch {
871+
$statusCode = 401
826872
}
827-
$p = New-Object System.Diagnostics.Process
828-
$p.StartInfo = $processInfo
829-
$p.Start() | Out-Null
830-
$p.WaitForExit()
831-
$stdout = $p.StandardOutput.ReadToEnd()
832-
873+
874+
# a 200 response indicates that the PAT is valid
875+
# if we didn't get this, we need to re-run the credential provider with the -I arg
876+
if ($statusCode -ne 200) {
877+
$token = getTokenFromCredentialProvider $credProvider $internalFeed $true;
878+
}
879+
833880
# npmrc files can contain multiple sources, so we'll create a buildxl specific one here
834881
if (![System.IO.File]::Exists("$env:USERPROFILE/.npmrc")) {
835882
New-Item -Path "$env:USERPROFILE/.npmrc" -ItemType File
@@ -838,9 +885,7 @@ if ($isMicrosoftInternal -and (-not $isRunningOnADO)) {
838885
Set-Content -Path "$env:USERPROFILE/.npmrc" -Value (Get-Content "$env:USERPROFILE/.npmrc" | Select-String -Pattern '.*\/\/cloudbuild\.pkgs\.visualstudio\.com\/_packaging\/BuildXL\.Selfhost\/npm\/registry.*' -NotMatch)
839886
}
840887

841-
# parse token from output and base64 encode
842-
$tokenMatches = $stdout | Select-String -Pattern '.*\{"Username":"[a-zA-Z0-9]*","Password":"(.*)"\}.*'
843-
$token = $tokenMatches.Matches.Groups[1].Value
888+
# base64 encode the token
844889
$b64token = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($token))
845890

846891
# add new lines with token to npmrc

0 commit comments

Comments
 (0)