--- lang: ja-jp breaks: true --- # 概要 ![](https://hackmd.io/_uploads/SkV2Hvi52.png) # WebAPI ## テンプレートからプロジェクトを作成 ![](https://hackmd.io/_uploads/r1BAoIjc3.png) ![](https://hackmd.io/_uploads/Bkne2Isqn.png) ## csproj ```xml= ・・・ <ItemGroup> <PackageReference Include="Microsoft.Identity.Web" Version="2.13.0" /> </ItemGroup> ・・・ ``` ## appsettings.json ```json= { "AzureAd": { "Instance": "https://login.microsoftonline.com/", "ClientId": "WebAPIのアプリケーション (クライアント) ID", "Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]", "TenantId": "WebAPIのディレクトリ (テナント) ID" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } ``` ## Program.cs ```csharp= ・・・ IConfigurationSection configuration = builder.Configuration.GetSection("AzureAd"); builder.Services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(configuration); ・・・ ``` ## WeatherForecastController.cs ```csharp= ・・・ [Authorize(Roles = "API.Console")] [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase ・・・ ``` :::info `[Authorize(Roles = "API.Console")]`を追加する。 "API.Console"は、`Azure Active Directory` にアプリロールとして追加された値を指定する。 * ![](https://hackmd.io/_uploads/S1R6dPw52.png) * ![](https://hackmd.io/_uploads/rJcNtwv53.png) * ![](https://hackmd.io/_uploads/rk-wFwwch.png) ::: # Console ## テンプレートからプロジェクトを作成 ![](https://hackmd.io/_uploads/rJgjC8i53.png) ![](https://hackmd.io/_uploads/ryZnRLj92.png) ## csproj ```xml= ・・・ <ItemGroup> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Identity.Web" Version="2.13.0" /> </ItemGroup> <ItemGroup> <None Update="appsettings.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> ・・・ ``` ## appsettings.json ```json= { "Instance": "https://login.microsoftonline.com/{0}", "Tenant": "コンソールアプリのディレクトリ (テナント) ID", "ClientId": "コンソールアプリのアプリケーション (クライアント) ID", "ClientSecret": "コンソールアプリのクライアントシークレットの値", "BaseAddress": "https://localhost:44372", "Scope": "WebAPIのアプリケーション ID の URI/.default", "Certificate": {} } ``` ## AuthenticationConfig.cs ```csharp= using Microsoft.Identity.Web; namespace Console; public class AuthenticationConfig { public string Instance { get; set; } = "https://login.microsoftonline.com/{0}"; public string Tenant { get; set; } = ""; public string ClientId { get; set; } = ""; public string ClientSecret { get; set; } = ""; public CertificateDescription? Certificate { get; set; } = null; public string BaseAddress { get; set; } = ""; public string Scope { get; set; } = ""; } ``` ## Program.cs ```csharp= // See https://aka.ms/new-console-template for more information using System.Diagnostics; using System.Globalization; using System.Net.Http.Headers; using System.Text.Json.Nodes; using Console; using Microsoft.Extensions.Configuration; using Microsoft.Identity.Client; using Microsoft.Identity.Web; System.Console.WriteLine("Hello, World!"); // Reads the configuration from a json file AuthenticationConfig? config; { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); IConfigurationRoot Configuration = builder.Build(); config = Configuration.Get<AuthenticationConfig>(); } if (config == null) { throw new Exception("AuthenticationConfig is null"); } System.Console.WriteLine(config); // Even if this is a console application here, a daemon application is a confidential client application Uri authorityUri = new Uri(String.Format(CultureInfo.InvariantCulture, config.Instance, config.Tenant)); IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithClientSecret(config.ClientSecret) .WithAuthority(authorityUri) .Build(); // Add an in-memory well partitioned token cache to MSAL.NET confidential client application. // Don't use this method in ASP.NET Core: rather use: services.AddInMemoryTokenCache() in ConfigureServices. app.AddInMemoryTokenCache(); // With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the // application permissions need to be set statically (in the portal or by PowerShell), and then granted by // a tenant administrator string[] scopes = new string[] { config.Scope }; AuthenticationResult? result = null; result = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); if (result != null) { System.Console.WriteLine($"[AccessToken]{result.AccessToken}"); System.Console.WriteLine($"[ExpiresOn]{result.ExpiresOn.LocalDateTime.ToString()}"); await CallWebApiAndProcessResultASync( $"{config.BaseAddress}/WeatherForecast", result.AccessToken, node => System.Console.WriteLine(node.ToString()) ); } static async Task CallWebApiAndProcessResultASync( string webApiUrl, string accessToken, Action<JsonNode> processResult ) { var httpClient = new HttpClient(); if (!string.IsNullOrEmpty(accessToken)) { var defaultRequestHeaders = httpClient.DefaultRequestHeaders; if ( defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json") ) { httpClient.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json") ); } defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage response = await httpClient.GetAsync(webApiUrl) .ConfigureAwait(false); string content = await response.Content.ReadAsStringAsync() .ConfigureAwait(false); if (response.IsSuccessStatusCode) { string json = content; JsonNode? result = JsonNode.Parse(json); if (result != null) { processResult(result); } } else { Debug.WriteLine($"Failed to call the Web Api: {response.StatusCode}[{content}]"); } } } ``` :::info `AcquireTokenForClient`で取得される`AccessToken`の有効期限は、`ExpiresOn`により取得できる。 ※取得後1時間の日時が取得される。 ::: # 実行 ![](https://hackmd.io/_uploads/Sy4gewo53.png) ![](https://hackmd.io/_uploads/BJ9SgPi5n.png)