İçeriğe geç
KURUMSAL Kurumsal teknoloji · İşletmenize özel mühendislik Kurumsal Yazılım · Yönetilen Sunucular · Kurumsal E-posta · LiteSpeed + CloudLinux · Imunify360 Güvenlik · cPanel Yönetim · Uygun pakette 3 Gbps'ye kadar ağ profili
AIOR

SSO Kurulumu: SAML mı OIDC mi? IdP Seçimi, Entegrasyon, Sorun Giderme

  • Konu başlatıcısı Konu başlatıcısı Aior
  • Başlangıç tarihi Başlangıç tarihi

Aior

Administrator

Yetkili üye
Katıldı
2 Nis 2023
Mesajlar
929
Tepki puanı
2
Puan
18
Yaş
41
Konum
Turkey
Web sitesi
aior.com
Konu sahibi

Tek oturum açma (single sign-on, SSO), kullanıcının kimliğini kimlik sağlayıcıda bir kez doğrulatıp ardından birçok uygulamaya, hiçbirine ayrıca parola yazmadan girebilmesidir. Bunun için üç karar vermeniz ve pek çok değeri iki tarafta harfi harfine aynı tutmanız gerekir: Her uygulama hangi protokolü konuşuyor (SAML 2.0 mı, OpenID Connect mi), kullanıcılar hangi kimlik sağlayıcıda (identity provider, IdP) tutulacak ve hesaplar her uygulamada nasıl açılıp kapatılacak? Bu rehberde önce bu kararları ele alıyor, ardından Keycloak ile bir OIDC ve Microsoft Entra ID ile bir SAML entegrasyonunu adım adım kuruyor, sonda da ilk gün karşınıza çıkacak hatalar için bir sorun giderme tablosu veriyoruz. OAuth 2.0 belirteç akışlarını OAuth 2.0 ve OIDC: kimlik doğrulama akışları ve güvenlik kalıpları konusunda ayrıntılı olarak ele aldık; MFA, LDAP ve rol politikasının da kendi alt forumları olduğu için burada her birine yalnızca birer paragraf ayırıyoruz. Menü ve alan adlarını, Keycloak 26.8 ile Microsoft Entra yönetim merkezinin Ekim 2026 itibarıyla İngilizce arayüzünde göründükleri gibi yazıyoruz.

SSO nedir, ne değildir?​

SSO'da kullanıcının IdP'de tek bir doğrulanmış oturumu bulunur. Her uygulama tarayıcıyı IdP'ye yönlendirir, kullanıcı hakkında imzalı bir beyan alır ve kendi yerel oturumunu açar; parola uygulamaya hiç ulaşmaz. Parola senkronizasyonu ya da bütün uygulamaları aynı LDAP dizinine bağlamak ise başka bir şeydir: Kullanıcı aynı parolayı her giriş formuna yazar, her uygulama bu parolayı işler ve içlerinden biri ele geçirildiğinde parola da açığa çıkar.
  • Kimlik sağlayıcı (identity provider, IdP): Kullanıcıyı doğrular (parola, MFA, passkey) ve imzalı beyanlar üretir. Örnekler: Microsoft Entra ID, Okta, Keycloak.
  • Servis sağlayıcı (service provider, SP) ya da bağlı taraf (relying party, RP): IdP'ye güvenen uygulama. SAML buna SP, OIDC ise RP ya da istemci (client) der.
  • Beyan (assertion): SAML'ın imzalı XML belgesi. Özneyi (NameID), öznitelikleri (attribute) ve geçerlilik penceresi ile hedef kitle (audience) gibi koşulları taşır.
  • Kimlik belirteci (ID token): OIDC'nin imzalı JSON Web Token'ı (JWT). Kimin giriş yaptığını, hangi istemci için düzenlendiğini (aud), ne zaman üretilip ne zamana kadar geçerli olduğunu (iat, exp) ve hangi isteğe yanıt verdiğini (nonce) söyler.
  • Oturum (session): En az iki tür oturum vardır; IdP'nin oturum çerezi ve her uygulamanın kendi oturumu. SSO'yu mümkün kılan birincisidir; oturum kapatmayı zorlaştıran da diğerlerinin ayrı tutulmasıdır.
  • Federasyon (federation): IdP ile SP arasında meta veri, uç nokta ve imzalama anahtarı değişimiyle kurulan güven ilişkisi.
  • Kimlik aracılığı (identity brokering): IdP'nin doğrulamayı başka bir IdP'ye devretmesi; örneğin Keycloak'un kullanıcıyı Entra ID'ye ya da Google'a göndermesi. Uygulamalar yine tek bir IdP ile konuşur.

SAML 2.0 ile OpenID Connect karşılaştırması​

KonuSAML 2.0OpenID Connect (OIDC)
BelirteçXML imzalı (XML Signature) beyan, isteğe bağlı XML şifrelemeJWT biçiminde kimlik belirteci (JWS), isteğe bağlı JWE; API'ler için ayrıca erişim belirteci (access token)
Mesaj akışıGenellikle yalnızca tarayıcı üzerinden: AuthnRequest yönlendirmeyle gider, Response kendiliğinden gönderilen bir HTML formuyla Assertion Consumer Service (ACS) adresine POST edilirTarayıcı yönlendirmesi kısa ömürlü bir kod getirir; uygulama bu kodu arka kanaldan (back-channel), belirteç uç noktasında (token endpoint) belirteçlerle değiştirir (gizli istemcilerde istemci doğrulaması, ek olarak PKCE)
Yapılandırma değişimiMeta veri (metadata) XML'i: entityID, uç noktalar, sertifikalar/.well-known/openid-configuration keşif belgesi (discovery document) ve JWKS adresi
Anahtar yenilemeSertifikalar meta veride; birçok SP elle yapıştırılan sertifikayı sabitlerAnahtarlar JWKS'te kid ile yayınlanır; çoğu kütüphane bilinmeyen bir kid görünce anahtarları yeniden çeker
Oturum kapatmaSingle Logout (SLO), çoğunlukla tarayıcı yönlendirme zinciriRP-Initiated, Front-Channel ve Back-Channel Logout belirtimleri
SPA, mobil, APIUygun değil: XML, form POST, API belirteci yokBunlar için tasarlandı (PKCE, erişim belirteci)
Nerede karşılaşırsınızKurumsal SaaS, eski iç uygulamalar, B2B federasyonlarıYeni web uygulamaları, SPA, mobil uygulamalar, API'ler, Kubernetes ve bulut araçları
Sahada işe yarayan karar kuralları:
  • Yeni geliştirdiğiniz bir uygulama: authorization code akışı ve PKCE ile OIDC.
  • Yalnızca SAML sunan bir SaaS ürünü: SAML kullanın; mecbur kalmadıkça önüne protokol çeviren bir proxy koymayın.
  • Karışık bir uygulama envanteri: İki protokolü de konuşan bir IdP seçin. Her uygulama desteklediği protokolü kullanır, kullanıcı yine tek bir giriş ekranı görür.
  • API çağırmak için belirteç gerekiyorsa: OIDC/OAuth 2.0. SAML size oturum açma sağlar, API kimlik bilgisi sağlamaz.
Protokollerden hiçbiri kendiliğinden "daha güvenli" değildir. İmza, issuer (belirteci üreten taraf), audience ve geçerlilik penceresi denetlendiğinde ikisi de güvenlidir; denetlenmediğinde ikisi de kolayca atlatılır. XML imzasını doğru işlemek ise daha zordur: GitLab'da ve omniauth-saml kullanan uygulamalarda SAML girişini sağlayan ruby-saml kütüphanesinde 2024'te (CVE-2024-45409) ve 2025'te (CVE-2025-25291, CVE-2025-25292) kritik kimlik doğrulama atlatma açıkları çıktı. Bakımı süren bir kütüphane kullanın, güncellemelerini takip edin ve XML imza doğrulamasını asla kendiniz yazmayın.

Kimlik sağlayıcı (IdP) seçimi​

İşe kullanıcı hesaplarının bugün nerede tutulduğuna bakarak başlayın: Microsoft 365 kullanan bir şirkette bütün hesaplar zaten Entra ID'dedir, Google Workspace kullanan bir şirkette Google'dadır. Ardından protokol desteğini, provizyonu, politika özelliklerini, barındırma biçimini ve maliyet modelini karşılaştırın:
IdPBarındırmaSSO protokolleriUygulamalara provizyonMaliyet modeli
Microsoft Entra IDBulutSAML, OIDC, WS-FedGaleri ve özel uygulamalar için SCIM 2.0 provizyon hizmetiKullanıcı başı lisans katmanları; Conditional Access ve grup tabanlı uygulama ataması P1 veya üstünü ister
Google Workspace / Cloud IdentityBulutSAML (özel SAML uygulamaları), Google OAuth istemcileriyle OIDCDesteklenen uygulamalar listesindeki uygulamalara otomatik provizyonKullanıcı başı, sürüme (edition) göre değişir
OktaBulutSAML, OIDCLifecycle Management özellikleriyle SCIMKullanıcı ve ürün başı abonelik
KeycloakKendi sunucunuzdaOIDC, SAML; LDAP/AD ve Kerberos federasyonu; kimlik aracılığıÇekirdekte uygulamalara giden SCIM yok (26.8, Keycloak'a provizyon yapılması için desteklenen bir SCIM API'si ekledi)Açık kaynak (Apache 2.0); bedelini operasyonla ödersiniz
authentikKendi sunucunuzdaOIDC, SAML, LDAP, RADIUS, proxySCIM sağlayıcısı: her değişikliği iletir, saatte bir tam eşitleme yaparAçık kaynak çekirdek; Enterprise sürümü kullanıcı başı ücretlendirilir
JumpCloud, Ping Identity, OneLogin, ZITADEL ve Authelia da seçenekler arasında; aynı ölçütler onlar için de geçerli.

Kendi sunucunuzda çalıştırdığınız bir IdP'nin maliyeti kapasiteden değil, operasyondan gelir. IdP sıfırıncı katman (tier-0) bir sistemdir: Çöktüğünde kimse hiçbir uygulamaya giriş yapamaz. Keycloak'un kendi boyutlandırma rehberi, saniyedeki her 15 parolalı giriş için yaklaşık 1 vCPU ve ani yükler için bunun üzerine yüzde 150 pay; bellek için de 10.000 önbelleğe alınmış oturum dâhil pod başına yaklaşık 1.250 MB öngörüyor (ölçümler AWS üzerinde, Argon2 parola özetlemesiyle yapılmış; üretime almadan önce kendi yük testinizi yapın). 1.000 kişilik bir şirkette herkes mesai başında on dakika içinde giriş yapsa bile saniyede ikiden az giriş olur. Asıl zamanı alan işler ise yüksek erişilebilirlik (en az iki düğüm ve çoğaltılmış bir PostgreSQL), sürüm yükseltmeleri, yedekleme, TLS, izleme ve nöbet düzenidir. Bulut IdP'ler bu yükü üstlenir; karşılığında kullanıcı başı ücret ödersiniz ve sağlayıcının bölge ve özellik kararlarına bağlı kalırsınız.

MFA, dizin hizmeti ve roller nereye oturur?​

Çok faktörlü doğrulamanın (MFA) yeri IdP'dir. Bütün uygulamalar IdP'ye güvendiği için orada zorunlu tutulan MFA hepsini kapsar. Hassas bir işlem için daha güçlü doğrulama isteyen bir uygulama, kullanıcının nasıl doğrulandığını OIDC'de acr ve amr taleplerinden (claim), SAML'da AuthnContextClassRef değerinden okuyabilir.
Dizin hizmeti (LDAP, Active Directory) IdP'nin arkasında kullanıcı ve parola kaynağı olarak kalır; her uygulamanın tek tek bağlandığı bir sistem olmaktan çıkar.
Roller: IdP kullanıcının kim olduğunu ve hangi gruplarda bulunduğunu söyler; bu grupların uygulamada ne yapabileceğine uygulama karar verir. Bu eşlemeyi küçük ve açık tutun.

Adım adım A: Keycloak ve Grafana ile OIDC kurulumu​

Varsayımlar: https://sso.example.com adresinde Keycloak 26.8, acme adlı bir realm ve uygulama olarak https://grafana.example.com adresindeki Grafana. OIDC destekleyen her uygulama aynı kalıbı izler. Denemek için kurup silebileceğiniz geçici bir laboratuvar ortamı:
Bash:
docker run --rm -p 127.0.0.1:8080:8080 \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD=degistirin \
  quay.io/keycloak/keycloak:26.8.1 start-dev

# Üretimde: harici bir veritabanıyla "kc.sh start", --hostname https://sso.example.com
# ve TLS'i sonlandıran bir reverse proxy'nin arkasında
# --proxy-headers xforwarded --http-enabled true

Keycloak tarafı​

  1. Manage realms > Create realm ile acme adlı realm'i oluşturun. Hazır gelen master realm'i yalnızca Keycloak'un yönetimi için kullanın; Keycloak belgeleri de uygulama kullanıcılarını orada açmamanızı söyler.
  2. Clients > Create client: Client type OpenID Connect, Client ID grafana.
  3. Capability config: Client authentication açık (Grafana sunucuda çalıştığı için gizli istemci, yani confidential client), Standard flow açık, Direct access grants kapalı, Implicit flow kapalı, PKCE method S256.
  4. Login settings: Valid redirect URIs alanına joker karakter kullanmadan tam olarak https://grafana.example.com/login/generic_oauth, Valid post logout redirect URIs alanına https://grafana.example.com/login, Web origins alanına https://grafana.example.com yazın.
  5. Credentials sekmesi: Client secret değerini Git deposuna değil, gizli anahtar kasanıza (secret store) koyun.
  6. Client scopes > grafana-dedicated: Group Membership türünde bir eşleyici (mapper) ekleyin: Token Claim Name alanına groups yazın; Full group path kapalı (varsayılanı açıktır ve /üst/alt biçiminde tam yol üretir), Add to ID token açık, Add to userinfo açık.
  7. Groups: grafana-admins ve grafana-editors gruplarını oluşturup kullanıcıları ekleyin. Roller varsayılan olarak kimlik belirtecine değil, erişim belirtecine (realm_access ve resource_access talepleri) yazılır; bu örnekte grupları eşlememizin nedeni de bu.

Uygulama tarafı (grafana.ini)​

Bütün uç noktalar https://sso.example.com/realms/acme issuer adresinin altındadır; keşif belgesi /realms/acme/.well-known/openid-configuration yolundadır. allow_sign_up = true satırı, Grafana'nın hesabı ilk girişte açmasını, yani anlık provizyonu (just-in-time, JIT) sağlar.
INI:
[server]
root_url = https://grafana.example.com/

[auth.generic_oauth]
enabled = true
name = Keycloak
allow_sign_up = true
client_id = grafana
client_secret = $__file{/etc/grafana/secrets/keycloak_client_secret}
scopes = openid email profile
use_pkce = true
auth_url = https://sso.example.com/realms/acme/protocol/openid-connect/auth
token_url = https://sso.example.com/realms/acme/protocol/openid-connect/token
api_url = https://sso.example.com/realms/acme/protocol/openid-connect/userinfo
login_attribute_path = preferred_username
email_attribute_path = email
name_attribute_path = name
groups_attribute_path = groups
role_attribute_path = contains(groups[*], 'grafana-admins') && 'Admin' || contains(groups[*], 'grafana-editors') && 'Editor' || 'Viewer'
signout_redirect_url = https://sso.example.com/realms/acme/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2Fgrafana.example.com%2Flogin

Kendi kodunuzda kimlik belirtecini doğrulamak​

Uygulamayı kendiniz yazıyorsanız OpenID sertifikalı (OpenID Certified) ya da bakımı süren bir OIDC istemci kütüphanesi kullanın. Kimlik belirtecini doğrudan işlemeniz gerekiyorsa aşağıdaki gibi doğrulayın. Örnek, wiki adıyla kayıtlı bir iç uygulama içindir ve PyJWT 2.14 veya sonrasını ister: 2.13 sürümüyle belirtecin alg değeri JWKS'ten alınan anahtara bağlandı, 2.14 ise PyJWKClient'ın anahtarları çekme ve yenileme biçimini sıkılaştırdı.
Python:
# pip install "pyjwt[crypto]>=2.14" requests
import jwt
import requests

ISSUER = "https://sso.example.com/realms/acme"
CLIENT_ID = "wiki"

conf = requests.get(f"{ISSUER}/.well-known/openid-configuration", timeout=5).json()
if conf["issuer"] != ISSUER:
    raise RuntimeError("keşif belgesi farklı bir issuer bildiriyor")
jwks = jwt.PyJWKClient(conf["jwks_uri"])  # anahtarları önbelleğe alır, bilinmeyen kid görünce yeniden çeker

def validate_id_token(id_token: str, expected_nonce: str) -> dict:
    signing_key = jwks.get_signing_key_from_jwt(id_token)
    claims = jwt.decode(
        id_token,
        signing_key,
        algorithms=["RS256"],   # realm'in imzaladığı algoritma; asla belirteç başlığından almayın
        audience=CLIENT_ID,     # aud bizim client_id değerimizi içermeli
        issuer=ISSUER,          # iss birebir eşleşmeli
        leeway=60,              # exp/iat/nbf için izin verilen saat kayması (saniye)
        options={"require": ["iss", "aud", "exp", "iat", "sub", "nonce"]},
    )
    if claims["nonce"] != expected_nonce:  # yönlendirmeden önce kullanıcının oturumuna kaydedilen değer
        raise ValueError("nonce uyuşmuyor")
    return claims
Yerel hesabı iss + sub ikilisine bağlayın; e-postaya ya da kullanıcı adına asla. OpenID Connect Core belirtimi bunu açıkça söyler: Kimlik belirtecindeki sub ve iss talepleri birlikte kullanıldığında, bağlı tarafın son kullanıcı için kalıcı tanımlayıcı olarak güvenebileceği tek taleplerdir.

Adım adım B: Microsoft Entra ID ile SAML SSO​

Varsayımlar: SaaS üreticisi belgelerinde SP entityID değerini https://app.example.com/saml/metadata, ACS adresini https://app.example.com/saml/acs olarak veriyor. En az Cloud Application Administrator rolüne ihtiyacınız var.

Entra ID yapılandırması​

  1. Entra yönetim merkezi > Entra ID > Enterprise apps > All applications > New application. Uygulama galerideyse oradan ekleyin: SAML ayarları hazır gelir, bazı kayıtlarda provizyon bağlayıcısı da bulunur. Galeride yoksa Create your own application > Integrate any other application you don't find in the gallery (Non-gallery) seçeneğini kullanın.
  2. Single sign-on > SAML > Basic SAML Configuration. Identifier (Entity ID): SP meta verisindeki entityID, harfi harfine. Entra bu değeri beyandaki Audience alanına yazar ve SP'nin AuthnRequest içindeki Issuer değerinin buna birebir uymasını bekler. Reply URL (Assertion Consumer Service URL): şeması, yolu ve varsa sondaki eğik çizgisiyle birlikte ACS adresi. Sign on URL: SP tarafından başlatılan (SP-initiated) giriş için uygulamanın kendi giriş adresi; boş bırakılırsa My Apps kutucuğu IdP tarafından başlatılan (IdP-initiated) bir akış başlatır. Relay State ve Logout Url: yalnızca üretici belgeliyorsa doldurun.
  3. Attributes & Claims. Unique User Identifier (Name ID) varsayılan olarak user.userprincipalname değeridir. Canlıya almadan önce SP'nin hesap anahtarı olarak hangi değeri kullanacağına karar verin: Kişi evlenip soyadını değiştirdiğinde, adı düzeltildiğinde ya da şirket yeni bir alan adına geçtiğinde UPN de değişir ve SP aynı kişi için ikinci bir hesap açar. SP izin veriyorsa Persistent biçimiyle user.objectid gibi değişmeyen bir değer kullanın, e-postayı sıradan bir öznitelik olarak gönderin. Yetkilendirme için Add a group claim > Groups assigned to the application seçeneğini kullanın: Belirteçte yalnızca uygulamaya atanmış gruplar yer alır (bu seçenekte iç içe gruplar dikkate alınmaz; kullanıcının atanmış gruba doğrudan üye olması gerekir) ve Entra'nın SAML belirteci için koyduğu 150 grup sınırının genellikle çok altında kalırsınız. Sınır aşılırsa Entra grupları hiç göndermez, yerine bir Microsoft Graph bağlantısı koyar.
  4. SAML Certificates. Entra üç yıl geçerli bir imzalama sertifikası üretmiştir. SP meta veriyi düzenli olarak çekebiliyorsa ona App Federation Metadata Url adresini verin (Microsoft, üreticilerden bu adresi en az 24 saatte bir kontrol etmelerini ve sertifika değişimi sırasında ikinci bir sertifikayı kabul etmelerini istiyor). Çekemiyorsa Certificate (Base64) dosyasını indirin ve Set up bölümündeki Login URL ile Microsoft Entra Identifier değerleriyle birlikte iletin. Bildirim listesine bir ekip posta kutusu ekleyin; Entra, sertifikanın bitişinden 60, 30 ve 7 gün önce e-posta gönderir. Çoğu uygulamada Signing Option varsayılan olarak Sign SAML assertion, imza algoritması da SHA-256'dır; yalnızca SP isterse Sign SAML response and assertion seçeneğine geçin.
  5. Users and groups: Girmesi gereken kullanıcıları ya da grupları atayın. Grup tabanlı atama Entra ID P1 veya P2 ister ve iç içe (nested) grupların üyelerini kapsamaz. Properties altında Assignment required Yes olduğunda atanmamış bir kullanıcı, SaaS uygulamasında kendine yeni bir hesap açtırmak yerine AADSTS50105 hatası alır.
  6. Önce SAML sayfasındaki Test düğmesiyle, ardından SAML-tracer eklentisi kayıttayken gizli bir tarayıcı penceresinden SP tarafından başlatılan bir girişle test edin.

SP yanıtta neyi denetler?​

Sadeleştirilmiş hâli (ad alanları ve imzalar çıkarıldı):
Kod:
<Response Destination="https://app.example.com/saml/acs" InResponseTo="_req42">
  <Issuer>https://sts.windows.net/<tenant-id>/</Issuer>        <- SP'nin güvendiği IdP entityID'si olmalı
  <Assertion>
    <Subject>
      <NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">...</NameID>
      <SubjectConfirmationData InResponseTo="_req42"             <- SP'nin gönderdiği AuthnRequest'in ID'si
          Recipient="https://app.example.com/saml/acs"           <- bu ACS adresi olmalı
          NotOnOrAfter="2026-10-27T09:05:00Z"/>
    </Subject>
    <Conditions NotBefore="2026-10-27T09:00:00Z" NotOnOrAfter="2026-10-27T10:10:00Z">
      <AudienceRestriction>
        <Audience>https://app.example.com/saml/metadata</Audience>  <- SP entityID'si olmalı
      </AudienceRestriction>
    </Conditions>
  </Assertion>
</Response>

Kullanıcı provizyonu: JIT mi, SCIM mi?​

Anlık provizyon (just-in-time, JIT) hesabı ilk girişte, beyandan ya da kimlik belirtecinden gelen bilgilerle açar. Ek bir entegrasyon gerektirmez; ama biri işten ayrıldığında bunu uygulamaya haber veren olmaz ve öznitelikler ancak kullanıcı yeniden giriş yaptığında güncellenir. SCIM 2.0 (şema RFC 7643'te, protokol RFC 7644'te) ise IdP'nin uygulamanın /Users ve /Groups uç noktalarını çağırarak hesap açmasını, güncellemesini, devre dışı bırakmasını ve silmesini sağlar. Örneğin Entra'nın provizyon hizmeti, kullanıcının ataması kaldırıldığında, hesabı devre dışı bırakıldığında ya da geçici olarak silindiğinde (soft delete) active değerini false yapar; kullanıcı kalıcı olarak silindiğinde, yani geçici silmeden 30 gün sonra DELETE gönderir. Hizmet periyodik döngülerle çalıştığı için değişiklikler anında yansımaz. Devre dışı bırakma, ağ üzerinde Content-Type: application/scim+json başlığıyla /scim/v2/Users/{id} adresine gönderilen ve şu gövdeyi taşıyan bir PATCH isteğidir:
JSON:
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": false }
  ]
}
IdP gruplarını uygulama rollerine tek bir yerde eşleyin (IdP'nin grup talebi ya da SCIM ile grup aktarımı) ve eşlemeyi küçük tutun. Bir SSO projesinin asıl güvenlik kazancı hesap kapatmadır (deprovisioning): SSO yalnızca bir sonraki girişe karar verir. Uygulamanın içinde üretilmiş API belirteçleri, yenileme belirteçleri (refresh token), mobil oturumlar ve paylaşım bağlantıları, hesap orada kapatılana kadar yaşamaya devam eder. İşten ayrılan biri için işe yarayan bir sıra şöyledir: IdP'de oturum açmayı engelleyin, kullanıcının IdP oturumlarını ve yenileme belirteçlerini iptal edin, SCIM'in (ya da betiğinizin) uygulama hesaplarını kapatmasını sağlayın ve en kritik sistemlerde sonucu yönetim panelinden doğrulayın.

Oturumlar ve oturum kapatma (logout)​

Aynı anda üç ayrı süre işler: IdP oturumu (Keycloak varsayılanları: SSO Session Idle 30 dakika, SSO Session Max 10 saat), uygulamanın kendi oturum çerezi ve belirteç ömürleri (Keycloak varsayılan olarak 5 dakikalık erişim belirteci üretir). Birini sonlandırmak diğerlerini sonlandırmaz.
  • SAML Single Logout (SLO, tek noktadan oturum kapatma): IdP her SP'ye, çoğunlukla tarayıcı yönlendirmeleriyle bir LogoutRequest gönderir. Tek bir SP'nin hata vermesi zinciri koparır; birçok SaaS ürünü SLO'yu zaten yok sayar.
  • OIDC RP-Initiated Logout: Uygulama tarayıcıyı id_token_hint ve post_logout_redirect_uri parametreleriyle IdP'nin end_session_endpoint adresine gönderir; bu dönüş adresi kayıtlı olmalıdır (Keycloak'ta Valid post logout redirect URIs).
  • OIDC Front-Channel Logout: IdP her uygulamanın oturum kapatma adresini iframe içinde yükler; içerik güvenlik politikası (Content Security Policy) ve üçüncü taraf çerez engelleri bunu sık sık durdurur.
  • OIDC Back-Channel Logout: IdP, sid ve/veya sub içeren imzalı bir oturum kapatma belirtecini (logout token) her uygulamanın arka kanal adresine sunucudan sunucuya POST eder. En güvenilir seçenek budur; bir kullanıcının oturumu yönetim konsolundan sonlandırıldığında da Keycloak bu çağrıyı yapabilir. Keycloak'ta arka kanal adresi yalnızca Front channel logout kapalıyken kullanılır.
Oturum kapatma çoğu SSO projesinin en zayıf halkasıdır, çünkü her uygulamanın iş birliğine bağlıdır. Uygulama oturumlarını kısa tutun, destekleyen uygulamalarda back-channel logout kullanın ve işten ayrılanlar için oturum kapatmaya değil, hesap kapatmaya güvenin.

SAML ve OIDC hatalarında sorun giderme​

BelirtiOlası nedenÇözüm
Girişten hemen sonra beyan "henüz geçerli değil" ya da "süresi dolmuş"NotBefore/NotOnOrAfter değerlerine göre saat kayması (clock skew); Entra NotBefore değerini hiç pay bırakmadan üretim anına koyarSP sunucularında NTP (chrony) çalıştırın; SP kütüphanesinde bir ila üç dakikalık kayma toleransı tanımlayın; IdP tarafındaki ömürleri uzatmayın
Audience geçersiz; AADSTS700016SP ile IdP'deki Entity ID farklı (http/https, sondaki eğik çizgi, eski alan adı)SP meta verisindeki entityID'yi Identifier (Entity ID) alanına harfi harfine kopyalayın
Keycloak'ta "Invalid parameter: redirect_uri"; AADSTS50011Yönlendirme ya da ACS adresi farklı; çoğu zaman TLS'i sonlandıran bir proxy'nin arkasındaki uygulama http:// ile başlayan bir adres üretirAdresi birebir kaydedin; X-Forwarded-Proto başlığının işlenmesini ya da uygulamanın dış adresini (Grafana'da root_url) düzeltin
Aylarca sorunsuz çalıştıktan sonra "Invalid signature"IdP sertifikasını ya da realm anahtarını yeniledi, SP ise eskisini sabitlemiştiSP meta veri adresini ya da JWKS'i okusun; yenilemeyi bir örtüşme süresiyle yapın
Ad değişikliğinden sonra mükerrer kullanıcıHesap anahtarı olarak NameID ya da e-posta kullanılmışDeğişmeyen bir kimliğe bağlayın (iss+sub, objectid); hesapları bir kez birleştirin
POST isteği ACS adresine ulaşıyor ama uygulama oturum bulunamadığını, state değerini tanımadığını ya da InResponseTo uyuşmazlığını bildiriyorSameSite: Chromium tabanlı tarayıcılar SameSite özniteliği olmayan çerezleri Lax sayar; Lax çerezler IdP'den gelen siteler arası POST isteğiyle gönderilmezPOST isteğiyle gönderilmesi gereken çerezde SameSite=None; Secure kullanın ya da istek durumunu RelayState anahtarıyla sunucu tarafında tutun. Chromium bu tür çerezler için iki dakikalık geçici bir "Lax+POST" istisnası belgelemişti; geçerli olduğu yerde hata yalnızca kullanıcı IdP'de uzun kaldığında, örneğin MFA kaydı sırasında ortaya çıkar
Uygulama ile IdP arasında yönlendirme döngüsüUygulama kendi oturumunu tutamıyor: düz HTTP ile açılan sayfada Secure işaretli çerez, yanlış çerez alanı (domain) ya da yolu (path) veya proxy arkasında uygulama ile IdP'nin dış adres konusunda anlaşamamasıGeri dönüşten sonra uygulamanın oturum çerezini yazdığını kontrol edin; proxy başlıklarını (X-Forwarded-*) ve dış adresi düzeltin
Kullanıcı giriyor ama hiçbir yetkisi yokGrup talebi yapılandırılmamış, eşleyici talebi kimlik belirtecine eklemiyor ya da Entra'nın sınırı (SAML'da 150, JWT'de 200 grup) aşıldığı için gruplar düşmüşYalnızca uygulamaya atanmış grupları gönderin ya da uygulama rolleri (app roles) kullanın; eşleyicideki Add to ID token ve Add to access token ayarlarını kontrol edin
AADSTS50105 ya da AADSTS75011Kullanıcı atanmamış; ya da SP, kullanıcının giriş biçimine uymayan bir kimlik doğrulama bağlamı (RequestedAuthnContext) istemişKullanıcıyı ya da grubu atayın; SP'deki RequestedAuthnContext ayarını kaldırın ya da uyumlu hâle getirin
Zaman kazandıran iki komut:
Bash:
# DevTools > Network'te ACS adresine giden POST isteğinden kopyalanan (URL kodlaması çözülmüş) SAMLResponse değerini çözün
base64 -d < saml-response.b64 | xmllint --format - | grep -E 'Issuer|Audience|NotBefore|NotOnOrAfter|NameID|InResponseTo'

# Size verilen IdP imzalama sertifikasının konusu, bitiş tarihi ve parmak izi
openssl x509 -in idp-signing.pem -noout -subject -enddate -fingerprint -sha256
Belirteçleri ve beyanları kendi makinenizde çözün; üretim belirteçlerinin çevrim içi çözücülerde işi yoktur. Aşırı büyük belirteçler proxy'leri de bozabilir (nginx günlüğünde "upstream sent too big header" görürsünüz); tampon boyutlarını durmadan büyütmek yerine grup taleplerini daraltmak daha iyi bir çözümdür.

SSO güvenlik kontrol listesi​

  • SSO arkasındaki her uygulama için MFA IdP'de zorunlu, yöneticiler için oltalamaya dayanıklı yöntemlerle (passkey, FIDO2 anahtarı): Entra'da Conditional Access (P1) ya da güvenlik varsayılanları (security defaults), Keycloak'ta kimlik doğrulama akışları (authentication flows).
  • Uygulamalar imzalı beyan ya da imzalı yanıt istiyor, imzasız olanı reddediyor; imzalar SHA-256; SAML kütüphaneleri güncel.
  • Birebir yönlendirme adresleri ve joker karakter yok; S256 ile PKCE; implicit flow ve direct access grants kapalı. OAuth 2.0 güvenliği için güncel en iyi uygulamaları tanımlayan RFC 9700, parola akışının (password grant) kullanılmaması gerektiğini (MUST NOT) açıkça yazar.
  • SSO çalışmaya başladıktan sonra uygulamalardaki yerel giriş yolları kapalı (uygulama gerektiriyorsa belgelenmiş tek bir acil durum yöneticisi hariç); böylece ne kullanıcılar ne de saldırganlar IdP'yi atlayabilir. Microsoft belgeleri bu konuyu ayrıca vurgular.
  • Keycloak'ta kaba kuvvet saldırısı tespiti açık (Realm settings > Security Defenses > Brute Force Detection); varsayılan olarak kapalıdır.
  • SAML sertifikaları ve realm anahtarları için bir yenileme takvimi. Entra sertifikaları varsayılan olarak üç yıl geçerlidir; Keycloak üç ila altı ayda bir yeni anahtar oluşturmayı, eskileri de bir ila iki ay sonra silmeyi önerir. Rutinin kendisini gizli anahtar rotasyonu konumuzda anlattık.
  • Federasyona bağlı olmayan, passkey ile korunan, engelleyici politikaların dışında tutulan, her kullanımında alarm üreten ve en az 90 günde bir test edilen en az iki acil durum hesabı (break-glass; Microsoft'un Entra için önerisi). Ayrıca hipervizör, güvenlik duvarı ve IdP'nin kendisi gibi sıfırıncı katman sistemlerde yerel yönetici erişimi.
  • En az yetki ilkesine uygun, açıkça tanımlanmış gruptan role eşleme. Keycloak'ta Full Scope Allowed seçeneğini kapatın (kullanımdan kaldırılma sürecinde) ve rol kapsam eşlemelerini (role scope mappings) tanımlayın.
  • Denetim günlükleri açık: Keycloak olayları varsayılan olarak saklamaz (Realm settings > Events altında hem User events settings hem Admin events settings için Save events). Keycloak olaylarını ve Entra oturum açma ile denetim günlüklerini SIEM'inize gönderin. Erişim gözden geçirmelerini ve işe giriş, görev değişikliği ve ayrılış süreçlerini kimlik ve erişim yönetimi temelleri konusunda özetledik.

Sık sorulan sorular​

SSO ile OAuth arasındaki fark nedir?
OAuth 2.0 yetki devri (delegated authorization) içindir: Bir uygulama bir API'yi çağırmak için erişim belirteci alır. OpenID Connect bunun üzerine kimlik belirteciyle kimlik doğrulamayı ekler. SSO ise OIDC ya da SAML ile kurduğunuz kullanıcı deneyimidir.

SAML artık eskidi mi?
Hayır. Kurumsal SaaS ürünlerinin çoğu hâlâ SAML destekler, bir kısmı da yalnızca onu destekler. Yeni uygulamalarda OIDC'yi doğru uygulamak daha kolaydır; üreticinin şart koştuğu yerde SAML'ı kullanmaya devam edin.

SSO'yu LDAP ya da Active Directory ile kullanabilir miyim?
Evet. Dizin, IdP'nin arkasında kullanıcı deposu olarak kalır: Keycloak, LDAP ve AD dizinlerini kullanıcı federasyonu (user federation) ile doğrudan bağlar ve etki alanına katılmış bilgisayarlarda Kerberos ile Windows oturumundan otomatik giriş sağlayabilir; Entra ID ise şirket içindeki AD'den eşitleme yapar. Uygulamalar LDAP'a bağlanmak yerine IdP ile SAML ya da OIDC konuşur.

SCIM'e ihtiyacım var mı?
Uygulamadaki hesaplar veri, API belirteci ya da uzun oturum tutuyorsa veya her ay işe giren ve ayrılan varsa evet: JIT kimseyi silmez. Uygulama ne SCIM'i ne de eşdeğer bir yöntemi destekliyorsa, API'si üzerinden düzenli çalışan bir mutabakat betiği yazın.

IdP çökerse ne olur?
Her yerde yeni girişler başarısız olur. Mevcut uygulama oturumları süreleri dolana kadar çalışmaya devam eder, belirteç yenileme ise başarısız olur. IdP'yi yüksek erişilebilir çalıştırın, keşif ya da meta veri uç noktasını izleyin ve sorunu çözmek için gereken sistemlere acil durum erişimini hazır tutun.

Sonuç ve sonraki adımlar​

Protokolü her uygulama için ayrı seçin, ilk kullanıcı giriş yapmadan önce hesap anahtarına karar verin; provizyonu, oturum kapatmayı ve sertifika bitiş tarihlerini sonraya kalan işler olarak değil, projenin parçası olarak ele alın.
  1. Uygulamalarınızı ve her birinin desteklediği protokolü listeleyin (OIDC, SAML, hiçbiri).
  2. IdP'yi seçin ve bir test realm'i ya da test kiracısı (tenant) kurun.
  3. Yukarıdaki adımlarla bir OIDC ve bir SAML uygulamasını bağlayın; sorun giderme tablosunu elinizin altında tutun.
  4. SCIM'i ya da bir hesap kapatma betiğini devreye alın ve bir çalışanın ayrılışını uçtan uca test edin.
  5. Sertifika ve anahtar bitiş tarihlerini ortak bir takvime işleyin, denetim günlüklerini açın.
SSO geçişinizde ya da IdP sıkılaştırmasında ikinci bir göze ihtiyaç duyarsanız AIOR siber güvenlik ekibi yardımcı olabilir.

Single sign-on (SSO) lets a user authenticate once at an identity provider and then open many applications without typing a password into any of them. Getting there takes three decisions and a lot of exact string matching: which protocol each application speaks (SAML 2.0 or OpenID Connect), which identity provider (IdP) holds the users, and how accounts are created and removed inside each application. This guide covers those decisions, walks through an OIDC integration with Keycloak and a SAML integration with Microsoft Entra ID, and ends with a troubleshooting table for the errors that show up on day one. OAuth 2.0 token flows get a deeper treatment in our thread OAuth 2.0 and OIDC: authentication flows and security patterns; MFA, LDAP and role policy have their own sub-forums, so each gets one paragraph here. Admin console labels are quoted from the English UI of Keycloak 26.8 and the Microsoft Entra admin center as of October 2026.

What SSO is, and what it is not​

With SSO the user holds one authenticated session at the IdP. Each application redirects the browser to the IdP, receives a signed statement about the user and creates its own local session; the application never sees the password. Password synchronization, or pointing every application at the same LDAP directory, is a different thing: the user types the same password into every login form, every application handles that credential, and a breach of any one of them exposes it.
  • Identity provider (IdP): authenticates the user (password, MFA, passkey) and issues signed statements. Examples: Microsoft Entra ID, Okta, Keycloak.
  • Service provider (SP) or relying party (RP): the application that trusts the IdP. SAML calls it the SP; OIDC calls it the RP or client.
  • Assertion: SAML's signed XML document with the subject (NameID), attributes and conditions such as the validity window and the audience.
  • ID token: OIDC's signed JSON Web Token (JWT) saying who signed in, for which client (aud), when it was issued and when it expires (iat, exp), and in answer to which request (nonce).
  • Session: there are at least two, the IdP's session cookie and each application's own session. SSO works because the first exists; logout is hard because the others are separate.
  • Federation: the trust between IdP and SP, built by exchanging metadata, endpoints and signing keys.
  • Identity brokering: an IdP that hands authentication to another IdP, for example Keycloak sending users to Entra ID or Google, so applications only ever talk to one IdP.

SAML 2.0 vs OpenID Connect​

AspectSAML 2.0OpenID Connect (OIDC)
TokenXML assertion with XML Signature, optional XML EncryptionJWT ID token (JWS), optional JWE; separate access token for APIs
Message flowUsually browser only: AuthnRequest by redirect, Response by an auto-submitted form POST to the Assertion Consumer Service (ACS)Browser redirect brings back a short-lived code; the app swaps it for tokens at the token endpoint over a back channel (with client authentication where the app can keep a secret, plus PKCE)
Configuration exchangeMetadata XML: entityID, endpoints, certificatesDiscovery document at /.well-known/openid-configuration plus a JWKS URL
Key rolloverCertificates in metadata; many SPs pin a pasted certificateKeys published in the JWKS with a kid; most libraries refetch on an unknown kid
LogoutSingle Logout (SLO), usually a chain of browser redirectsRP-Initiated, Front-Channel and Back-Channel Logout specifications
SPA, mobile, APIsPoor fit: XML, form POST, no API tokensDesigned for them (PKCE, access tokens)
Where you meet itEnterprise SaaS, older internal apps, B2B federationsNew web apps, SPAs, mobile apps, APIs, Kubernetes and cloud tooling
Decision rules that hold up in practice:
  • An application you are building now: OIDC with the authorization code flow and PKCE.
  • A SaaS product that only offers SAML: use SAML. Don't put a protocol-translating proxy in front of it unless you have to.
  • A mixed estate: pick an IdP that speaks both. Each application gets the protocol it supports, and users still see one login page.
  • You need tokens to call APIs: OIDC/OAuth 2.0. SAML gives you a login, not an API credential.
Neither protocol is more secure by default. Both are safe when the signature, issuer, audience and validity window are checked, and both fail when they aren't. XML signature handling is the harder one to get right: ruby-saml, the library behind GitLab's and omniauth-saml's SAML login, had critical authentication bypasses in 2024 (CVE-2024-45409) and 2025 (CVE-2025-25291 and CVE-2025-25292). Use a maintained library, keep it patched, and never write your own XML signature validation.

Choosing an identity provider​

Start with where your users already live: on Microsoft 365 every account is already in Entra ID, on Google Workspace it is in Google. Then compare protocols, provisioning, policy features, hosting and cost model:
IdPHostingSSO protocolsProvisioning to appsCost model
Microsoft Entra IDCloudSAML, OIDC, WS-FedSCIM 2.0 provisioning service for gallery and custom appsPer-user tiers; Conditional Access and group-based app assignment need P1 or higher
Google Workspace / Cloud IdentityCloudSAML (custom SAML apps), OIDC through Google OAuth clientsAutomated provisioning for a list of supported appsPer user, depends on edition
OktaCloudSAML, OIDCSCIM through its Lifecycle Management featuresPer-user, per-product subscription
KeycloakSelf-hostedOIDC, SAML; LDAP/AD and Kerberos federation; brokeringNo outbound SCIM in core (26.8 adds a supported SCIM API for provisioning into Keycloak)Open source (Apache 2.0); you pay in operations
authentikSelf-hostedOIDC, SAML, LDAP, RADIUS, proxySCIM provider: pushes each change, full sync hourlyOpen-source core; Enterprise edition billed per user
JumpCloud, Ping Identity, OneLogin, ZITADEL and Authelia are among the other options; the same criteria apply.

A self-hosted IdP costs operations rather than capacity. It is a tier-0 system: when it is down, nobody signs in anywhere. Keycloak's own sizing guide budgets about 1 vCPU per 15 password logins per second plus 150% headroom for spikes, and about 1,250 MB of RAM per pod with 10,000 cached sessions (measured on AWS with Argon2 password hashing; run your own load test before production). A 1,000-person company where everyone signs in within ten minutes produces fewer than two logins per second. The time goes into availability (at least two nodes and a replicated PostgreSQL), upgrades, backups, TLS, monitoring and on-call. A hosted IdP takes that work off your hands in exchange for per-user fees and the vendor's choices on regions and features.

Where MFA, the directory and roles fit​

MFA belongs at the IdP. Once every application trusts the IdP, enforcing MFA there covers all of them, and an application that wants a stronger step for a sensitive action can read how the user authenticated (acr and amr claims in OIDC, AuthnContextClassRef in SAML).
The directory (LDAP, Active Directory) stays the source of users and passwords behind the IdP instead of something each application binds to.
Roles: the IdP says who the user is and which groups they belong to; the application decides what those groups may do. Keep that mapping small and explicit.

Walkthrough A: OIDC with Keycloak and Grafana​

Assumptions: Keycloak 26.8 at https://sso.example.com with a realm named acme, and Grafana at https://grafana.example.com as the application. Any OIDC-capable application follows the same pattern. A throwaway lab instance:
Bash:
docker run --rm -p 127.0.0.1:8080:8080 \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD=change-me \
  quay.io/keycloak/keycloak:26.8.1 start-dev

# Production: "kc.sh start" with an external database, --hostname https://sso.example.com
# and, behind a TLS-terminating reverse proxy, --proxy-headers xforwarded --http-enabled true

Keycloak side​

  1. Manage realms > Create realm, with acme as the realm name. Keep the master realm for administering Keycloak; its documentation says not to create application users there.
  2. Clients > Create client: Client type OpenID Connect, Client ID grafana.
  3. Capability config: Client authentication On (a confidential client, because Grafana runs on a server), Standard flow On, Direct access grants Off, Implicit flow Off, PKCE method S256.
  4. Login settings: Valid redirect URIs exactly https://grafana.example.com/login/generic_oauth, with no wildcard; Valid post logout redirect URIs https://grafana.example.com/login; Web origins https://grafana.example.com.
  5. Credentials tab: copy the client secret into your secret store, not into Git.
  6. Client scopes > grafana-dedicated: add a mapper of type Group Membership with Token Claim Name groups, Full group path Off (it defaults to On and produces /parent/child paths), Add to ID token On, Add to userinfo On.
  7. Groups: create grafana-admins and grafana-editors and add users. Roles go into the access token by default (realm_access and resource_access claims), not the ID token, which is why this example maps groups.

Application side (grafana.ini)​

Every endpoint hangs off the issuer https://sso.example.com/realms/acme, whose discovery document is at /realms/acme/.well-known/openid-configuration. allow_sign_up = true makes Grafana create the account at first login, which is just-in-time (JIT) provisioning.
INI:
[server]
root_url = https://grafana.example.com/

[auth.generic_oauth]
enabled = true
name = Keycloak
allow_sign_up = true
client_id = grafana
client_secret = $__file{/etc/grafana/secrets/keycloak_client_secret}
scopes = openid email profile
use_pkce = true
auth_url = https://sso.example.com/realms/acme/protocol/openid-connect/auth
token_url = https://sso.example.com/realms/acme/protocol/openid-connect/token
api_url = https://sso.example.com/realms/acme/protocol/openid-connect/userinfo
login_attribute_path = preferred_username
email_attribute_path = email
name_attribute_path = name
groups_attribute_path = groups
role_attribute_path = contains(groups[*], 'grafana-admins') && 'Admin' || contains(groups[*], 'grafana-editors') && 'Editor' || 'Viewer'
signout_redirect_url = https://sso.example.com/realms/acme/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2Fgrafana.example.com%2Flogin

Validating the ID token in your own code​

If you write the application yourself, use an OpenID Certified or well-maintained OIDC client library. If you must handle the ID token directly, validate it like this. The example is for an internal app registered as client wiki and needs PyJWT 2.14 or later: 2.13 started binding the token's alg to the key taken from the JWKS, and 2.14 hardened how PyJWKClient fetches and refreshes keys.
Python:
# pip install "pyjwt[crypto]>=2.14" requests
import jwt
import requests

ISSUER = "https://sso.example.com/realms/acme"
CLIENT_ID = "wiki"

conf = requests.get(f"{ISSUER}/.well-known/openid-configuration", timeout=5).json()
if conf["issuer"] != ISSUER:
    raise RuntimeError("discovery document names a different issuer")
jwks = jwt.PyJWKClient(conf["jwks_uri"])  # caches keys, refetches when it sees an unknown kid

def validate_id_token(id_token: str, expected_nonce: str) -> dict:
    signing_key = jwks.get_signing_key_from_jwt(id_token)
    claims = jwt.decode(
        id_token,
        signing_key,
        algorithms=["RS256"],   # what the realm signs with; never take it from the token header
        audience=CLIENT_ID,     # aud must contain our client_id
        issuer=ISSUER,          # iss must match exactly
        leeway=60,              # seconds of clock skew allowed on exp/iat/nbf
        options={"require": ["iss", "aud", "exp", "iat", "sub", "nonce"]},
    )
    if claims["nonce"] != expected_nonce:  # saved in the user's session before the redirect
        raise ValueError("nonce mismatch")
    return claims
Key the local account on the pair iss + sub, never on email or username. OpenID Connect Core is explicit about it:
The sub (subject) and iss (issuer) Claims from the ID Token, used together, are the only Claims that an RP can rely upon as a stable identifier for the End-User

Walkthrough B: SAML with Microsoft Entra ID​

Assumptions: the SaaS vendor documents its SP entityID as https://app.example.com/saml/metadata and its ACS as https://app.example.com/saml/acs. You need at least the Cloud Application Administrator role.

Entra ID configuration​

  1. Entra admin center > Entra ID > Enterprise apps > All applications > New application. If the app is in the gallery, add it from there: SAML settings come pre-shaped and some entries include a provisioning connector. Otherwise choose Create your own application > Integrate any other application you don't find in the gallery (Non-gallery).
  2. Single sign-on > SAML > Basic SAML Configuration. Identifier (Entity ID): the SP's entityID from its metadata, character for character. Entra writes it into the assertion's Audience and expects the Issuer of the SP's AuthnRequest to match it exactly. Reply URL (Assertion Consumer Service URL): the ACS URL including scheme, path and any trailing slash. Sign on URL: the app's own login URL for SP-initiated sign-in; left empty, the My Apps tile starts an IdP-initiated flow. Relay State and Logout Url: only if the vendor documents them.
  3. Attributes & Claims. The Unique User Identifier (Name ID) defaults to user.userprincipalname. Decide before go-live which value the SP uses as its account key: a UPN changes when someone changes their name or the company moves to a new domain, and the SP then creates a second account for the same person. Where the SP allows it, use a stable value such as user.objectid with the Persistent format and send email as an ordinary attribute. For authorization, use Add a group claim > Groups assigned to the application: the token then carries only the groups assigned to the app (nested groups are not considered with this option, so users must be direct members), which normally keeps it far below Entra's limit of 150 groups in a SAML token. Above that limit Entra drops the groups and sends a Microsoft Graph link instead.
  4. SAML Certificates. Entra has generated a signing certificate valid for three years. Give the SP the App Federation Metadata Url if it can poll metadata (Microsoft asks vendors to check it at least every 24 hours and to accept a second certificate during rollover); otherwise download Certificate (Base64) and pass on the Login URL and Microsoft Entra Identifier from the Set up section. Add a team mailbox to the notification list: Entra emails 60, 30 and 7 days before expiry. For most apps the Signing Option defaults to Sign SAML assertion with SHA-256; switch to Sign SAML response and assertion only if the SP asks for it.
  5. Users and groups: assign the users or groups who should get in. Group-based assignment needs Entra ID P1 or P2 and does not cover members of nested groups. With Assignment required set to Yes under Properties, an unassigned user gets AADSTS50105 instead of a fresh account in the SaaS app.
  6. Test with the Test button on the SAML page, then with an SP-initiated login from a private browser window while the SAML-tracer extension records the exchange.

What the SP checks in the response​

Simplified, with namespaces and signatures removed:
Kod:
<Response Destination="https://app.example.com/saml/acs" InResponseTo="_req42">
  <Issuer>https://sts.windows.net/<tenant-id>/</Issuer>        <- must equal the IdP entityID the SP trusts
  <Assertion>
    <Subject>
      <NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">...</NameID>
      <SubjectConfirmationData InResponseTo="_req42"             <- the ID of the AuthnRequest the SP sent
          Recipient="https://app.example.com/saml/acs"           <- must be this ACS
          NotOnOrAfter="2026-10-27T09:05:00Z"/>
    </Subject>
    <Conditions NotBefore="2026-10-27T09:00:00Z" NotOnOrAfter="2026-10-27T10:10:00Z">
      <AudienceRestriction>
        <Audience>https://app.example.com/saml/metadata</Audience>  <- must equal the SP entityID
      </AudienceRestriction>
    </Conditions>
  </Assertion>
</Response>

Provisioning and deprovisioning: JIT vs SCIM​

Just-in-time (JIT) provisioning creates the account at first login from the assertion or ID token. It needs no extra integration, but nothing tells the application when someone leaves, and attributes only change when the user logs in again. SCIM 2.0 (schema in RFC 7643, protocol in RFC 7644) lets the IdP call the application's /Users and /Groups endpoints to create, update, disable and delete accounts. Entra's provisioning service, for example, sets active to false when a user is unassigned, disabled or soft-deleted, and sends a DELETE once the user is permanently deleted, 30 days after a soft delete. It works in periodic cycles, so it is not instant. On the wire, a disable is a PATCH to /scim/v2/Users/{id} with Content-Type application/scim+json and this body:
JSON:
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": false }
  ]
}
Map IdP groups to application roles in one place (the IdP's group claim or the SCIM group push) and keep the mapping small. Deprovisioning is where an SSO project pays off for security: SSO only decides the next login, while API tokens, refresh tokens, mobile sessions and shared links created inside the application live on until the account is disabled there. A workable offboarding order: block sign-in at the IdP, revoke the user's IdP sessions and refresh tokens, let SCIM (or your script) disable the application accounts, and confirm the result in the admin panels of the systems that matter most.

Sessions and logout​

Three clocks run at once: the IdP session (Keycloak defaults: SSO Session Idle 30 minutes, SSO Session Max 10 hours), the application's own session cookie, and token lifetimes (Keycloak issues 5-minute access tokens by default). Ending one does not end the others.
  • SAML Single Logout: the IdP sends a LogoutRequest to each SP, usually through browser redirects. One SP that fails breaks the chain, and many SaaS products ignore SLO.
  • OIDC RP-Initiated Logout: the app sends the browser to the IdP's end_session_endpoint with id_token_hint and a post_logout_redirect_uri, which must be registered (Valid post logout redirect URIs in Keycloak).
  • OIDC Front-Channel Logout: the IdP loads each app's logout URL in iframes; Content Security Policy and third-party cookie blocking often stop it.
  • OIDC Back-Channel Logout: the IdP POSTs a signed logout token carrying sid and/or sub to each app's back-channel URL, server to server. It is the most reliable option, and Keycloak can also send it when an administrator ends a user's session. In Keycloak the back-channel URL is used only when Front channel logout is Off.
Logout is the weakest part of most rollouts because it needs every application to cooperate. Keep application sessions short, use back-channel logout where the application supports it, and rely on deprovisioning for people who leave.

Troubleshooting SAML and OIDC errors​

SymptomLikely causeFix
Assertion "not yet valid" or "expired" right after loginClock skew against NotBefore/NotOnOrAfter; Entra sets NotBefore to the issue time with no bufferRun NTP (chrony) on SP hosts; allow one to three minutes of skew in the SP library; don't stretch IdP lifetimes
Invalid audience; AADSTS700016Entity ID differs between SP and IdP (http vs https, trailing slash, old domain)Copy the entityID from the SP metadata into Identifier (Entity ID) verbatim
Keycloak "Invalid parameter: redirect_uri"; AADSTS50011Redirect URI or ACS URL differs; often the app sits behind a TLS proxy and builds an http:// URLRegister the exact URL; fix X-Forwarded-Proto handling or the app's public URL (root_url in Grafana)
"Invalid signature" after months of workingThe IdP rotated its certificate or realm key and the SP pinned the old oneLet the SP read the metadata URL or JWKS; rotate with an overlap period
Duplicate user after a renameNameID or email used as the account keyKey on an immutable ID (iss+sub, objectid); merge the accounts once
The POST reaches the ACS but the app reports no session, unknown state or an InResponseTo mismatchSameSite: Chromium-based browsers treat cookies without a SameSite attribute as Lax, and Lax cookies are not sent on the IdP's cross-site POSTSet SameSite=None; Secure on the cookie that must survive the POST, or keep request state server-side keyed by RelayState. Chromium documented a temporary two-minute "Lax+POST" exception for such cookies; where it still applies, the failure only shows when a user spends longer at the IdP, for example during MFA enrolment
Redirect loop between app and IdPThe app cannot keep its own session: a Secure cookie on a page served over plain HTTP, the wrong cookie domain or path, or, behind a proxy, app and IdP disagreeing on the external URLCheck that the app sets its session cookie after the callback; fix the proxy headers (X-Forwarded-*) and the public URL
User signs in but has no rightsGroup claim not configured, the mapper does not add it to the ID token, or Entra's overage limit (150 groups in SAML, 200 in a JWT) dropped itEmit only groups assigned to the application or use app roles; check the mapper's Add to ID token and Add to access token switches
AADSTS50105 or AADSTS75011User not assigned; or the SP requested an authentication context (RequestedAuthnContext) that does not match how the user signed inAssign the user or group; remove or align RequestedAuthnContext in the SP
Two commands that save time:
Bash:
# Decode a SAMLResponse copied (URL-decoded) from DevTools > Network > the POST to the ACS
base64 -d < saml-response.b64 | xmllint --format - | grep -E 'Issuer|Audience|NotBefore|NotOnOrAfter|NameID|InResponseTo'

# Subject, expiry date and fingerprint of the IdP signing certificate you were given
openssl x509 -in idp-signing.pem -noout -subject -enddate -fingerprint -sha256
Decode tokens and assertions locally; production tokens do not belong in online decoders. Oversized tokens can also break proxies (nginx logs "upstream sent too big header"); trimming group claims is a better fix than ever-larger buffers.

SSO security checklist​

  • MFA enforced at the IdP for every application behind SSO, with phishing-resistant methods (passkeys, FIDO2 keys) for administrators: Conditional Access (P1) or security defaults in Entra, authentication flows in Keycloak.
  • Applications require signed assertions or signed responses and reject unsigned ones; SHA-256 signatures; SAML libraries patched.
  • Exact redirect URIs with no wildcards, PKCE with S256, implicit flow and direct access grants off. RFC 9700, the OAuth 2.0 Security Best Current Practice, says the password grant MUST NOT be used.
  • Local login paths in the applications disabled once SSO works (apart from one documented break-glass admin where an application needs it), so neither users nor attackers can go around the IdP; Microsoft's documentation calls this out explicitly.
  • Keycloak brute force detection switched on (Realm settings > Security Defenses > Brute Force Detection); it is off by default.
  • A rotation calendar for SAML certificates and realm keys. Entra certificates default to three years; Keycloak suggests new keys every three to six months and removing old ones one to two months later. Our secret rotation thread covers the routine.
  • At least two break-glass accounts that do not depend on federation, protected by passkeys, excluded from blocking policies, alerting on every use and tested at least every 90 days (Microsoft's guidance for Entra), plus local admin access to tier-0 systems such as the hypervisor, the firewall and the IdP itself.
  • Least-privilege mapping from groups to roles, explicitly. In Keycloak, turn off Full Scope Allowed (now deprecated) and define role scope mappings.
  • Audit logs on: Keycloak does not store events by default (Realm settings > Events, Save events under both User events settings and Admin events settings). Ship Keycloak events and Entra sign-in and audit logs to your SIEM. Access reviews and the joiner-mover-leaver process are outlined in our identity and access management fundamentals thread.

FAQ​

What is the difference between SSO and OAuth?
OAuth 2.0 delegates authorization: an application gets an access token to call an API. OpenID Connect adds authentication on top, in the form of the ID token. SSO is the user experience you build with OIDC or SAML.

Is SAML obsolete?
No. Most enterprise SaaS products still support it and some support nothing else. For new applications OIDC is easier to implement correctly; keep SAML where a vendor requires it.

Can I use SSO with LDAP or Active Directory?
Yes. The directory stays the user store behind the IdP: Keycloak federates LDAP and AD directly and can use Kerberos for automatic sign-in on domain-joined PCs, while Entra ID synchronizes from on-premises AD. Applications talk SAML or OIDC to the IdP instead of binding to LDAP.

Do I need SCIM?
If accounts in the application hold data, API tokens or long sessions, or people join and leave every month, yes: JIT never removes anyone. If an application supports neither SCIM nor an equivalent, script a regular reconciliation through its API.

What happens if the IdP is down?
New logins fail everywhere. Existing application sessions keep working until they expire, while token refreshes fail. Run the IdP highly available, monitor its discovery or metadata endpoint, and keep break-glass access to the systems you need to fix it.

Conclusion and next steps​

Choose the protocol per application, settle the account key before the first user logs in, and treat provisioning, logout and certificate expiry as part of the project rather than follow-ups.
  1. List your applications and the protocols each supports (OIDC, SAML, neither).
  2. Choose the IdP and set up a test realm or tenant.
  3. Integrate one OIDC and one SAML application with the walkthroughs above, keeping the troubleshooting table at hand.
  4. Turn on SCIM or a deprovisioning script and test an offboarding end to end.
  5. Put certificate and key expiry dates in a shared calendar and switch on audit logging.
If you would like a second pair of eyes on an SSO rollout or IdP hardening, AIOR's cyber security team can help.
 
Son düzenleyen:

Forum istatistikleri

Konular
925
Mesajlar
932
Üyeler
26
En son üye
Jessicawatson

Çevrimiçi üyeler

Şu anda çevrimiçi üye yok.

Öne çıkan içerik

AIOR
AIOR TEKNOLOJİ

Tüm ihtiyaçlarınız için Teklif alın

Hosting · Domain · Sunucu · Tasarım · Yazılım · Mühendislik · Sektörel Çözümler

Teklif al

7/24 Destek · Anında yanıt

Geri
Üst