# OpenID Connect ile tek oturum açma

> Kullanıcıları OpenID Connect ile kimlik sağlayıcınız üzerinden girin, sağlayıcı gruplarını takım rollerine çevirin. Keycloak, Entra ID, Okta ve Google ayarları.

- Dokümantasyon: Kılavuzlar
- Güncelleme: 2026-11-04
- Kaynak: https://queryproxy.com/tr/docs/tek-oturum-acma/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
QueryProxy, discovery belgesi yayımlayan her OpenID Connect (OIDC) sağlayıcısı
üzerinden kullanıcı girişi yapabilir ve takım rollerini sağlayıcının bildirdiği
gruplardan belirleyebilir. Bu sayfa ayarları, her sağlayıcı için bir örneği, rol
eşlemesini ve parola girişini kapatmadan önce bilmeniz gereken davranışları anlatır.

> **Çok adımlı doğrulama (MFA) sağlayıcının işidir.** SSO ile giren bir kullanıcı
> `QUERYPROXY_REQUIRE_2FA` kapsamının dışındadır: QueryProxy ondan ikinci bir
> faktör istemez ve sağlayıcının hangisini kullandığını denetleyemez. MFA'yı kimlik
> sağlayıcıda, QueryProxy için kaydettiğiniz uygulamada zorunlu kılın.

## Nasıl çalışır

QueryProxy, PKCE'li authorization code akışını kullanır. `state` ve `nonce`
değerlerini denetler, ID token imzasını sağlayıcının yayımladığı anahtarlara karşı
doğrular. SAML desteklenmez.

1. `QUERYPROXY_OIDC_ISSUER` ve `QUERYPROXY_OIDC_CLIENT_ID` birlikte ayarlanınca
   giriş sayfasında **Log in with SSO** düğmesi görünür.
2. Kullanıcı sağlayıcıda oturum açar ve `{APP_URL}/auth/oidc/callback` adresine
   döner.
3. QueryProxy kullanıcıyı bulur ya da oluşturur, rol eşlemesini uygular ve oturum
   açar.

Kullanıcı, e-posta adresiyle değil, issuer ve subject (`sub`) çiftiyle tanınır.

## QueryProxy'yi sağlayıcıya kaydedin

Gizli (confidential) bir OIDC istemcisi ("web uygulaması") oluşturun ve tek bir
redirect URI kaydedin:

```text
https://queryproxy.example.com/auth/oidc/callback
```

Bu adres `APP_URL` artı `/auth/oidc/callback`'tir ve birebir eşleşmelidir.
`APP_URL`'yi kullanıcıların yazdığı adrese, yani ters vekilinizin arkasındaki
HTTPS adresine ayarlayın.

Ayrıca bir **post-logout redirect URI** kaydedin: `APP_URL` artı `/login`, örneğin
`https://queryproxy.example.com/login`. QueryProxy, çıkışta sağlayıcının oturumunu
sonlandırırken bunu gönderir (bkz. [Çıkış yapma](#çıkış-yapma)). Kayıtlı değilse
sağlayıcı kullanıcıyı geri göndermek yerine bir hata sayfası gösterebilir.

Sonra istemciyi QueryProxy tarafında tanımlayın:

```dotenv
QUERYPROXY_OIDC_ISSUER=https://idp.example.com/realms/queryproxy
QUERYPROXY_OIDC_CLIENT_ID=queryproxy
QUERYPROXY_OIDC_CLIENT_SECRET=...        # ya da QUERYPROXY_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc
```

`QUERYPROXY_OIDC_ISSUER`, sonuna `/.well-known/openid-configuration` eklenince
sağlayıcıyı tarif eden belgeye ulaşılan temel URL'dir. Değiştirdikten sonra konteynerleri yeniden başlatın.

| Değişken | Varsayılan | Anlamı |
| :-- | :-- | :-- |
| `QUERYPROXY_OIDC_ISSUER` | yok | Issuer URL'si. Bu ve istemci kimliği birlikte ayarlanınca SSO açılır |
| `QUERYPROXY_OIDC_CLIENT_ID` | yok | Sağlayıcıda kayıtlı istemci kimliği |
| `QUERYPROXY_OIDC_CLIENT_SECRET` | yok | İstemci secret'ı. `QUERYPROXY_OIDC_CLIENT_SECRET_FILE` onu dosyadan okur |
| `QUERYPROXY_OIDC_SCOPES` | `openid email profile` | Sağlayıcıdan istenen kapsamlar, boşlukla ayrılmış. Bazı sağlayıcılar `groups` eklenmesini ister |
| `QUERYPROXY_OIDC_GROUP_CLAIM` | `groups` | Kullanıcının gruplarını listeleyen claim. Tam bu adda claim yoksa `realm_access.roles` gibi noktalı yol izlenir |
| `QUERYPROXY_OIDC_ROLE_MAP` | yok | Virgülle ayrılmış `grup:takım:rol` girdileri; bkz. [Rol eşlemesi](#rol-eşlemesi) |
| `QUERYPROXY_LOCAL_LOGIN` | `all` | Parolayla kimin girebileceği: `all`, `admins_only` ya da `none` |

## ID token'ın taşıması gerekenler

- **`sub`**, her zaman.
- **`email`**, bir sağlayıcı kimliği ilk kez girdiğinde geçerli bir adres. Olmazsa
  QueryProxy hesabı ne bağlayabilir ne oluşturabilir; giriş reddedilir. Zaten
  tanıdığı bir kimlik (aynı issuer ve `sub`) onsuz da girer.
- Daha önce görmediği her hesap için **doğrulanmış e-posta** (aşağıya bakın):
  `email_verified` değeri `true`. Microsoft'un ID token claim referansı bu claim'i Entra ID için listelemez; onun için
  `email_verified` yoksa ve `xms_edov` değeri `true` ise adres doğrulanmış sayılır.
  Açıkça `false` gelen `email_verified` reddedilir. İki claim de adresin
  doğrulandığını söylemiyorsa adres doğrulanmamış sayılır.
- Görünen ad için `name` ya da `preferred_username`. İkisi de yoksa adresin `@`
  öncesi kullanılır.

### İlk giriş, bağlama ve doğrulanmış e-posta

- **İlk girişte yeni kullanıcı oluşturulur** (just-in-time provisioning); ama
  yalnızca sağlayıcı adresin doğrulandığını söylüyorsa (`email_verified` `true`; Entra
  ID için `xms_edov` `true`).
  Yeni kullanıcının parolası ve takımı yoktur; rol eşlemesi ya da bir yönetici
  ona takım verene kadar böyle kalır. Bu yolla kimse sistem yöneticisi olmaz.
- **Aynı e-posta adresine sahip mevcut bir yerel hesap sağlayıcıya bağlanır**; bu
  da yalnızca adres aynı biçimde doğrulanmışsa olur. Bağlama `user.sso_linked` olarak
  denetlenir. Bağlandıktan sonra hesap, siz `QUERYPROXY_LOCAL_LOGIN` ile parola girişini
  kısıtlamadıkça parolasını korur.
- Adres doğrulanmamışsa giriş reddedilir ve ret `auth.sso_refused` olarak
  denetlenir.
- Başka bir sağlayıcı subject'ine zaten bağlı yerel hesap da reddedilir; çünkü
  e-posta eşleşmesi tek başına bir şey kanıtlamaz. Bu ret denetim günlüğüne değil,
  uygulama günlüğüne yazılır.
- Askıya alınmış ve silinmiş kullanıcılar SSO ile de giremez.

Kullanıcının beyan ettiği adresi doğrulamayan bir sağlayıcı bağlamayın.
Doğrulanmamış adresleri doğrulanmış gösteren bir sağlayıcı, aynı e-postaya sahip
bir yerel hesabı ele geçirebilir.

`QUERYPROXY_OIDC_ISSUER`'ı sonradan değiştirmek mevcut SSO hesaplarını taşımaz:
bunlar eski issuer'a bağlıdır ve e-postayla eşleştirilmek yerine reddedilir.

SSO'nun yazdığı denetim olayları: `auth.sso_login`, `auth.sso_refused`,
`user.sso_provisioned`, `user.sso_linked` ve `user.role_synced`.

## Rol eşlemesi

`QUERYPROXY_OIDC_ROLE_MAP`, sağlayıcı gruplarını her girişte takım rollerine
çevirir. Her girdi `grup:takım:rol` biçimindedir; girdiler virgülle ayrılır:

```dotenv
QUERYPROXY_OIDC_ROLE_MAP=platform-dba:payments:dba,platform-dev:payments:developer,security:payments:auditor
```

- **`grup`**, grup claim'indeki değerlerle birebir, büyük/küçük harfe duyarlı bir
  dize olarak karşılaştırılır. Girdi sağdan bölünür; bu yüzden grup adı iki nokta
  içerebilir.
- **`takım`**, var olan bir takımın slug'ı ya da adıdır. Var olmayan bir takımı
  gösteren girdi atlanır ve günlüğe yazılır.
- **`rol`** `dba`, `developer` ya da `auditor`'dır. Sistem yöneticisi bayrağı bir
  gruptan verilemez.
- Aynı takıma birden çok grup ulaşırsa en yüksek rol kazanır (`dba`, sonra
  `developer`, sonra `auditor`).

QueryProxy her girişte kullanıcının **eşlemenin adını andığı takımlardaki**
üyeliklerini gruplarla aynı hâle getirir: ekler, değiştirir ve kaldırır; her
değişikliği `user.role_synced` olarak denetim günlüğüne yazar. Eşlemenin anmadığı
takımlardaki üyeliklere dokunulmaz. Böylece o takımlar için doğruluk kaynağı
sağlayıcı olur: orada elle değiştirdiğiniz rol, kullanıcının bir sonraki girişinde
ezilir.

Bilmeniz gerekenler:

- **Değişiklikler bir sonraki girişte uygulanır.** QueryProxy sağlayıcıyı
  yoklamaz. Birinin erişimini hemen kesmek için Users ekranında askıya alın:
  bu, açık oturumlarını sonlandırır.
- **Grup claim'inin olmaması girişi reddettirir.** Rol eşlemesi ayarlıyken grup
  claim'i olmayan bir ID token reddedilir, günlüğe yazılır ve hiçbir şey
  değiştirilmez. Boş bir grup listesi geçerli bir yanıttır ve olduğu gibi uygulanır:
  kullanıcı eşlenen takımlardaki rollerini kaybeder.
- **Microsoft Entra ID group overage desteklenmez.** Kullanıcı çok fazla gruba
  üyeyse Entra ID `groups` claim'ini koymaz ve Microsoft Graph'a yönlendirir;
  QueryProxy bu yönlendirmeyi izlemez, bu yüzden rol eşlemesi ayarlıyken bu
  kullanıcılar reddedilir. Sınırın altında kalma yolları için Entra ID örneğine
  bakın.
- ID token grup claim'ini taşımıyorsa ya da e-postanın doğrulanıp doğrulanmadığı
  hakkında bir şey söylemiyorsa (ne `email_verified` ne `xms_edov`), QueryProxy
  eksik claim'ler için sağlayıcının userinfo uç noktasını da okur.
- Rol eşlemesi isteğe bağlıdır. Eşleme olmadan SSO ile giren kullanıcılar bir yönetici
  ekleyene kadar takımsız kalır.

## Sağlayıcı örnekleri

Projenin uçtan uca test ettiği tek sağlayıcı Keycloak'tur (CI iş akışı bir Keycloak
26.8.1 konteyneri üzerinden oturum açar ve grup eşlemesini denetler). Diğer üçü sağlayıcının
belgelerini izler; devreye almadan önce bir test kullanıcısıyla deneyin.

### Keycloak

1. Realm'inizde standart (authorization code) akışı kullanan ve istemci secret'ı
   olan bir OpenID Connect istemcisi oluşturun. Geçerli redirect URI'yi yukarıdaki
   gibi verin.
2. İstemcinin özel kapsamına, token claim adı `groups` olan bir **Group
   Membership** mapper'ı ekleyin ve **Add to ID token** açık kalsın.
3. **Full group path** için karar verin. Açıkken grup adları
   `/engineering/backend` gibi yollardır ve rol eşlemesi tam o metni kullanmalıdır.
   Kapatırsanız düz adlar gelir.
4. Ayarlar:

```dotenv
QUERYPROXY_OIDC_ISSUER=https://keycloak.example.com/realms/queryproxy
QUERYPROXY_OIDC_CLIENT_ID=queryproxy
QUERYPROXY_OIDC_CLIENT_SECRET=...
QUERYPROXY_OIDC_ROLE_MAP=dba-team:payments:dba,developers:payments:developer
```

Yalnızca adresi Keycloak'un doğrulanmış (`email_verified`) bildirdiği kullanıcılar
oluşturulabilir ya da bağlanabilir. Kaynak:
[Keycloak server administration guide](https://www.keycloak.org/docs/latest/server_admin/).

### Microsoft Entra ID

1. **App registrations** altında yukarıdaki redirect URI ile bir web uygulaması
   kaydedin ve bir istemci secret'ı oluşturun.
2. Kiracınızın v2.0 issuer'ını kullanın:
   `https://login.microsoftonline.com/<tenant-id>/v2.0`.
3. **Token configuration** altında bir **groups claim** ekleyin. Entra ID varsayılan
   olarak claim'e grup **nesne kimliklerini** (object ID) koyar; rol eşlemesi
   girdileri bu kimlikleri kullanır
   (`0760b6cf-170e-4a14-91b3-4b78e0739963:payments:dba`).
4. Token başına 200 grup sınırının altında kalmak için tüm gruplar yerine
   **Groups assigned to the application** seçeneğini seçin; sınırı aşan
   kullanıcılara `groups` claim'i verilmez ve rol eşlemesi ayarlıyken reddedilirler.
5. QueryProxy'nin e-postayı doğrulamak için kullandığı claim'leri ekleyin.
   **Token configuration** altında **Add optional claim**'i seçin, **ID** token
   türünü seçin, **xms_edov**'u ve token zaten bir e-posta adresi taşımıyorsa
   **email**'i işaretleyip **Add**'e basın. (`xms_edov` yalnızca `email` claim'i
   varken döner. QueryProxy varsayılan olarak `email` kapsamını ister; v2.0
   uç noktasında bu kapsam da `email` claim'ini verir.) Portal `xms_edov`'u
   listelemiyorsa onu uygulamanın **Manifest** bölümünde `optionalClaims.idToken`
   altına ekleyin; Microsoft manifesti isteğe bağlı claim yapılandırmanın diğer yolu
   olarak belgeler.

```dotenv
QUERYPROXY_OIDC_ISSUER=https://login.microsoftonline.com/<tenant-id>/v2.0
QUERYPROXY_OIDC_CLIENT_ID=<application-client-id>
QUERYPROXY_OIDC_CLIENT_SECRET=...
QUERYPROXY_OIDC_ROLE_MAP=<group-object-id>:payments:dba
```

> **`xms_edov` olmadan ilk giriş reddedilir.** Microsoft'un ID token claim referansı
> Entra ID için bir `email_verified` claim'i listelemez. `xms_edov`'u ID token'a eklemezseniz QueryProxy
> adresin doğrulandığını anlayamaz; hesabı ne oluşturur ne bağlar: giriş reddedilir
> ve ret `auth.sso_refused` olarak denetlenir. `xms_edov` yalnızca kiracı adresin
> alan adını doğrulamışsa `true` olur; Microsoft, Facebook ve SAML/WS-Fed
> hesaplarının doğrulanmış alan adı olmadığını belirtir, bu yüzden bu kullanıcılar
> da reddedilir.

Kaynaklar:
[Configure optional claims](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims),
[Optional claims reference](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference)
(`xms_edov`, `email`),
[Group claims in tokens](https://learn.microsoft.com/en-us/security/zero-trust/develop/configure-tokens-group-claims-app-roles).

### Okta

1. Bir **OIDC web uygulaması** oluşturun ve redirect URI'yi kaydedin.
2. ID token'a bir groups claim ekleyin: uygulamanın **Sign On** sekmesinde
   **OpenID Connect ID Token** bölümünü düzenleyin ve claim adı `groups` olan, eşlediğiniz
   grupları döndüren bir filtreyle **Group claim type** ayarlayın. Özel bir
   authorization server'da bunun yerine ID token'a **Groups** değer türünde bir
   claim ekleyin.
3. Org authorization server ile `groups` kapsamını isteyin:

```dotenv
QUERYPROXY_OIDC_ISSUER=https://example.okta.com
QUERYPROXY_OIDC_CLIENT_ID=<client-id>
QUERYPROXY_OIDC_CLIENT_SECRET=...
QUERYPROXY_OIDC_SCOPES="openid email profile groups"
QUERYPROXY_OIDC_ROLE_MAP=Platform DBA:payments:dba
```

Özel bir authorization server'da issuer onun URL'sidir, örneğin
`https://example.okta.com/oauth2/default`; orada gruplar eklediğiniz claim'den gelir
ve `groups`, `QUERYPROXY_OIDC_SCOPES` içinde yalnızca o claim'i bir `groups` kapsamına
bağladıysanız gerekir. Kaynak:
[Customize tokens with a groups claim](https://developer.okta.com/docs/guides/customize-tokens-groups-claim/main/).

### Google

Google Cloud konsolunda **Web application** türünde bir OAuth istemcisi oluşturun
ve redirect URI'yi ekleyin. Google'ın issuer'ı `https://accounts.google.com`'dur ve
`email` kapsamı istendiğinde ID token'ı `email_verified` taşır. Google'ın OpenID
Connect belgeleri bir groups claim'i anlatmaz; bu yüzden Google'ı yalnızca giriş
için kullanın, `QUERYPROXY_OIDC_ROLE_MAP`'i boş bırakın ve takımları kullanıcılara
elle verin.

```dotenv
QUERYPROXY_OIDC_ISSUER=https://accounts.google.com
QUERYPROXY_OIDC_CLIENT_ID=<client-id>.apps.googleusercontent.com
QUERYPROXY_OIDC_CLIENT_SECRET=...
```

Kaynak: [OpenID Connect | Google Identity](https://developers.google.com/identity/openid-connect/openid-connect).

## Çıkış yapma

SSO ile girmiş bir kullanıcı QueryProxy'den çıkınca QueryProxy kimlik
sağlayıcıdaki oturumu da sonlandırır (RP-initiated logout); kullanıcı orada da
çıkmış olur. Tarayıcıyı sağlayıcının `end_session_endpoint` adresine, kullanıcının
ID token'ı `id_token_hint`, `client_id` ve post-logout redirect URI'si
`{APP_URL}/login` ile yönlendirir.

- Sağlayıcı discovery belgesinde bir `end_session_endpoint` yayımlamalıdır. Yoksa
  ya da discovery belgesi okunamıyorsa çıkış yalnızca QueryProxy oturumunu
  sonlandırır ve giriş sayfasına döner.
- Parolayla açılmış oturum ve yerel kullanıcı sağlayıcıya asla yönlendirilmez.
- ID token yalnızca bu amaçla sunucu tarafındaki oturumda tutulur. Günlüğe ya da
  denetim günlüğüne yazılmaz.
- Çıkış, eskiden olduğu gibi CSRF token'lı bir POST'tur.

## SSO'nun yanında parola girişi

`QUERYPROXY_LOCAL_LOGIN`, parolayı hâlâ kimin kullanabileceğini sınırlar:

| Değer | Anlamı |
| :-- | :-- |
| `all` | Varsayılan. Parolası olan herkes kullanabilir |
| `admins_only` | Yalnızca sistem yöneticileri parolayla girer (break-glass erişimi); diğer herkes SSO kullanır |
| `none` | Parola girişi ve parola sıfırlama kapalıdır |

Bilinmeyen bir değer `admins_only` sayılır. `none`'ı ayarlamadan önce SSO'nun, onu
kullanabilen bir yönetici hesabıyla çalıştığından emin olun: geri girmek için
değişkeni değiştirip yeniden başlatmanız gerekir.

Yalnızca SSO üzerinden var olan hesapların yerel parolası yoktur. Sağlayıcıya
bağlı bir kullanıcı sıfırlama bağlantısıyla parola almaz ve yöneticinin **Reset
password** işlemi, `QUERYPROXY_LOCAL_LOGIN` ne derse desin onu reddeder. Profil
sayfasındaki parola formu yerel parolası olmayan kullanıcılarda gizlenir.
[SCIM](/tr/docs/scim-kullanici-saglama/)'in açtığı hesaplar SSO hesabı sayılır.
`QUERYPROXY_REQUIRE_2FA`, sağlayıcıya bağlanmış ve parolasıyla giren yerel hesap
için geçerli olmaya devam eder.

Sağlayıcıya bağlı **ve** daha önce yerel parolası olan bir kullanıcı,
`QUERYPROXY_LOCAL_LOGIN` `all` olduğu sürece o parolayla girmeye devam edebilir ve
profil sayfasından değiştirebilir. Sağlayıcıyı herkes için tek giriş yolu yapmak
için `QUERYPROXY_LOCAL_LOGIN=admins_only` ya da `none` ayarlayın.

## Sınırlar

- **Sağlayıcıda çıkış bir `end_session_endpoint` ister.** Bunu yayımlamayan bir
  sağlayıcı kullanıcıyı orada açık bırakır; bkz. [Çıkış yapma](#çıkış-yapma).
- Kurulum başına **tek sağlayıcı**. SAML desteklenmez. Girişler arasında erişimi
  kaldırmak [SCIM ile kullanıcı sağlama](/tr/docs/scim-kullanici-saglama/)nın işidir.
- Parolası olmayan bir SSO kullanıcısı için kişisel erişim token'ı oluşturmak,
  son 15 dakika içinde SSO ile girmiş olmayı ister; bkz.
  [REST API ve token'lar](/tr/docs/rest-api/).

## Sonraki adımlar

- Kullanıcıları sağlayıcıdan oluşturmak ve askıya almak için [SCIM ile kullanıcı sağlama](/tr/docs/scim-kullanici-saglama/).
- Değişkenlerin bağlamı için [Yapılandırma](/tr/docs/yapilandirma/#tek-oturum-açma).
- Kontrol listesinin kalanı için [Üretim güvenliği](/tr/docs/uretim-guvenligi/).
- Her takım rolünün neye izin verdiği için [Onay akışı](/tr/docs/onay-akisi/).
