Azure App Service の Web AppsにはAzure ADで簡単に認証を設定できたりします(Easy Authというらしい)。
Azure App Service での認証と承認 | Microsoft Docs
これを使うとアプリ側に認証コード書かなくて済むのと、ASP.NET (Coreじゃない) の場合は、
すべての言語フレームワークで、App Service はユーザーの要求を要求ヘッダーに挿入することによってコードで要求を使用できるようにします。 ASP.NET 4.6 アプリの場合、App Service は認証されたユーザーの要求で ClaimsPrincipal.Current を設定するので、標準の .NET コード パターン ([Authorize] 属性など) に従うことができます。
フレームワーク側でPrincipalに適宜情報を設定してくれたりしますが、ASP.NET Core 2.xではサポートされていません。どうすればいいのかググってたらいくつか方法があったので、備忘録をブログっておきます。
stackoverflow の以下の記事。
抜粋して拡張メソッド化すると以下のような感じです。app.UseEasyAuth()
とかすればOKです。
パイプライン途中でヘッダ情報からGenericPrincipalを生成して、Context.Userに設定する方法です。
public static class ApplicationBuilderExtension { public static void UseEasyAuth(this IApplicationBuilder app) { app.Use(async (context, next) => { // Create a user on current thread from provided header if (context.Request.Headers.ContainsKey("X-MS-CLIENT-PRINCIPAL-ID")) { // Read headers from Azure var azureAppServicePrincipalIdHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"][0]; var azureAppServicePrincipalNameHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"][0]; // Create claims id var claims = new Claim[] { new System.Security.Claims.Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", azureAppServicePrincipalIdHeader), new System.Security.Claims.Claim("name", azureAppServicePrincipalNameHeader) }; // Set user in current context as claims principal var identity = new GenericIdentity(azureAppServicePrincipalNameHeader); identity.AddClaims(claims); // Set current thread user to identity context.User = new GenericPrincipal(identity, null); } await next.Invoke(); }); } }
もう1つは、こちらの記事です。
こちらも抜粋すると以下の通り。上との違いは、/.auth/me
にリクエストなげて自身の情報をゲットしてクレーム作っているとこでしょうか。
アプリケーションでは、/.auth/me を呼び出して認証されたユーザーの追加の詳細を取得することもできます。
public static void UseEasyAuth2(this IApplicationBuilder app) { app.Use(async (context, next) => { // Create a user on current thread from provided header if (context.Request.Headers.ContainsKey("X-MS-CLIENT-PRINCIPAL-ID")) { // Read headers from Azure var azureAppServicePrincipalIdHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"][0]; var azureAppServicePrincipalNameHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"][0]; #region extract claims via call /.auth/me //invoke /.auth/me var cookieContainer = new CookieContainer(); HttpClientHandler handler = new HttpClientHandler() { CookieContainer = cookieContainer }; string uriString = $"{context.Request.Scheme}://{context.Request.Host}"; foreach (var c in context.Request.Cookies) { cookieContainer.Add(new Uri(uriString), new Cookie(c.Key, c.Value)); } string jsonResult; using (HttpClient client = new HttpClient(handler)) { var res = await client.GetAsync($"{uriString}/.auth/me"); jsonResult = await res.Content.ReadAsStringAsync(); } //parse json var obj = JArray.Parse(jsonResult); string user_id = obj[0]["user_id"].Value<string>(); //user_id // Create claims id List<Claim> claims = new List<Claim>(); foreach (var claim in obj[0]["user_claims"]) { claims.Add(new Claim(claim["typ"].ToString(), claim["val"].ToString())); } // Set user in current context as claims principal var identity = new GenericIdentity(azureAppServicePrincipalIdHeader); identity.AddClaims(claims); #endregion // Set current thread user to identity context.User = new GenericPrincipal(identity, null); } await next.Invoke(); });
リクエストごとに呼ばれるよりはキャッシュしたりしたほうが実用的でしょう。どちらの方法を使うかはケースバイケースで。