From f30c9c75790dc2da2e625b8e40874731837d9f16 Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Mon, 12 Feb 2024 11:15:34 +0900 Subject: [PATCH 1/2] Using Bearer Token for Kudu REST API calls --- AppService.Acmebot/Internal/KuduClient.cs | 21 ++++++++++++------- .../Internal/KuduClientFactory.cs | 8 +++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/AppService.Acmebot/Internal/KuduClient.cs b/AppService.Acmebot/Internal/KuduClient.cs index 1cd853c..a1cccb7 100644 --- a/AppService.Acmebot/Internal/KuduClient.cs +++ b/AppService.Acmebot/Internal/KuduClient.cs @@ -3,28 +3,33 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Text; +using System.Threading; using System.Threading.Tasks; +using Azure.Core; + namespace AppService.Acmebot.Internal; public class KuduClient { - public KuduClient(HttpClient httpClient, Uri scmUri) + public KuduClient(HttpClient httpClient, Uri scmUri, TokenCredential tokenCredential) { _httpClient = httpClient; _scmHost = scmUri.Host; - _basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes(scmUri.UserInfo)); + _tokenCredential = tokenCredential; } private readonly HttpClient _httpClient; private readonly string _scmHost; - private readonly string _basicAuth; + private readonly TokenCredential _tokenCredential; public async Task ExistsFileAsync(string filePath) { + var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(), CancellationToken.None); + var request = new HttpRequestMessage(HttpMethod.Head, $"https://{_scmHost}/api/vfs/site/{filePath}"); - request.Headers.Authorization = new AuthenticationHeaderValue("Basic", _basicAuth); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token); var response = await _httpClient.SendAsync(request); @@ -43,15 +48,17 @@ public async Task ExistsFileAsync(string filePath) return false; } - public Task WriteFileAsync(string filePath, string content) + public async Task WriteFileAsync(string filePath, string content) { + var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(), CancellationToken.None); + var request = new HttpRequestMessage(HttpMethod.Put, $"https://{_scmHost}/api/vfs/site/{filePath}"); - request.Headers.Authorization = new AuthenticationHeaderValue("Basic", _basicAuth); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token); request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); request.Content = new StringContent(content, Encoding.UTF8); - return _httpClient.SendAsync(request); + await _httpClient.SendAsync(request); } } diff --git a/AppService.Acmebot/Internal/KuduClientFactory.cs b/AppService.Acmebot/Internal/KuduClientFactory.cs index 3e79cdc..8013ac9 100644 --- a/AppService.Acmebot/Internal/KuduClientFactory.cs +++ b/AppService.Acmebot/Internal/KuduClientFactory.cs @@ -1,21 +1,25 @@ using System; using System.Net.Http; +using Azure.Core; + namespace AppService.Acmebot.Internal; public class KuduClientFactory { - public KuduClientFactory(IHttpClientFactory httpClientFactory) + public KuduClientFactory(IHttpClientFactory httpClientFactory, TokenCredential tokenCredential) { _httpClientFactory = httpClientFactory; + _tokenCredential = tokenCredential; } private readonly IHttpClientFactory _httpClientFactory; + private readonly TokenCredential _tokenCredential; public KuduClient CreateClient(Uri scmUri) { var httpClient = _httpClientFactory.CreateClient(); - return new KuduClient(httpClient, scmUri); + return new KuduClient(httpClient, scmUri, _tokenCredential); } } From b90f2136dbd0b207990da86244f3b1f74ca16680 Mon Sep 17 00:00:00 2001 From: Tatsuro Shibamura Date: Wed, 14 Feb 2024 04:04:40 +0900 Subject: [PATCH 2/2] Complete prefer use Bearer Token --- .../Functions/SharedActivity.cs | 34 +++++++++---------- AppService.Acmebot/Internal/KuduClient.cs | 23 +++---------- .../Internal/KuduClientFactory.cs | 17 ++++++++-- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/AppService.Acmebot/Functions/SharedActivity.cs b/AppService.Acmebot/Functions/SharedActivity.cs index 0941f35..6cdb20b 100644 --- a/AppService.Acmebot/Functions/SharedActivity.cs +++ b/AppService.Acmebot/Functions/SharedActivity.cs @@ -209,22 +209,22 @@ public async Task Http01Precondition([ActivityTrigger] string id) private async Task Http01Precondition_WebSite(ResourceIdentifier resourceId) { - var webSite = _armClient.GetWebSiteResource(resourceId); + WebSiteResource webSite = await _armClient.GetWebSiteResource(resourceId).GetAsync(); WebSiteConfigResource config = await webSite.GetWebSiteConfig().GetAsync(); // 既に .well-known が仮想アプリケーションとして追加されているか確認 var virtualApplication = config.Data.VirtualApplications.FirstOrDefault(x => x.VirtualPath == "/.well-known"); - if (virtualApplication != null) + if (virtualApplication is not null) { return; } - // 発行プロファイルを取得 - var credentials = await webSite.GetPublishingCredentialsAsync(WaitUntil.Completed); + // ファイル操作用の KuduClient を作成 + var scmUri = webSite.Data.HostNameSslStates.First(x => x.HostType == AppServiceHostType.Repository); - var kuduClient = _kuduClientFactory.CreateClient(credentials.Value.Data.ScmUri); + var kuduClient = await _kuduClientFactory.CreateClientAsync(scmUri.Name); try { @@ -253,22 +253,22 @@ private async Task Http01Precondition_WebSite(ResourceIdentifier resourceId) private async Task Http01Precondition_WebSiteSlot(ResourceIdentifier resourceId) { - var webSiteSlot = _armClient.GetWebSiteSlotResource(resourceId); + WebSiteSlotResource webSiteSlot = await _armClient.GetWebSiteSlotResource(resourceId).GetAsync(); WebSiteSlotConfigResource config = await webSiteSlot.GetWebSiteSlotConfig().GetAsync(); // 既に .well-known が仮想アプリケーションとして追加されているか確認 var virtualApplication = config.Data.VirtualApplications.FirstOrDefault(x => x.VirtualPath == "/.well-known"); - if (virtualApplication != null) + if (virtualApplication is not null) { return; } - // 発行プロファイルを取得 - var credentials = await webSiteSlot.GetPublishingCredentialsSlotAsync(WaitUntil.Completed); + // ファイル操作用の KuduClient を作成 + var scmUri = webSiteSlot.Data.HostNameSslStates.First(x => x.HostType == AppServiceHostType.Repository); - var kuduClient = _kuduClientFactory.CreateClient(credentials.Value.Data.ScmUri); + var kuduClient = await _kuduClientFactory.CreateClientAsync(scmUri.Name); try { @@ -329,25 +329,25 @@ public async Task> Http01Authorization([Activ }); } - // 発行プロファイルを取得 - PublishingUserData credentials; + // ファイル操作用の KuduClient を作成 + HostNameSslState scmUri; var resourceId = new ResourceIdentifier(id); if (resourceId.ResourceType == WebSiteResource.ResourceType) { - var webSite = _armClient.GetWebSiteResource(resourceId); + WebSiteResource webSite = await _armClient.GetWebSiteResource(resourceId).GetAsync(); - credentials = (await webSite.GetPublishingCredentialsAsync(WaitUntil.Completed)).Value.Data; + scmUri = webSite.Data.HostNameSslStates.First(x => x.HostType == AppServiceHostType.Repository); } else { - var webSiteSlot = _armClient.GetWebSiteSlotResource(resourceId); + WebSiteSlotResource webSiteSlot = await _armClient.GetWebSiteSlotResource(resourceId).GetAsync(); - credentials = (await webSiteSlot.GetPublishingCredentialsSlotAsync(WaitUntil.Completed)).Value.Data; + scmUri = webSiteSlot.Data.HostNameSslStates.First(x => x.HostType == AppServiceHostType.Repository); } - var kuduClient = _kuduClientFactory.CreateClient(credentials.ScmUri); + var kuduClient = await _kuduClientFactory.CreateClientAsync(scmUri.Name); // Kudu API を使い、Answer 用のファイルを作成 foreach (var challengeResult in challengeResults) diff --git a/AppService.Acmebot/Internal/KuduClient.cs b/AppService.Acmebot/Internal/KuduClient.cs index a1cccb7..d1ffa96 100644 --- a/AppService.Acmebot/Internal/KuduClient.cs +++ b/AppService.Acmebot/Internal/KuduClient.cs @@ -1,35 +1,23 @@ -using System; -using System.Net; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; -using System.Threading; using System.Threading.Tasks; -using Azure.Core; - namespace AppService.Acmebot.Internal; public class KuduClient { - public KuduClient(HttpClient httpClient, Uri scmUri, TokenCredential tokenCredential) + public KuduClient(HttpClient httpClient) { _httpClient = httpClient; - _scmHost = scmUri.Host; - _tokenCredential = tokenCredential; } private readonly HttpClient _httpClient; - private readonly string _scmHost; - private readonly TokenCredential _tokenCredential; public async Task ExistsFileAsync(string filePath) { - var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(), CancellationToken.None); - - var request = new HttpRequestMessage(HttpMethod.Head, $"https://{_scmHost}/api/vfs/site/{filePath}"); - - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token); + var request = new HttpRequestMessage(HttpMethod.Head, $"/api/vfs/site/{filePath}"); var response = await _httpClient.SendAsync(request); @@ -50,11 +38,8 @@ public async Task ExistsFileAsync(string filePath) public async Task WriteFileAsync(string filePath, string content) { - var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(), CancellationToken.None); - - var request = new HttpRequestMessage(HttpMethod.Put, $"https://{_scmHost}/api/vfs/site/{filePath}"); + var request = new HttpRequestMessage(HttpMethod.Put, $"/api/vfs/site/{filePath}"); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token); request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); request.Content = new StringContent(content, Encoding.UTF8); diff --git a/AppService.Acmebot/Internal/KuduClientFactory.cs b/AppService.Acmebot/Internal/KuduClientFactory.cs index 8013ac9..7124aac 100644 --- a/AppService.Acmebot/Internal/KuduClientFactory.cs +++ b/AppService.Acmebot/Internal/KuduClientFactory.cs @@ -1,5 +1,8 @@ using System; using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; using Azure.Core; @@ -7,19 +10,27 @@ namespace AppService.Acmebot.Internal; public class KuduClientFactory { - public KuduClientFactory(IHttpClientFactory httpClientFactory, TokenCredential tokenCredential) + public KuduClientFactory(IHttpClientFactory httpClientFactory, TokenCredential tokenCredential, AzureEnvironment environment) { _httpClientFactory = httpClientFactory; _tokenCredential = tokenCredential; + _environment = environment; } private readonly IHttpClientFactory _httpClientFactory; private readonly TokenCredential _tokenCredential; + private readonly AzureEnvironment _environment; - public KuduClient CreateClient(Uri scmUri) + public async Task CreateClientAsync(string scmHost) { var httpClient = _httpClientFactory.CreateClient(); - return new KuduClient(httpClient, scmUri, _tokenCredential); + var context = new TokenRequestContext(new[] { _environment.ResourceManager.DefaultScope }, null); + var accessToken = await _tokenCredential.GetTokenAsync(context, CancellationToken.None); + + httpClient.BaseAddress = new Uri($"https://{scmHost}"); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token); + + return new KuduClient(httpClient); } }