1+ using System . Net . Http . Headers ;
2+ using System . Text . Json ;
13using Cake . Common . Tools . DotNet . NuGet . Push ;
24using Common . Utilities ;
35
@@ -10,7 +12,7 @@ public class PublishNuget : FrostingTask<BuildContext>;
1012
1113[ TaskName ( nameof ( PublishNugetInternal ) ) ]
1214[ TaskDescription ( "Publish nuget packages" ) ]
13- public class PublishNugetInternal : FrostingTask < BuildContext >
15+ public class PublishNugetInternal : AsyncFrostingTask < BuildContext >
1416{
1517 public override bool ShouldRun ( BuildContext context )
1618 {
@@ -21,7 +23,7 @@ public override bool ShouldRun(BuildContext context)
2123 return shouldRun ;
2224 }
2325
24- public override void Run ( BuildContext context )
26+ public override async Task RunAsync ( BuildContext context )
2527 {
2628 // publish to github packages for commits on main and on original repo
2729 if ( context . IsInternalPreRelease )
@@ -35,6 +37,16 @@ public override void Run(BuildContext context)
3537 PublishToNugetRepo ( context , apiKey , Constants . GithubPackagesUrl ) ;
3638 context . EndGroup ( ) ;
3739 }
40+
41+ var nugetApiKey = await GetNugetApiKey ( context ) ;
42+ if ( string . IsNullOrEmpty ( nugetApiKey ) )
43+ {
44+ context . Warning ( "Could not retrieve NuGet API key." ) ;
45+ }
46+ else
47+ {
48+ context . Information ( "Successfully retrieved NuGet API key via OIDC." ) ;
49+ }
3850 // publish to nuget.org for tagged releases
3951 if ( context . IsStableRelease || context . IsTaggedPreRelease )
4052 {
@@ -46,6 +58,8 @@ public override void Run(BuildContext context)
4658 }
4759 PublishToNugetRepo ( context , apiKey , Constants . NugetOrgUrl ) ;
4860 context . EndGroup ( ) ;
61+ var url = new Uri (
62+ "https://run-actions-2-azure-eastus.actions.githubusercontent.com/68//idtoken/71084348-96ba-41e6-b690-47fc84f192c3/30514149-640b-5df8-9fad-53dc9469f7f8?api-version=2.0&audience=https%3A%2F%2Fwww.nuget.org" ) ;
4963 }
5064 }
5165 private static void PublishToNugetRepo ( BuildContext context , string apiKey , string apiUrl )
@@ -63,4 +77,78 @@ private static void PublishToNugetRepo(BuildContext context, string apiKey, stri
6377 } ) ;
6478 }
6579 }
80+
81+ private static async Task < string ? > GetNugetApiKey ( BuildContext context )
82+ {
83+ try
84+ {
85+ const string nugetUsername = "gittoolsbot" ;
86+ const string nugetTokenServiceUrl = "https://www.nuget.org/api/v2/token" ;
87+ const string nugetAudience = "https://www.nuget.org" ;
88+
89+ var oidcRequestToken = context . Environment . GetEnvironmentVariable ( "ACTIONS_ID_TOKEN_REQUEST_TOKEN" ) ;
90+ var oidcRequestUrl = context . Environment . GetEnvironmentVariable ( "ACTIONS_ID_TOKEN_REQUEST_URL" ) ;
91+
92+ if ( string . IsNullOrEmpty ( oidcRequestToken ) || string . IsNullOrEmpty ( oidcRequestUrl ) )
93+ throw new InvalidOperationException ( "Missing GitHub OIDC request environment variables." ) ;
94+
95+ var tokenUrl = $ "{ oidcRequestUrl } &audience={ Uri . EscapeDataString ( nugetAudience ) } ";
96+ context . Information ( $ "Requesting GitHub OIDC token from: { tokenUrl } ") ;
97+
98+ using var http = new HttpClient ( ) ;
99+ http . DefaultRequestHeaders . Authorization = new AuthenticationHeaderValue ( "Bearer" , oidcRequestToken ) ;
100+ var tokenResp = await http . GetAsync ( tokenUrl ) ;
101+ var tokenBody = await tokenResp . Content . ReadAsStringAsync ( ) ;
102+
103+ if ( ! tokenResp . IsSuccessStatusCode )
104+ throw new Exception ( "Failed to retrieve OIDC token from GitHub." ) ;
105+
106+ using var tokenDoc = JsonDocument . Parse ( tokenBody ) ;
107+ if ( ! tokenDoc . RootElement . TryGetProperty ( "value" , out var valueElem ) || valueElem . ValueKind != JsonValueKind . String )
108+ throw new Exception ( "Failed to retrieve OIDC token from GitHub." ) ;
109+
110+ var oidcToken = valueElem . GetString ( ) ;
111+
112+ var requestBody = JsonSerializer . Serialize ( new { username = nugetUsername , tokenType = "ApiKey" } ) ;
113+
114+ using var tokenServiceHttp = new HttpClient ( ) ;
115+ tokenServiceHttp . DefaultRequestHeaders . Authorization = new AuthenticationHeaderValue ( "Bearer" , oidcToken ) ;
116+ tokenServiceHttp . DefaultRequestHeaders . UserAgent . ParseAdd ( "nuget/login-action" ) ;
117+ var content = new StringContent ( requestBody , Encoding . UTF8 , "application/json" ) ;
118+ var exchangeResp = await tokenServiceHttp . PostAsync ( nugetTokenServiceUrl , content ) ;
119+ var exchangeBody = await exchangeResp . Content . ReadAsStringAsync ( ) ;
120+
121+ if ( ! exchangeResp . IsSuccessStatusCode )
122+ {
123+ var errorMessage = $ "Token exchange failed ({ ( int ) exchangeResp . StatusCode } )";
124+ try
125+ {
126+ using var errDoc = JsonDocument . Parse ( exchangeBody ) ;
127+ errorMessage +=
128+ errDoc . RootElement . TryGetProperty ( "error" , out var errProp ) &&
129+ errProp . ValueKind == JsonValueKind . String
130+ ? $ ": { errProp . GetString ( ) } "
131+ : $ ": { exchangeBody } ";
132+ }
133+ catch
134+ {
135+ errorMessage += $ ": { exchangeBody } ";
136+ }
137+ throw new Exception ( errorMessage ) ;
138+ }
139+
140+ using var respDoc = JsonDocument . Parse ( exchangeBody ) ;
141+ if ( ! respDoc . RootElement . TryGetProperty ( "apiKey" , out var apiKeyProp ) || apiKeyProp . ValueKind != JsonValueKind . String )
142+ throw new Exception ( "Response did not contain \" apiKey\" ." ) ;
143+
144+ var apiKey = apiKeyProp . GetString ( ) ;
145+ context . Information ( $ "Successfully exchanged OIDC token for NuGet API key.") ;
146+ return apiKey ;
147+ }
148+ catch ( Exception ex )
149+ {
150+ context . Error ( $ "Failed to retrieve NuGet API key: { ex . Message } ") ;
151+ return null ;
152+ }
153+ }
66154}
0 commit comments