Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı sıra, bizlerin farklı yönetimsel masraflardan da olabildiğince kaçınabilmesini sağlamaktadır.
Günümüzde bir çoğumuz cloud-native uygulamalar geliştiriyor veya cloud’a migration’lar gerçekleştiriyor. Bu süreçlerde ise çeşitli masrafları minimize edebilmek için cloud sağlayıcıların farklı PaaS, SaaS, Serverless gibi çözümlerinden de olabildiğince yararlanmaya çalışıyoruz. Azure Active Directory B2C ise consumer identity ve access management konusunda yararlanabileceğimiz cloud çözümlerinden bir tanesi. Özellikle customer-facing bir uygulama geliştiriyorsak, güvenli ve ölçeklenebilir bir consumer identity management özelliğine sahip olmak oldukça önem arz etmektedir.
.NET dünyasında bu tarz ihtiyaçlar için genellikle Identity Server configure edilerek kullanılmaktadır. Elbette diğer servis’lerde de olduğu gibi identity servis’inin de güvenliğini, ölçeklenebilirliğini ve hosting’ini yönetiyor olmamız gerekmektedir. Öte yandan alternatif olarak Azure AD B2C kullanarak bu servis’in güvenliğini, ölçeklenebilirliğini, GDPR gibi veri koruma yönetmeliklerine uyumluluğunu ve benzeri karmaşıklıkları bizim yerimize Azure‘un üstlenmesini sağlayabilir ve doğrudan kendi core business’ımıza odaklana da biliriz.
Azure‘un sunmuş olduğu bu servis, consumer identity ve access management konusunda bizlere bir çok fonksiyonalite ve esneklikler sunmaktadır.
Şimdi daha konuyu iyi anlayabilmek adına, Azure AD B2C ile basit bir örnek gerçekleştirelim.
Azure AD B2C kullanabilmemiz için öncelikle bir tenant oluşturmamız gerekmektedir. Tenant’ı bir organization’a ait olan tüm kullanıcı bilgilerini, uygulama ve API kayıtlarını ve policy gibi çeşitli resource’larını barındıran isolated bir container olarak düşünebiliriz.
Öncelikle bir tenant oluşturabilmek için buradaki adımları takip edelim. Ben “MyTodo” adında bir organization oluşturdum. Tenant’ı oluşturduktan sonra ise kullanmaya başlayabilmek için ilgili tenant’ı içeren directory’e geçiş yapmamız gerekmektedir. Bunun için Azure portal üzerinden “Directories + subscriptions” bölümüne gidelim ve oluşturmuş olduğumuz directory’e geçiş işlemini gerçekleştirelim.
Geçiş işlemini gerçekleştirdikten sonra artık identity access işlemleri için Azure AD B2C‘yi configure etmeye başlayabiliriz.
Azure AD B2C içerisinde consumer identity ve access management işlemleri için kullanıcıların takip etmeleri gereken çeşitli business logic’ler tanımlayabilmekteyiz. Bu işlemleri ise iki farklı şekilde gerçekleştirebilmekteyiz. Eğer kompleks bir sürecimiz yoksa, örneğin REST çağrıları yapmayı gerektiren işlemler gibi, hali hazırda sunulmuş olan user flow’ları hızlıca kullanmaya başlayabiliriz. Eğer kompleks bir sürece sahipsek ve policy-driven bir yaklaşıma ihtiyacımız varsa, ozaman kendi custom XML-based policy’lerimizi tanımlamamız gerekmektedir.
Bu makale kapsamında ise predefined sunulan user flow’ları nasıl kullanabileceğimize bir bakacağız. Predefined sunulan user flow’lar ile consumer identity ve access management adına ihtiyacımız olacak olan bir çok işemleri kolaylıkla ve hızlı bir şekilde gerçekleştirebilmekteyiz.
Bu işlemlerin bazıları;
Gördüğümüz gibi ihtiyacımız olabilecek standart bir çok özellik user flow’lar ile esnek bir şekilde bizlere sağlanmaktadır.
Şimdi hızlıca ilk user flow’u tanımlayalım. Bunun için oluşturmuş olduğumuz Azure AD B2C instance’ına gidelim ve “Policies” menüsü altında bulunan “User flows” a tıklayalım. Ardından “New user flow” a tıklayarak “Sign up and sign in” seçeneğini seçelim. İsminden de anlaşılabileceği üzere kullanıcıların sistemimize üye olup, giriş yapabilecekleri flow’u configure edeceğiz. “Version” olarak ise recommended olanı seçelim ve flow’u aşağıdaki gibi configure edelim. Ardından oluşturacak olduğumuz flow’un ismini, daha sonra kullanmak üzere not alalım.
Burada gördüğümüz gibi MFA‘ı etkinleştirebilmekte ve kullanıcıdan almak istediğimiz bilgileri ve token içerisinde claim olarak saklamak istediğimiz bilgileri configure edebilmekteyiz. Ayrıca flow oluşturulduktan sonra da farklı configuration’lar gerçekleştirebilmekteyiz.
Customer-facing bir Web App geliştireceğimizi düşünelim ve geliştirme sürecini basitleştirebilmek ve hızlandırabilmek adına identity access ve management işlemlerini Azure AD B2C‘ye delege etmek istediğimizi varsayalım. Ayrıca bu Web App‘in arka planda bir Web API‘ı da güvenli bir şekilde consume etmesini istediğimizi düşünelim.
İlk olarak aşağıdaki komut satırı ile MyTodoOrgWeb adında bir .NET 7 Web App projesi oluşturalım.
dotnet new webapp -n MyTodoOrgWeb --auth IndividualB2C
Burada “auth” opsiyonunu “IndividualB2C” olarak belirttiğimiz için, .NET CLI Azure AD B2C‘yi kullanabilmemiz için gerekli olan template’i “OpenID Connect” configuration’ı ile birlikte bizim için otomatik olarak oluşturmaktadır.
Şimdi bu projeyi configure etmeden önce, ilk olarak onu oluşturmuş olduğumuz tenant içerisinde tanımlamamız gerekmektedir. Böylece authentication işlemlerini Azure AD B2C ile gerçekleştirebileceğiz. Bunun için Azure AD B2C instance’ına gidelim ve sol menüden “App registration” sekmesini seçelim. Ardından “New registration” a tıklayarak aşağıdaki gibi tanımlama işlemini yapalım.
İsim olarak buraya “MyTodoOrgWeb” verelim ve “Supported account types” seçeneğini de sonuncu olan seçeneği seçelim. “Redirect URI” olarak ise .NET CLI ile oluşturmuş olduğumuz örnek proje template’i içerisinde de default olarak gelen, “signin-oidc” callback adresini belirtelim.
App registration işlemini tamamladıktan sonra ise “Authentication” tab’ına gidelim ve OpenID Connect ile sign in işlemini hızlı bir şekilde gerçekleştirebilmek için şimdilik implicit grant flow’u “Access tokens” ve “ID tokens” seçeneklerini seçerek etkinleştirelim. Daha sonra bir Web API çağırma aşamasına geçtiğimizde ise implicit grant flow’u kapatarak, daha secure olabilmesi açısından authorization code flow‘u etkinleştiriyor olacağız. Çünkü implicit grant flow’da ilgili token’lar redirect’ler sırasında URL ‘lerin query parametreleri içerisinde iletildiği için, kolaylıkla intercept edilebilme riskleri bulunmaktadır.
Ardından “Certificates & secrets” tab’ına giderek şimdiden bir client secret tanımlayalım. Bu secret “MyTodoOrgWeb” ‘in bir sonraki aşamada oluşturacak olduğumuz Web API ‘ı güvenli bir şekilde consume edebilmesi için kullanılacak.
Şimdi ufak bir test yapabiliriz.
Öncelikle “MyTodoOrgWeb” uygulamasının “appsettings.json” dosyasını aşağıdaki gibi oluşturmuş olduğumuz bilgiler doğrultusunda güncelleyelim.
{ "AzureAdB2C": { "Instance": "https://YOUR_ORGANIZATION.b2clogin.com/", "ClientId": "CLIENT_ID", "CallbackPath": "/signin-oidc", "Domain": "YOUR_ORGANIZATION.onmicrosoft.com", "SignedOutCallbackPath": "/signout/B2C_1_CreateUserFlow", "SignUpSignInPolicyId": "B2C_1_CreateUserFlow", "ClientSecret": "YOUR_CLIENT_SECRET" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
Ardından “MyTodoOrgWeb” uygulamasını çalıştıralım ve “Sign in” butonuna basalım.
Bu noktada bizi yönlendirmiş olduğu “Sign up and sign in” user flow unun URL ‘i içerisine bakarsak, “response_type=id_token” query parametresinin set edildiğini görebiliriz. Yani burada sign in işlemi sonucunda bir “id_token” elde ediyor olacağız.
Gördüğümüz .NET CLI ile oluşturmuş olduğumuz Web App template’i ve Azure AD B2C user flow’ları sayesinde kolay ve hızlı bir şekilde sign up ve sign in işlemlerini gerçekleştirebilen bir yapıya sahip olduk.
Şimdi ise bu Web App üzerinden Azure AD B2C ile güvenli bir hale getirilmiş (getireceğimiz) bir Web API ‘a nasıl erişim sağlayabiliriz bir ona bakalım.
Yine .NET CLI ‘ı kullanarak “MyTodoOrgAPI” adında örnek bir .NET 7 Web API projesi oluşturalım.
dotnet new webapi -n MyTodoOrgAPI --auth IndividualB2C
.NET CLI bize yine authentication ve authorization işlemleri için Azure AD B2C kullanan bir API template’i oluşturacaktır.
Ardından daha önce yaptığımız gibi projenin configuration işlemine başlamadan önce tenant içerisinde tanımlama işlemini gerçekleştirelim.
Bu sefer “Supported account types” olarak “Accounts in this organizational directory only…” seçeneğini seçelim.
App registration işlemini tamamladıktan sonra ise şimdi bu API üzerinde fine-grained bir access kontrole sahip olabilmemiz için, scope’lardan yararlanacağız. Scope’lar sayesinde bir API ‘ın farklı endpoint’lerine veya fonksiyonalitelerini olan erişimi, client’lar bazında sınırlayabilmekteyiz.
Bunun için öncelikle “Expose an API” tab’ına geçelim ve scope’lar için prefix olarak kullanılacak olan “Application ID URI” bilgisini düzenleyelim. Ben “mytodoorgapi” değerini verdim. Ardından “Add a scope” butonuna basalım ve aşağıdaki gibi “read” adında örnek bir scope ekleyelim.
Kısaca bu API ‘ı kullanıcı adına çağıracak olan client’ın, “read” scope’una sahip olması beklenecektir.
Dolayısıyla “MyTodoOrgWeb” ‘in “MyTodoOrgAPI” ‘ına erişim sağlayabilmesi için, tanımlamış olduğumuz bu scope’a erişim iznini vermemiz gerekmektedir. Bunun için Azure AD B2C üzerinden “MyTodoOrgWeb” uygulamasına gidelim ve “API permissions” tab’ına geçelim. Ardından “Add permission” butonuna tıklayalım ve “APIs my organization uses” sekmesi altından tanımlamış olduğumuz API ‘ı bularak “read” scope’unu seçelim.
“Read” scope’u için erişim iznini ekledikten sonra ise, son olarak “Admin consent” ‘i vermemiz gerekmektedir. Kısacası “MyTodoOrgWeb” ‘in “MyTodoOrgAPI” ‘ına doğrudan erişebilmesi için, “Admin consent” ‘i verme işlemi gerçekleştirmemiz gerekmektedir.
Bu noktadan itibaren Azure AD B2C üzerindeki configuration işlemlerini her iki uygulama içinde tamamlamış olduk.
Şimdi ilk olarak “MyTodoOrgAPI” ‘ın kod tarafındaki düzenlemelerini gerçekleştirelim. Template ile default gelen “WeatherForecast” örneğini olduğu gibi kullanalım. Sadece controller içerisindeki “Route” ve “RequiredScope” attribute’lerini explicit bir şekilde aşağıdaki gibi güncelleyelim.
[Route("weatherforecasts")] [RequiredScope("read")]
Böylece “read” scope’una sahip olan bir client, bu controller altındaki endpoint’lere erişim sağlayabilecektir. “Program.cs” içerisine baktığımızda da zaten API ‘ın authentication middleware’i ile hazır bir şekilde geldiğini görebiliriz.
Ardından API ‘ın da “appsettings.json” configuration dosyasını daha önce oluşturmuş olduğumuz bilgiler doğrultusunda güncelleyelim.
{ "AzureAdB2C": { "Instance": "https://YOUR_ORGANIZATION.b2clogin.com/", "ClientId": "CLIENT_ID", "Domain": "YOUR_ORGANIZATION.onmicrosoft.com", "Scopes": "read", "SignUpSignInPolicyId": "B2C_1_CreateUserFlow" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
Artık “MyTodoOrgAPI” ‘da Azure AD B2C authentication kullanımına hazır durumda.
Şimdi ise “MyTodoOrgWeb” tarafındaki kod düzenlemelerini gerçekleştirelim. Öncelikle “MyTodoOrgAPI” template’i ile hazır gelen “WeatherForecast” class’ını, “MyTodoOrgWeb” içerisinde de olduğu gibi tanımlayalım. Ardından “MyTodoOrgWeb” ‘in “Pages” klasörü altındaki “Index.cshtml” dosyasını aşağıdaki gibi güncelleyelim.
@page @model IndexModel @using Microsoft.Identity.Web @using System.Net.Http.Headers @inject ITokenAcquisition _tokenAcquisition @inject IHttpClientFactory _clientFactory @{ ViewData["Title"] = "Home page"; async Task<List<WeatherForecast>> GetWeatherForecastAsync() { var requiredScopeForMyTodoOrgAPI = "https://mytodoorganization.onmicrosoft.com/mytodoorgapi/read"; var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes: new[] { requiredScopeForMyTodoOrgAPI }, user: User); string baseAddress = "http://localhost:5076"; string route = "weatherforecasts"; var client = _clientFactory.CreateClient(); client.BaseAddress = new Uri(baseAddress); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await client.GetAsync(route); if (response.IsSuccessStatusCode) { return await response.Content.ReadFromJsonAsync<List<WeatherForecast>>(); } throw new Exception(response.ReasonPhrase); } } @if (User.Identity?.IsAuthenticated == true) { List<WeatherForecast> weatherForecasts = await GetWeatherForecastAsync(); <ul> @foreach (var weatherForecast in weatherForecasts) { <li>Date: @weatherForecast.Date.ToShortDateString()</li> <li>Summary: @weatherForecast.Summary</li> <li>Temperature: @weatherForecast.TemperatureC</li> } </ul> }
Burada “GetWeatherForecastAsync” method’u içerisinde “MyTodoOrgAPI” ‘ını secure bir şekilde sign-in olmuş kullanıcı adına çağırabilme işlemini gerçekleştirebilmek için, “Microsoft.Identity.Web” authentication library’si ile gelen “ITokenAcquisition” servis’ini kullanıyoruz. Bu servis bizim için access token alma işlemini otomatik olarak gerçekleştirmektedir.
Bu servis ayrıca uygulamanın performansını arttırabilmek adına ilgili access token’ın cache yönetimini ve ayrıca kullanıcı session’ının geçerli kalabilmesi için ilgili access token’ın yenilenmesi gibi işlemleri de gerçekleştirmektedir.
NOT: “baseAddress” variable’ını kendi local ortamınızdaki “MyTodoOrgAPI” ‘ının adresi ile değiştirmeyi unutmayın.
Şimdi “MyTodoOrgWeb” içerisindeki “Program.cs” dosyasına gidelim ve “ITokenAcquisition” servis’inin etkinleştirme işlemini “EnableTokenAcquisitionToCallDownstreamApi” method’unu “AddMicrosoftIdentityWebApp” satırından sonra dahil ederek gerçekleştirelim. Ayrıca örnek olması açısından in-memory token cache özelliğini de aktif edelim. Production ortamları için distributed cache özelliğini kullanabiliriz.
// Add services to the container. builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection(Constants.AzureAdB2C)) .EnableTokenAcquisitionToCallDownstreamApi() .AddDistributedTokenCaches();
“ITokenAcquisition” servis’i default olarak authorization code flow ‘u Web App tarafında etkin hale getirmektedir. Elbette bu servis’i kullanmadan da authorization code flow’u “MyTodoOrgWeb” tarafında etkin hale getirebilirdik. Biz bir önceki aşamada henüz bir API ‘a sahip olmadığımız için ve sign in işlemlerini kolay bir şekilde gerçekleştirebilmek adına implicit grant flow’u kullandık. Aşağıdaki OpenID Connect configuration’ı ile de Web App tarafında authorization code flow’u etkinleştirebilirdik.
services.Configure(OpenIdConnectDefaults.AuthenticationScheme, options => { options.ResponseType = "code"; });
Dolayısıyla AzureAD B2C üzerinden “MyTodoOrgWeb” uygulaması için etkinleştirmiş olduğumuz implicit flow’u, aşağıdaki gibi her iki seçeneği de disable ederek, authorization code flow olarak değiştirebiliriz.
Artık sign in işlemleri sırasında Web App tarafında bir authrozaiton code elde ediliyor ve bu kod backend tarafında “ITokenAcquisition” servis’i vasıtasıyla bir access token elde edebilmek için kullanılıyor olacak.
Şimdi her iki uygulamayı da çalıştıralım ve “MyTodoOrgWeb” ‘in tekrardan sign in sayfasına gelelim. Yönlendirildiğimiz user flow’un URL ‘inde ise bu sefer “response_type” ‘ın “code” olarak set edildiğini görebiliriz.
Sign in işleminin sonrasında ise “MyTodoOrgWeb” ‘in authenticate olmuş kullanıcı adına geçerli bir access token alarak, “MyTodoOrgAPI” ‘ını başarılı bir şekilde consume ettiğini de görebiliriz. Ayrıca “MyTodoOrgAPI” ‘ı tarafında ise ilgili bearer token ve scope’u valide edilerek, ilgili işlem gerçekleştirilmektedir.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/enable-authentication-web-api?tabs=csharpclient
https://learn.microsoft.com/en-us/azure/active-directory-b2c/enable-authentication-web-application?tabs=visual-studio
https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow
{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…
{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…
{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…
{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…
{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…
{:tr}Bildiğimiz gibi .NET Conf 2022, 8-10 kasım arasında gerçekleşti. Konferans sırasında ise .NET 7 ve…