Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Keycloak | Asp.NET Core İle Browser Flow Authentication #7

Merhaba,


Bu içeriğimizde Asp.NET Core ile birlikte Keycloak’da Browser Flow Authentication konusunu hem teorik hem de pratik bir şekilde netleştirebileceğimiz bir incelemede bulunuyor olacağız.

Browser Flow Authentication Nedir?

Browser Flow, Keycloak’un tarayıcı tabanlı (interactive) kimlik doğrulama senaryoları için kullandığı authentication flow türüdür. Normalde bir web sitesine girildiğinde görülen login ekranı, MFA ekranı, hatırlatma cookie’si kontrolü vs. hepsi bu flow içerisinde tanımlanmaktadır.

Browser flow mantığında, uygulamalar kendi login ekranlarını tasarlamamakta, oturum açılması gerektiği taktirde uygulamalar kullanıcıları Keycloak’un login sayfasına yönlendirerek gerekli oturum açma işlemlerini orada sağlamaktadır. Böylece kullanıcı şifreleri, OTP değerleri vs. gibi kritik veriler client’lar tarafından erişilemeyecek ve yalnızca Keycloak’un kendi ekranında işlenecektir. Bunların yanında brute-force koruması da sağlanmış olacaktır. Böylece yapısal olarak merkezi bir doğrulama sistemi kurgulanmış, güvenlik açısından da güçlendirici hamleler sağlanmış olacaktır ve bir yandan da uygulamalardan da büyük bir sorumluluk külfeti kaldırılmış olacaktır.

Ayrıca browser flow authentication sayesinde, Tek Oturum Açma (veya Çoklu Oturum Açma) olarak bilinen Single Sign-On (SSO) doğrulama yöntemi de otomatik sağlanmış olacaktır. Single Sign-On ile aynı e-Devlet, Google yahut kurumsal ortamlarda olduğu gibi davranış sergilenecek ve nasıl ki bu ortamlarda oturum açıldığı taktirde, bunlarla ilişkili (yani bunlarla merkezi kimlik kontrolü sağlayan) diğer sistemlere giriş yapıldığında o oturum otomatik tanınacak ve doğrulanmış olacaktır.

Single Sign-On (SSO) nasıl çalışır?
  1. Kullanıcı ilk olarak herhangi bir X uygulamasına girer.
  2. Giriş yapmak için butona tıklar.
  3. X uygulaması, kullanıcıyı merkezi kimlik sağlayıcıya (Keycloak, e-Devlet, Google vs.) yönlendirir.
  4. Kullanıcı bu merkezi noktada şifre, MFA vs. ile bir kez doğrulama gerçekleştirir.
  5. Oturum başarılıysa bir session oluşturulur ve tarayıcıya cookie/token koyulur.
  6. Merkezi kimlik sağlayıcı, X uygulamasına “kullanıcı doğrulandı” bilgisini, yani token’ı verir.
  7. Kullanıcı X uygulamasına yönlendirilir.
  8. Sonra kullanıcı Y uygulamasına girdiğinde bu uygulama merkezi kimlik sağlayıcısına gidecektir.
  9. Identity provider mevcut oturumu görecektir.
  10. Şifre sormadan kullanıcı doğrulanacak ve y uygulamasına da otomatik giriş yapılmış olacaktır.
SSO’nun avantajları ve dezavantajları nelerdir?

Browser flow’un SSO davranışı sayesinde birden fazla uygulama için kullanıcı konforu oldukça yükseltilmiş olacaktır. Neden mi? Çünkü ne kadar uygulama o kadar şifre yönetimi ve hafızası gerektirecektir. Ki illaki bu şifreler unutulacaktır… Ee bu da şifre yorgunluğu anlamına gelen password fatigue durumuna sebebiyet verecektir (Malumunuz özellikle bankaların saçma şifre politikalarından dolayı ikrah etmiş vaziyetteyiz) Merkezi kimlik doğrulayıcısından tek kullanıcı adı ve şifre ile yapılan girişlerin birden fazla uygulamayı kapsaması oldukça değerli bir konfor sağlayacaktır. Ancak bu durumun getirisi olduğu kadar ciddi götürüsü olabilecek bir riski de mevcuttur. O da; tüm sistemlere erişim ihtimalinin, o tek hesabın ele geçirilmesine indirgenerek olası riskin oldukça artmasıdır.

Browser Flow İç Yapısı

Şimdi bizler için oldukça önemli olan browser flow iç yapısını değerlendirmeye başlayalım. Tabi bunu Keycloak inceleme serimizin 5. içeriği olan Keycloak’da Authentication, Identity, Identity Provider İle Birlikte Authentication Flow’ları Teorik Olarak İnceleyelim başlıklı makalemizin “Browser Authentication Flow’u Execution’larına Bölelim” başlıklı bölümünde (ki linke tıklandığında direkt ilgili başlığa gidecektir) detaylıca teorik olarak incelediğimizi hatırlatmak isterim. Bundan kaynaklı ilgili akıştaki execution’ları bir daha tek tek açıklama gereği görmemekteyim.

Peki biz ne yapacağız? diye sorarsanız, Keycloak’dan süreci inceleyip bu başlığı biraz hızlı geçeceğiz. Şimdi bunun için;Keycloak | Asp.NET Core İle Browser Flow Authentication #7Keycloak üzerinden Authentication sekmesine gelip, browser flow’a tıklayalım ve ardından açılan sayfada akışla ilgili tüm süreci/adımları gözlemleyelim.

Keycloak | Asp.NET Core İle Browser Flow Authentication #7

Keycloak’da Browser Flow Authentication adımları…

6 Kritik Soru / 6 Kritik Cevap

Şimdi browser flow ile ilgili kritikleri soru cevap formatında ilerleyerek yapalım. Böylece hem daha verimli bir yaklaşım sergilemiş hem de akıllarda olabilecek soruların cevaplarını da peşinen vermiş olacağız…

Soru 1 | Browser flow ne zaman tetiklenir?

Tarayıcıdan gelen, redirect tabanlı ve etkileşim gerektiren OIDC isteklerinde.

Soru 2 | Browser flow başarısız olursa ne olur?

Keycloak authorization code üretmez ve uygulamada da kullanıcı authenticate edilemez!

Soru 3 | Browser flow token üretir mi?

Hayır. Çünkü, token üretimi flow’dan ayrı bir iştir! Flow, kimlik doğrulama sürecidir ve bu süreç başarılıysa eğer token üretilir. Yani browser flow’dan bağımsız olarak hangi akış olursa olsun dolaylı olarak evet, token üretimini flow tetikleyebilir.

Soru 4 | Aynı realm’de farklı browser flow kullanılabilir mi?

Client bazlı authentication flow override yöntemi ile evet.

Soru 5 | Cookie authenticator neden önemlidir?

Her isteğin login ekranına gitmemesi için Single Sign-On (SSO) sağlar.

Soru 6 | SPA’lar için browser flow tavsiye ediliyor mu?

Evet, hatta tek doğru yol olarak.

Asp.NET Core | Keycloak | Browser Flow Örnek Uygulama

Şimdi Keycloak’daki browser flow’u Asp.NET Core MVC ile güzel bir şekilde örnek üzerinden ele almaya başlayabiliriz. Bunun için bir Asp.NET Core MVC uygulaması oluşturalım ve içerisine aşağıdaki kütüphaneyi yükleyelim.

Bu paket, Asp.NET Core’un Keycloak (yahut başka bir Identity Provider) ile OpenID Connect (OIDC) üzerinden konuşabilmesine yaramaktadır. Yani birazdan süreçte göreceğimiz üzere; tarayıcıyı Keycloak’a yönlendiren, authorization code edinen ve token endpoint’ine gidip token’ı çeken esasında bu kütüphanenin sağladığı işlevsellikler olacaktır.

Hoca ekstradan paket kullanmadan iş yürütemez miyiz?
Hayır, yürütemezsiniz. Bu paket olmaksızın browser flow’u başlatamaz, authorization code’u alamaz, token endpoint’ten token edinemez ve external login yapamazsınız.

Şimdi işin mantığını kavrayak ilerleyelim ve bu çerçevede yapılandırmaları adım adım gerçekleştirelim.

Tabi ilk olarak oluşturduğumuz uygulamayı Keycloak’ta temsil edecek olan client’ı tasarlayalım…Keycloak | Asp.NET Core İle Browser Flow Authentication #7Dikkat ederseniz, application-client adını vermiş olduğumuz bir confidential client oluşturmuş bulunuyoruz. Bu client’ın browser flow’u destekleyebilmesi için özellikle Standard flow‘un işaretlenmesi ve ekran alıntısında vurgulanmış olan url’lerin doğru bir şekilde girilmesi gerekmektedir.

Ardından uygulamanın Program.cs dosyasında aşağıdaki yapılandırmayı sağlayalım:

builder.Services.AddAuthentication(options =>
    {
        //Varsayılan kimlik doğrulama şeması olarak Cookie tabanlı kimlik doğrulama kullanılmaktadır.
        //Yani kullanıcı oturum bilgileri cookie'de saklanır.
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        //Kullanıcı, yetkisiz olarak kimlik doğrulama gerektiren bir sayfaya erişmeye çalıştığında
        //OpenID Connect protokolü kullanılarak kimlik doğrulama işlemi başlatılır ve Keycloak'a yönlendirme sağlanır.
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(options =>
    {
        //Kullanıcı giriş yapmadığında yönlendirileceği sayfa.
        options.LoginPath = "/login";
        //Kullanıcı çıkış yaptığında yönlendirileceği sayfa.
        options.LogoutPath = "/logout";
    })
    //OpenID Connect protokolü kullanılarak Keycloak ile entegrasyon sağlanılmaktadır.
    .AddOpenIdConnect(options =>
    {
        options.Authority = "http://127.0.0.1:8080/realms/master";
        options.ClientId = "application-client";
        options.ClientSecret = "xgVgEbqi5jxpZ0QvQDSVQMIlmsw2gbZv";
        options.ResponseType = "code";

        options.SaveTokens = true;
        options.GetClaimsFromUserInfoEndpoint = true;
        options.RequireHttpsMetadata = false;

        options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("email");

        options.CallbackPath = "/signin-oidc";

        options.TokenValidationParameters = new()
        {
            NameClaimType = "preferred_username",
            RoleClaimType = "roles"
        };
    });

Burada yapılan çalışmayı izah etmemiz gerekirse eğer;

  • 5 – 8. satır aralığı; Burada Asp.NET Core’un bu request sürecinde hangi authentication mekanizmasını kullanacağını ifade ediyoruz. Dikkat ederseniz, iki yapılandırma da farklı zamanlarda, farklı amaçlarla kullanılmaktadırlar.
    1️⃣DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme
    Bu şema yapılandırması, gelen request’te kullanıcı authenticate mi diye kontrol edilecekse nereden edileceğini belirtmektedir. Cookie’den 🙂

    Challenge nedir? Challenge, kimlik doğrulama sürecinde -kullanıcı doğrulanmamış, şimdi onu login olmaya yönlendiriyorum- anlamına gelen bir mekanizmadır. Asp.NET Core mimarisi, bir kullanıcı herhangi bir korumalı kaynağa sistemde onun kimliğini doğrulayacak geçerli bir bilgi (cookie veya token) olmadan erişmeye çalışırsa, bu isteği reddetmek yerine Challenge’ı tetikler ve önceden tanımlanmış Challenge Scheme üzerinden login sürecini başlatır. SPA’larda da Challenge mantığı mevcuttur.

    Ne zaman devreye girer?
    [Authorize] attribute’u çalıştığında ve HttpContext.User doldurulurken…

    2️⃣DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme

    Bu şema yapılandırması ise kullanıcının authenticate olmadığı durumda, onu nasıl login’e yönlendireceğinin cevabını verir. Burada verilen cevap ‘OpenIdConnect‘ şeklindedir.

    Ne zaman devreye girer?
    [Authorize] attribute’u çalıştığında ve kullanıcının authenticated olmadığı anlaşılınca.

    Bu yapılandırma sayesinde Asp.NET Core kullanıcıyı Keycloak login’e yönlendirecektir. Bu faaliyete Challenge denmektedir!
     
     

    Authorization Code nedir? Authorization Code, -kullanıcının başarıyla kimliği doğrulandı- bilgisini temsil eden kısa ömürlü, tek kullanımlı, geçici bir koddur.

    Bizler; kullanıcı yetkisini ifade eden gerçek token’ı tarayıcıya vermek veya url’de taşımak istemediğimiz için token yerine değersiz ve bir anlamı olmayan Authorization Code’u kullanıyoruz. Bundan kaynaklı Authorization Code;
    →Token değildir!
    →Yetki vermemektedir!
    →API çağrısında kullanılmamaktadır!

    Yalnızca token almak için kullanılan özel bir koddur. Bu kod ile uygulama, arka planda Keycloak’dan gerçek token’ı elde edebilmektedir.

    Challenge, Asp.NET Core’un şunu demesidir; şimdi bu kullanıcıyı doğrulanabileceği bir yere yönlendiriyorum…

  • 23. satır; options.ResponseType = "code" Bu yapılandırmayla login’in başarılı olması durumunda Keycloak’un bize ne döndüreceğini yapılandırmış oluyoruz.

    code diyerek Authorization Code Flow‘u ifade etmiş oluyoruz.

    code, bir token talebi değil, token’a giden güvenli biletin(authorization code) ta kendisidir!

  • 25. satır;options.SaveTokens = true OIDC ile alınan token’ların (access, id, refresh) Asp.NET Core authentication ticket’ına kaydedilmesini sağlar. Yani, normal şartlarda token alındığı taktirde herhangi bir yerde saklanmamaktadır. Ancak SaveTokens = true dendiğinde token’lar, o anki login işlemiyle ilgili ek bilgileri barındıran AuthenticationProperties içerisine eklenecektir ve Cookie’ye şifreli şekilde gömülecektir. Böylece bizler request sürecinde istediğimiz noktada HttpContext.GetTokenAsync("access_token") şeklinde token’a erişim gösterebiliyor olacağız.

    SaveTokens = true, -aldığım token’ları sakla ki sonra kullanabileyim- demektir.

    Burada AuthenticationProperties‘i claim’ler yahut User.Identity ile karıştırmamak lazımdır. Bu, sadece authentication ticket’ın bir parçasıdır…

  • 26. satır;options.GetClaimsFromUserInfoEndpoint = true Bu, kullanıcı oturumu başarıyla açtıktan yani doğrulandıktan sonra Asp.NET Core uygulamasına Keycloak’un /userinfo endpoint’ine ekstra bir çağrı yapmasını ve kullanıcı bilgilerini oradan almasını söyleyen bir yapılandırmadır.
  • 29 – 32. satır aralığı; Login olurken belirtilen kapsamlar (scopes) dahilinde bilgi istendiği ifade edilmektedir. Normalde, varsayılan olarak AddOpenIdConnect yapılandırmasında openid ve profile scope’ları otomatik eklenmektedir.
  • 34. satır;options.CallbackPath = "/signin-oidc" Keycloak’un login bittiği taktirde tarayıcıyı geri döndüreceği callback adresini ifade etmektedir. Bu adres üzerinden üretilen token eşliğinde kullanıcı uygulamaya geri döndürülmüş olur.

Evet, olması gereken altyapıyı artık yapılandırdığımızı söyleyebilirim. Bundan sonrası hafif ince işçilik. Haliyle gelin şimdi login ve logout action’larını halledelim:

    public class AccountController : Controller
    {
        [HttpGet("/login")]
        public IActionResult Login(string returnUrl = "/")
            => Challenge(new AuthenticationProperties { RedirectUri = returnUrl }, OpenIdConnectDefaults.AuthenticationScheme);

        [Authorize]
        [HttpGet("/logout")]
        public IActionResult Logout()
        {
            return SignOut(new AuthenticationProperties { RedirectUri = "/" }, CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme);
        }
    }

Uygulamada login ve logout talepleri olduğu taktirde bu action’lar tetiklenecektir. Burada dikkat ederseniz AuthenticationProperties yapılandırması eşliğinde Challenge ve SignOut metotlarından istifade edilmektedir.

Challenge metodu, [Authorize] olan bir endpoint’e login olmamış kullanıcıdan gelen request neticesinde otomatik tetikleneceği gibi, buradaki örnekte olduğu gibi manuel de tetiklenebilmektedir. Bu tetikleme gerçekleştiği taktirde OpenIdConnect şemasındaki yapılandırmalar devreye girecek ve kullanıcı Keycloak’a login sürecinin başlaması için yönlendirilecektir.

SignOut metodu ise -mevcut kimliği artık geçersiz say- demektir. Yani bu metot ile Asp.NET Core cookie’si silinecek ve kullanıcı uygulamadan çıkarılacaktır. Ama unutulmamalıdır ki, SSO açık kalacaktır! Burada, Program.cs‘de yapılandırılmış her iki şemanın özellikle belirtildiğini görüyoruz. Bunun nedeni, hem Asp.NET Core’da sadece uygulamayı ilgilendirecek şekilde iç logout’u gerçekleştirmek hem de Keycloak’a kullanıcıyla ilgili logout olunduğunun bilgisini vermektir. OpenIdConnectDefaults.AuthenticationScheme, bu ikinci eylemi gerçekleştirecektir ve Keycloak’ın /protocol/openid-connect/logout endpoint’ine giderek, ilgili kullanıcının session’ını düşürecektir. Evet, bu eylem sonunda kullanıcının aktif session’ları listesinin güncellendiği gözlemlenebilecektir. Haliyle burada her iki şemanın da verilmesine kesinlikle özen gösterilmelidir.

Sadece Cookie verirsen

Uygulamadan çıkılır, Keycloak hala login kalır. Kullanıcı uygulamaya geldiği an şifresiz otomatik giriş sağlanır.

Sadece OIDC verirsen

Keycloak logout olur ama uygulama cookie’si tutulur. Haliyle uygulama hala login olarak yoluna devam eder.

Evet, artık uygulamayı test edebiliriz. Tabi bunun için aşağıdaki gibi iki adet action tasarlamamız yeterli olacaktır:

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            ViewBag.Title = "Index";
            return View();
        }

        [Authorize]
        public IActionResult Detail()
        {
            ViewBag.Title = "Detail";
            return View();
        }
    }

Görüldüğü üzere bu action’lardan birine direkt erişim sağlanabilirken bir diğeri ise yetkilendirme beklemektedir.

Son olarak Program.cs dosyasına aşağıdaki middleware’lerini ekleyelim ve uygulamayı derleyip, ayağa kaldıralım…

app.UseAuthentication();
app.UseAuthorization();

Keycloak | Asp.NET Core İle Browser Flow Authentication #7

Login Ekranını Özelleştirme

Keycloak’ta önceden tasarlanmış hazır temalar mevcuttur. Bunlar;

keycloak/
└── themes/
└── base/
└── keycloak/
└── keycloak.v2/

temalarıdır. Her şeyden önce özelleştirme niyetiyle base ve keycloak temalarının asla değiştirilmemesini önermekteyim. Eğer varsa yeni bir tasarıma ihtiyaç, bunun sıfırdan bir tema tasarlayarak giderilmesi en doğrusu olacaktır.

Nasıl mı?
Keycloak’da login ekranları FreeMarker (.ftl) template’leri ile üretilmektedir. Haliyle bu davranışta bir tema oluşturulması yeterli olacaktır. Bunun için aşağıdaki gibi bir klasör dizinine ve dosya yapılanmasına ihtiyaç vardır;

themes/
└── my-login-theme/
└── login/
├── login.ftl
├── login-reset-password.ftl
├── login-otp.ftl
├── theme.properties
└── resources/
├── css/
│ └── style.css
└── img/

Burada ben sizler için güzel bir template tasarlamış bulunuyorum.Keycloak | Asp.NET Core İle Browser Flow Authentication #7Evet… Görüldüğü üzere egzotik havasıyla, kayan yıldızlar eşliğinde gayet ferah ve estetik bir tasarım ortaya koymuş bulunuyorum (tasarım hafif bi i*nems* olmadı değil de neyse) Bu template’i indirebilmek için içeriğimizin sonundaki GitHub reposuna gitmeniz yeterli olacaktır. Ha yazıyı bölmeden direkt indirmek istiyorsanız buradaki linkten erişebilirsiniz.

Peki bu template’i Keycloak’a nasıl yükleyeceğiz?
Keycloak’da template’ler /opt/keycloak/themes/ dizininde tutulmaktadırlar. Dolayısıyla ana klasörü bu dizine atıp, Keycloak Admin Dashboard’undan da
Realm settingsThemesLogin theme kombinasyonunu takip ederek ilgili temayı seçerseniz hem yüklemiş hem de flow sürecinde tasarımı aktifleştirmiş olacaksınız.

Docker container’da ayağa kaldırılan Keycloak’a custom template’i yükleyebilmek için aşağıdaki talimatı kullanabilirsiniz.

docker cp {keycloak_custom_template_path} {keycloak_container_name}:/opt/keycloak/themes/keycloak-custom-login-theme

Keycloak | Asp.NET Core İle Browser Flow Authentication #7

SPA (Angular) + Asp.NET Core Web API + Keycloak Browser Flow (OTP Dahil)

Evet, artık içeriğimizde pratiksel olarak son dokunuşu en çok merak edilen ve kritik olan bir noktaya temas ederek gerçekleştirelim istiyorum. SPA (Single Page Application) kullanan projelerde browser flow tam olarak nasıl gerçekleştiriliyor? Bu başlık altında bu soruya net bir cevap vermeye ve hatta basit ama açıklayıcı bir örnek çalışmayla konuyu pekiştirerek cevabı taçlandırmaya çalışacağız.

Cookie mi, JWT mi?
Öncelikle şunu bilmekte fayda vardır ki; SPA, yapısı gereği cookie’ye pek de uygun değildir. Neden mi? Çünkü; HttpOnly cookie, JavaScript ile okunamamaktadır. Ee bu güvenli bir durum değil mi? diye sorarsanız, evet, belki güvenlidir ancak SPA tamamen JavaScript temelli olduğu için bu durumda token’ı cookie’den okuyup kullanamayacaktır.

Ayrıca SPA yapısal olarak aynı backend’i mobil gibi, masaüstü ya da IoT gibi farklı uygulamalarla paylaştığından dolayı, haliyle cookie, sadece browser’a özgü bir yapıya sahiptir. Oysaki JWT her yerde kullanılabilmektedir.

Bundan kaynaklı bizler SPA ile çalışırken genellikle JWT’yi tercih etmekte ve ona göre davranışımızı şekillendirmekteyiz. Haliyle buradaki çalışmada da JWT ile browser flow’a eşlik edeceğiz.

Şimdi bir Angular projesi eşliğinde, Asp.NET Core Web API projesi oluşturalım ve yapacağımız çalışmaları aşağıda gibi adım adım gerçekleştirelim.

  • Adım 1 (Keycloak’da SPA Client’ı Oluşturma)
    Her şeyden önce SPA uygulamasını Keycloak’da temsil edecek bir client oluşturarak işe başlamamız gerekmektedir. Bunun için angular-client adında ve aşağıdaki özelliklerde bir client oluşturalım.

    Standard Flow Enabled: ✅
    Implicit Flow: ❌
    Direct Access Grants: ❌
    Client Authentication: ❌ (KAPALI) – (Client authentication, client secret değerini gerektireceği ve SPA’da da bu değer güvenli bir şekilde tutulamayacağı (tutulsa da gizlenemeyeceği) için SPA’da kullanılmamaktadır.)
    Redirect URI: http://localhost:4200/*
    Web Origins: http://localhost:4200

    Ee hoca, signin-oidc’yi tanımlamayacak mıyız?
    signin-oidc, Asp.NET Core OpenIdConnect middleware’i tarafından otomatik oluşturulmaktadır ve server-side web app, cookie authentication ve AddOpenIdConnect senaryolarında kullanılmaktadır. SPA’da ise signin-oidc diye bir endpoint mevcut olmayacağı için callback, root route’a ya da herhangi bir client-side route’a yönlendirilebilir.

  • Adım 2 (Keycloak’da Backend Client’ı Oluşturma)
    Şimdi de Asp.NET Core Web API uygulaması için angular-api-client adında Keycloak’da bir client oluşturalım. Bu client, SPA’dan gelen token’ı doğrulayıp onu besleyen backend görevi göreceğinden tüm flow’ları kapalı olacaktır ve root url’i felan da olmayacaktır.
  • Adım 3 (Browser Flow’u Kopyalama ve SPA’da Uygun Hale Getirme)
    SPA ile Asp.NET Core senaryosu için mevcut browser flow’u spa_browser adında kopyalayıp içeriğini aşağıdaki gibi özelleştirelim.

    Browser
    ├── Cookie (ALTERNATIVE)
    ├── Identity Provider Redirector (ALTERNATIVE)
    ├── Username Password Form (REQUIRED)
    └── Conditional OTP (REQUIRED)
    ├── Condition – User Configured (REQUIRED)
    └── OTP Form (REQUIRED)

    Evet, böylece bir SPA için oldukça ideal flow tasarlamış bulunuyoruz. Ardından bu flow’u bind ederek, Keycloak’da aktifleştirelim.

  • Adım 4 (Angular’da Keycloak JS Adapter’ı Yükleme ve Temel Servisi İnşa Etme)
    Keycloak’un SPA’ler için resmi olarak sunduğu keycloak-js JavaScript client kütüphanesini kullanarak login, logout, token alma ve token yenileme işlemlerini Keycloak’a devredeceğiz. Haliyle öncelikle bu paketi uygulamaya yükleyelim;

    keycloak-js

    npm install keycloak-js

    Ardından aşağıdaki servisi oluşturarak Angular ile Keycloak arasındaki köprüyü oluşturak temel iletişim servisini inşa edelim:

    import { Injectable } from '@angular/core';
    import Keycloak from 'keycloak-js';
    
    @Injectable({
      providedIn: 'root',
    })
    export class KeycloakService {
      static keycloak: Keycloak = new Keycloak({
        url: 'http://127.0.0.1:8080/',
        realm: 'master',
        clientId: 'angular-client',
      });
    }
    

    Ardından Angular uygulamasında kullanıcıyı güvenli şekilde doğrulamak ve geçerli bir JWT üretebilmek için app.config.ts dosyasında aşağıdaki yapılandırmada bulunmamız gerekmektedir:

    import { KeycloakService } from './services/keycloak.service.';
    
    export const appConfig: ApplicationConfig = {
      providers: [
        provideBrowserGlobalErrorListeners(),
        provideRouter(routes),
        provideAppInitializer(() => {
          KeycloakService.keycloak.init({
            onLoad: 'login-required',
            pkceMethod: 'S256',
            checkLoginIframe: false
          })
        }),
        provideHttpClient()
      ]
    };
    

    8 ile 12. satır aralığındaki yapılandırma ile, SPA’nın Keycloak üzerinden güvenli şekilde login olup JWT almasını ve uygulamanın ancak bu kimlik doğrulandıktan sonra çalışmasını sağlamaktayız.

    Burada;

    • 1️⃣onLoad: 'login-required' ile login durumunu zorunlu kılmakta ve eğer kullanıcı login değilse hiç bekletmeksizin direkt Keycloak’a yönlendirmenin gerçekleştirilmesi gerektiğini ifade etmekteyiz.

      Böylece uygulama açısından en baştan kimlik garanti altına alınmaktadır.

      Buna alternatif olarak onLoad: 'check-sso' değerini verebilir ve login olunmadığı taktirde sessiz kalınmasını sağlayabiliriz. Özellikle bu ayar public sayfalar için uygundur.

    • 2️⃣pkceMethod: 'S256' ile bu SPA’nın authorization code flow’u PKCE ile kullanacağını açıkça belirtmekte ve PKCE’nin hangi yöntemle hash’leneceğini ifade etmekteyiz.
    • 3️⃣checkLoginIframe: false ile de önemli bir davranışı yapılandırıyoruz. Şöyle ki; Keycloak belirli periyotlarda arka planda bir iframe açmakta ve bunun üzerinden kullanıcının hala login olup olmadığını kontrol etmektedir. Bunu bizler göremesek de browser görebilmektedir. Bizler bu yapılandırma ile bu davranışı yönetebilmekteyiz.

      false olarak ayarlayarak Keycloak’a o gizli iframe işini bırakması ve token süresine bakılacağını ifade etmiş oluyoruz.

    Evet, bu çalışmalar sayesinde Angular uygulamasında /authorize endpoint’ine redirect işlemini gerçekleştirebilmekte, authorization code + PKCE akışını yönetebilmekte, dönen JWT’leri memory’de tutabilmekte, token süresi dolunca sessizce refresh işlemini gerçekleştirebilmekte ve logout’ta /logout‘a yönlendirmeyi sağlayabilmekteyiz.

  • Adım 5 (Token Alma)
    Bir önceki adımda onLoad: 'login-required' yapılandırması ile uygulamada login söz konusu değilse Keycloak’a yönlendirmesi gerektiği şeklinde bir konfigürasyonda bulunmuştuk. Haliyle bu vaziyette uygulamayı derleyip ayağa kaldırırsak direkt Keycloak’a yönlendirildiğimizi göreceksiniz. Herhangi bir kullanıcıyla giriş yapıldığı taktirde artık token alınmış ve login gerçekleştirilmiş olacaktır.

    Tabi burada dikkat edilmesi gereken noktalar vardır. Şöyle ki; elde edilen token keycloak-js tarafından ne localStorage’da, ne sessionStorage’da ne de cookie’de tutulmakta, yalnız SPA’nın JS memory’sinde tutulmaktadır. Ancak giriş yapıldıktan sonra tarayıcının cookie’lerine göz atarsak orada bazı yapılandırmaları görüyor olacağız.Keycloak | Asp.NET Core İle Browser Flow Authentication #7Bu yapılandırmalar token ile alakalı değildir. Bizzat Keycloak server cookie’leridir. Bunların SPA’da da gözükmesinin sebebi kafanızı karıştırmasın… Hem SPA hem de Keycloak localhost’ta ayağa kaldırıldığı için her ne kadar portları değişiyor olsa da bu domain’deki cookie’ler her iki tarafta da karman çorman gözükmektedir.

    Ayrıca Browser Flow Authentication Nedir? başlığında ele aldığımız MVC yöntemindeki gibi token, SPA çalışmasında cookie’e gömülmemektedir.

    Velhasıl…

    Evet, giriş yapıldıktan ve token elde edildikten sonra aşağıdaki gibi ulaşılabilmektedir:

        KeycloakService.keycloak.token
        KeycloakService.keycloak.refreshToken
    
  • Adım 6 (Asp.NET Core’da JWT Doğrulama ve Örnek Authorize İşlemleri)
    Artık Asp.NET Core’da JWT doğrulamayı sağlayabilir ve Authorize edilmiş bir endpoint üzerinden testimizi gerçekleştirebiliriz.

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(options =>
                    {
                        options.Authority = "http://127.0.0.1:8080/realms/master";
    
                        options.Audience = "account";
    
                        options.RequireHttpsMetadata = false;
    
                        options.TokenValidationParameters = new TokenValidationParameters
                        {
                            ValidateIssuer = true,
                            ValidateAudience = true,
                            ValidateLifetime = true,
                            ValidateIssuerSigningKey = true,
                            NameClaimType = JwtRegisteredClaimNames.PreferredUsername
                        };
                    });
    
    builder.Services.AddAuthorization();
    
    builder.Services.AddCors(options => options.AddDefaultPolicy(policy => policy.AllowAnyHeader()
                                                                                 .AllowAnyMethod()
                                                                                 .AllowAnyOrigin()));
    
    
    var app = builder.Build();
    
    app.UseCors();
    
    app.UseAuthentication()
       .UseAuthorization();
    
    app.MapGet("/", () => TypedResults.Ok("Hello World!")).
        RequireAuthorization();
    
    app.Run();
    
  • Adım 7 (Test Etme)
    Evet, sıra tüm yaptığımız çalışmaları test etmeye geldi diyebiliriz. Bunun için aşağıdaki gibi basit bir component tasarlayalım ve testimizi gerçekleştirelim:

    @Component({
      selector: 'app-root',
      imports: [RouterOutlet],
      template: `
      <h1>{{ hi() }}</h1>
    
      <button (click)="getHelloWord()">getHelloWord()</button>
      `
    })
    export class App {
      httpClient: HttpClient = inject(HttpClient);
    
      hi = signal("");
      getHelloWord() {
        this.httpClient.get("https://localhost:7297/", {
          headers: {
            Authorization: `Bearer ${KeycloakService.keycloak.token}`
          }
        }).subscribe((res) => this.hi.set(res.toString()));
      }
    }
    

    Keycloak | Asp.NET Core İle Browser Flow Authentication #7

İşte bu kadar 🙂 Çalıştığınız proje ister basit ister kurumsal ölçekte olsun fark etmeksizin SPA’da Keycloak ile browser flow’u uygulamak sade ve sadece bu adımlardan ibarettir.

Browser Flow’da En Çok Yapılan Hatalar

Son olarak browser flow’da dikkat edilmesi gereken hususlara “hata” orijinli bir bakışla göz atarak içeriğimizi nihayetlendirelim…

  • OTP’yi REQUIRED yapmak
    Keycloak’da browser flow’da -herkes 2FA kullansın ve güvenlik artsın- gerekçesiyle OTP Form REQUIRED olarak ayarlanırsa eğer OTP’si olmayan kullanıcılar login olamayacaktırlar!

    Neden mi?
    OTP, kullanıcının Keycloak’ta tanımlı bir credential’ıdır. Haliyle browser flow’da OTP’nin REQUIRED olması durumunda, eski kullanıcıda OTP yoksa yahut yeni kullanıcıda OTP setup yapılmadıysa bu kullanıcılar sistemin dışında bırakılacaklarından dolayı hiçbir zaman giriş yapamayacaktırlar!

    Peki ne yapılmalı?
    OTP için doğru model CONDITIONAL olarak tasarlanmasıdır. Bu sayede, OTP’si olmayan ilk login sürecinde OTP setup’a yönlendirilecek, yok eğer OTP’si varsa eğer direkt OTP doğrulamaya yönlendirilecektir. Böylece ne eski kullanıcı kilitlenecek, ne de yeni kullanıcı zorla OTP’ye geçirilecektir.

  • Cookie Authenticator’ı silmek
    -Zaten login oluyoruz, cookie’ye ne gerek var?- düşüncesiyle Cookie authenticator’ı silmeye çalışmayınız. Cookie, ilgili tarayıcının daha önce login olup olmadığını kontrol etmektedir. Böylece SSO ve session reuse gibi durumları yürütmektedir.

    Eğer ki, cookie’yi silerseniz aynı tarayıcıda olsa dahi her request’te tekrar tekrar username / password isteyecektir ve SSO davranışı sergilenemeyecektir. Bu da son kullanıcı açısından sürekli redirect loop hissiyatı eşliğinde -niye her refresh’te login istiyor!- isyanı doğuracaktır.

  • Browser flow’u kopyalayıp bağlamamak
    Browser flow’u duplicate edip kendinize göre düzenleyebilirsiniz. Ama unutulmamalıdır ki Keycloak’da default olan akış browser flow’dur. Haliyle sizlerin düzenlediği custom flow bind edilmediği sürece işlevsellik göstermeyecektir. Evet… flow oluşturmak, kullanmak demek değildir. Bind edilmelidir.
  • Client Flow Override’ı unutmak
    Keycloak’ta Authentication Flow’lar realm ve client olmak üzere iki seviyede uygulanırlar. Varsayılan olarak realm seviyesi geçerlidir. Yani aksi söylenmediği sürece ilgili realm üzerinden login olan herkes o realm’deki kabul edilen flow’a göre doğrulanıp, yetkilendirilir.

    Ancak gerçek hayatta şöyle bir ihtiyaç söz konusu olabilmektedir:
    → Web uygulamasında OTP olsun
    → SPA’da OTP olmasın
    → Admin UI’da farklı kurallar olsun…

    Bu durumda realm seviyesinde tek bir browser flow’un olması süreç açısından bir kriz gibi gözükebilir. İşte bu taktirde bizler client seviyesinde flow override gerçekleştirmeliyiz.

    Client flow override ile genel kuralı bozarak client seviyesinde özelleştirme yapabilmekteyiz.Keycloak | Asp.NET Core İle Browser Flow Authentication #7Bunun için yukarıdaki ekran alıntısında görüldüğü üzere Clients → {Client} → Advanced → Authentication flow overrides kombinasyonu takip edilmelidir.

  • Authorization Code ile Direct Grant’i karıştırmak
    Authorization Code ile Direct Grant’i karıştırmak olabilecek en kritik zihinsel hatalardan birisidir. Bu iki farklı davranışın doğasında aşağıdaki mekanizmalar söz konusudur.

    Authorization Code Low’da

    Kullanıcı tarayıcıdan gelir, Keycloak sayfasına yönlendirilir ve orada login olur.

    Bu yüzden şunlar mümkündür :
    → Browser
    → Redirect
    → /authorize
    → /signin-oidc
    → Cookie
    → SSO
    → MFA

    Direct Grant – Resource Owner Password Credential Flow (ROPC)’da ise

    Bir uygulama username & password’ü Keycloak’a REST API ile gönderir ve öyle login sağlar.

    Dolayısıyla burada
    → Browser yok
    → Redirect yok
    → Cookie yok
    → MFA sınırlı
    → ve kullanıcı etkileşimi yoktur!

    Sadece
    → Username / Password
    → API call
    mevcuttur.

    Hata şuradadır ki, -ben zaten token alıyorum, her ikisinde de flow aynı- diye düşünülebilir.

Nihai olarak;
Bu içerik boyunca, günümüzde en yaygın kimlik doğrulama yaklaşımlarından biri olan Keycloak Browser Flow yapısını hem teorik arka planıyla ele aldık hem de pratikte sıkça karşılaşılan server-side web uygulamaları (Asp.NET Core MVC) ve SPA tabanlı istemciler üzerinden gerçek kullanım senaryolarıyla değerlendirdik. Süreç içerisinde Single Sign-On (SSO) mantığını anlamlandırdık ve çoklu istemci barındıran mimarilerde kimlik doğrulama akışları planlanırken nelere dikkat edilmesi gerektiğine özellikle odaklandık. Bununla birlikte, Keycloak’un varsayılan olarak sunduğu login ekranının nasıl özelleştirilebileceğini inceleyerek, hem kullanıcı deneyimi hem de kurumsal ihtiyaçlar açısından nasıl daha esnek çözümler üretilebileceğini de ortaya koymuş olduk.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Repo Adı Buraya
https://github.com/gncyyldz/Keycloak.Examples

Bu repository, ilgili projenin kaynak kodlarını ve mimari yapısını içermektedir. Detaylar için GitHub üzerinden inceleyebilirsiniz.


GitHub’da Görüntüle →

Bunlar da hoşunuza gidebilir...

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir