การเข้าใช้งาน Web App ที่อยู่บน Azure App Service ให้บังคับ authen ด้วย Microsoft Authentication provider ก่อน ถึงจะใช้งานได้
- สร้าง Azure App Service
- ลงทะเบียน Web App ที่ Application Registration Portal
- คอนฟิก App Service ให้บังคับ Authentication
- อ่านค่า userid จาก .auth/me
1.สร้าง Azure App Service
ไปที่ Portal เลือกเมนู Create a resource > Web > Web App
ตั้งชื่อ jackwebapp แล้วจะได้ url เป็น jackwebapp.azurewebsites.net
Note: ตรง Operating System ถ้าเลือก Windows เมื่อสร้าง App Service เสร็จ แล้วเข้ามาแก้ไข Configuration > Stack Settings จะมีให้เลือกแค่ .NET, PHP, Python และ Java แต่ถ้า
ตรง Operating System เลือก Linux ที่ Stack Settings จะมีให้เลือกเพียบเลย
เมื่อสร้างเสร็จ กด Browse
หรือเรียกไปที่ https://jackwebapp.azurewebsites.net/
ขึ้นแบบนี้แสดงว่าเว็บพร้อมใช้งานละ
2.ลงทะเบียน Web App ที่ Application Registration Portal
ลงทะเบียน Application ที่ https://apps.dev.microsoft.com/
กด Add an app
แล้วตั้งชื่อเช่น JackWebAppRegis (ชื่อนี้จะแสดงให้ user เห็นตอน login ฉะนั้นตั้งให้ friendly หน่อย)
หน้าถัดมาจะเห็น Application Id แต่ยังไม่มี password
กดปุ่ม Generate new password แล้วจด password ไว้
เสร็จแล้ว Add Platform แล้วเลือก Web
ตรง Redirect URLs ให้ใส่ รูปแบบนี้
https://<your_string>.azurewebsites.net/.auth/login/microsoftaccount/callback
เช่น
https://jackwebapp.azurewebsites.net/.auth/login/microsoftaccount/callback
กด Save
3. คอนฟิก App Service ให้บังคับ Authentication
กลับมาที่ Portal เข้า App Service ที่เราสร้าง (jackwebapp)
เลือก Authentication / Authorization
กด On แล้วเลือก Microsoft
ใส่ค่า Client id และ Client Secret ที่ได้มาจากตอนลงทะเบียน application ในขั้นตอนที่ 2 แล้วกด OK จะกลับมาที่หน้า Authentication / Authorization
ที่ Action to take when request is not authenticated ให้เลือกเป็น Log in with Microsoft Account
แล้วกด Save
ตรงนี้เป็นการบังคับให้ต้อง authen ด้วย Microsoft account ก่อนถึงจะเข้าใข้เว็บได้
แต่ถ้าจะไม่บังคับก็ให้เลือกเป็น Allow Anonymous requests (no action)
ทีนี้พอเข้าเว็บเราอีกที https://jackwebapp.azurewebsites.net/ ก็จะ redirect ไปให้ login ด้วย Microsoft account ก่อน
เมื่อใส่ user & pass เสร็จ ก็จะมีการขอสิทธิเข้าถึงเบื้องต้น
กด Yes ก็จะเข้าเว็บ https://jackwebapp.azurewebsites.net/ ได้ละ
ถ้าจะดูว่าเราได้ claim อะไรบ้างก็ดูที่ https://jackwebapp.azurewebsites.net/.auth/me
เช่น
- user_id
- user_claims
- provider_name
- access_token
- expires_on
Note: การคอนฟิก App Service ให้ใช้ Authentication เป็นการทำ Single Sign-On ไปในตัว ไม่ว่าจะบังคับ (Log in with Microsoft Account
) หรือ ไม่บังคับ (Allow Anonymous requests (no action)
) ก็ทำให้สามารถใช้ browser เข้าไปดูไฟล์ หรือ deploy ไฟล์ .zip ได้ โดยเข้าไปที่
https://jackwebapp.scm.azurewebsites.net/ZipDeployUI
แต่ถ้าไม่ได้ login หรือ user ที่ login ไม่มีสิทธิ จะฟ้อง error ว่า
You are not authorized or do not have any subscriptions associated with your account.
4.อ่านค่า userid จาก .auth/me
วิธีที่ใช้คืออ่านค่าจาก .auth/me
ไมไ่ด้ใช้ Azure Mobile Apps: .NET Client SDK เพราะดูแล้วน่าจะง่ายกว่าเพราะเราใช้ .NET Core แต่ Package ที่ SDK ใช้ยังเป็น .NET Framework
แต่ถ้าเป็น mobile apps ไม่แน่ใจ ต้องลองดูที่ Authentication and authorization in Azure App Service for mobile apps อีกที
สร้างโปรเจ็กส์แบบ Web Application ด้วย .NET Core 2.1
Publish เว็บแอพไป Azure App Service ด้วย Visual Studio
ลองรันดู ถ้ารันได้ ก็ OK แต่ไม่รู้ว่า user ที่เข้ามาเป็นใคร ดังนั้นต่อมาเราจะหา userid
สร้างคลาส Global ไว้เก็บค่า userid
namespace JackWebApp { public class Global { public static string userid; public static string username; } }
ที่ไฟล์ Startup.cs แก้ไข Configure() ให้รับค่ามาจาก endpoint .auth/me
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Security.Principal; namespace JackWebApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, next) => { //Global.userid += "0"; // Create a user on current thread from provided header if (context.Request.Headers.ContainsKey("X-MS-CLIENT-PRINCIPAL-ID")) { //Global.userid += "1"; // 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 = string.Empty; 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 string provider_name = obj[0]["provider_name"].Value<string>(); Global.username = user_id; // Create claims id List<Claim> claims = new List<Claim>(); foreach (var claim in obj[0]["user_claims"]) { if (provider_name == "microsoftaccount") { if (claim["typ"].ToString() == "urn:microsoftaccount:id") { Global.userid = claim["val"].ToString(); } } 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(); }); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(); } } }
แสดงค่า userid ที่ About.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages; using System.Linq; using System.Security.Claims; namespace TipWebApp.Pages { public class AboutModel : PageModel { public string Message { get; set; } public void OnGet() { //Message = "Your application description page."; Message = Global.userid + " " + Global.username; ////Message = HttpContext. //ClaimsPrincipal claimsPrincipal = HttpContext.User; //// Get the claims values ////var name = identity.Claims.Where(c => c.Type == ClaimTypes.Name) //// .Select(c => c.Value).SingleOrDefault(); ////var sid = identity.Claims.Where(c => c.Type == ClaimTypes.Sid) //// .Select(c => c.Value).SingleOrDefault(); //var userid = claimsPrincipal.Claims.Where(c => c.Type == "urn:microsoftaccount:id") // .Select(c => c.Value).SingleOrDefault(); //var username = claimsPrincipal.Claims.Where(c => c.Type == "urn:microsoftaccount:name") // .Select(c => c.Value).SingleOrDefault(); //Message = "[" + userid + " " + username + "]"; } } }
- Access Claim values in controller in MVC 5 – การ query จาก claim (แต่ไม่ได้ใช้ละ)
ทีนี้พอ publish ขึ้นไปใหม่ ก็จะเห็น userid
และชื่อ ที่หน้า about ละ
https://jackwebapp.azurewebsites.net/About