
# Laravel uygulamasına oturum açma ekleyin

Bu hızlı başlangıç, standart OpenID Connect istemcisini `facile-it/php-openid-client` kullanarak bir Laravel 13 uygulamasına KeenAuth ile oturum açmayı ekler. KeenAuth'un henüz bir PHP SDK'sı yok ve buna ihtiyaç da yok: KeenAuth standart bir OpenID Connect sağlayıcısıdır, dolayısıyla standart bir OpenID Connect istemci kütüphanesi çalışır. Uygulamanız OAuth istemcisidir ve tüm belirteçleri sunucu tarafındaki oturumda tutar; tarayıcıya yalnızca şifrelenmiş bir oturum kimliği çerezi gider. Oturum açma rotalarını ekler, bir sayfayı korur, korumalı bir API'yi kullanıcının erişim belirteciyle çağırır, oturumu kapatır ve uygulamayı çevrimdışı test edersiniz.

Uygulamanın şu rotaları vardır:

| Rota | Ne yapar |
|---|---|
| `GET /` | Herkese açık ana sayfa: bir oturum açma düğmesi ya da kullanıcının adı ve bir oturum kapatma düğmesi |
| `GET /dashboard` | Oturum gerektirir. Kullanıcıyı ve API'nizin döndürdüğü iletileri gösterir |
| `POST /login` | Oturum açmayı başlatır. Laravel CSRF belirtecini denetler |
| `GET /auth/callback` | Yanıtı denetler, kodu değiştirir ve oturumu başlatır |
| `POST /logout` | Oturumu burada ve KeenAuth'ta sonlandırır |

## Başlamadan önce

- Bir KeenAuth ortamı. Bu ortamın yayımcısı `https://your-environment.eu.keenauth.app` biçimindedir.
- Ortamda bir web uygulaması ([Uygulama oluşturun](/docs/api/management/applications/create)) ve bir istemci gizli anahtarı (`client_secret_basic`). `development` ortamında, yönlendirme URI'si `http://localhost/auth/callback` ve oturum kapatma sonrası yönlendirme URI'si `http://localhost/` kaydedin; geri döngü URI'si bağlantı noktası olmadan kaydedilir ve `8000` gibi herhangi bir bağlantı noktasıyla eşleşir.
- Ortamda kayıtlı bir API ([API oluşturun](/docs/api/management/apis/create)) ve `messages:read` kapsamı. `identifier` değeri, örneğin `https://api.example.com`, erişim belirteçlerinin hedef kitlesidir. [Express hızlı başlangıcı](/docs/quickstarts/express), bu belirteçleri kabul eden bir API oluşturur.
- PHP 8.3 veya daha yeni bir sürüm ve Composer 2.

## 1. Yükleyin

```sh
composer require facile-it/php-openid-client:1.0.0 guzzlehttp/guzzle:8.3.1
```

Kütüphane bir PSR-18 HTTP istemcisine ihtiyaç duyar; Guzzle bunlardan biridir. Örnek, `composer.json` dosyasında her paketi tam olarak sabitler ve `composer.lock`, her paketi bir commit'e sabitler. `composer install --no-plugins --no-scripts` ile yüklenir, bu nedenle yükleme sırasında hiçbir paket kodu çalışmaz.

## 2. Yapılandırın

Uygulama yapılandırmasını yalnızca ortam değişkenlerinden, `config/keenauth.php` aracılığıyla okur. Bunları Laravel'in yüklediği `.env` dosyasına koyun ve bu dosyayı asla commit etmeyin.

| Değişken | Zorunlu | Anlamı |
|---|---|---|
| `KEENAUTH_ISSUER` | evet | Ortamınızın yayımcısı, `https` |
| `KEENAUTH_CLIENT_ID` | evet | Web uygulamasının `client_id` |
| `KEENAUTH_CLIENT_SECRET` | evet | Uygulamanın istemci gizli anahtarı |
| `APP_BASE_URL` | evet | Uygulamanın herkese açık URL'si. Yönlendirme URI'si bundan oluşturulur. `http` yalnızca `localhost`, `127.0.0.1` veya `[::1]` için kullanılır |
| `API_URL` | evet | Uygulamanın çağırdığı API, örneğin `http://localhost:8080` |
| `KEENAUTH_API_AUDIENCE` | evet | Bu API'nin KeenAuth'taki tanımlayıcısı |
| `KEENAUTH_ID_TOKEN_ALG` | hayır | Ortamınızın ID belirteçlerini imzaladığı algoritma: `RS256` (varsayılan), `PS256`, `ES256` veya `EdDSA` |
| `APP_KEY` | evet | Laravel'in kendi anahtarı; oturum çerezini şifreler. `php artisan key:generate` ile bir anahtar üretin |

`Config::fromArray` bu değerleri denetler. Eksik ya da kullanılamayan bir değer, ihtiyaç duyan ilk isteği durdurur ve hiçbir hata iletisi bir değer içermez. HMAC algoritmaları reddedilir; böylece istemci gizli anahtarı hiçbir zaman bir ID belirtecini imzalayamaz:

```php
        $alg = $values['id_token_alg'] ?? 'RS256';
        if (!in_array($alg, ['RS256', 'PS256', 'ES256', 'EdDSA'], true)) {
            // Never an HMAC algorithm: the client secret must not be able to sign an ID token.
            throw new InvalidArgumentException('KEENAUTH_ID_TOKEN_ALG must be RS256, PS256, ES256 or EdDSA');
        }
```

## 3. OpenID Connect istemcisini oluşturun

`App\KeenAuth\KeenAuth`, istemciyi keşif (`<issuer>/.well-known/openid-configuration`) üzerinden oluşturur ve üst veriyi ile anahtar kümesini bir saat önbellekte tutar:

```php
        $issuer = (new IssuerBuilder())
            ->setMetadataProviderBuilder((new MetadataProviderBuilder())->setHttpClient($this->http)
                ->setCache($this->cache)->setCacheTtl(3600))
            ->setJwksProviderBuilder((new JwksProviderBuilder())->withHttpClient($this->http)
                ->withCache($this->cache)->withCacheTtl(3600))
            ->build($this->config->issuer);
```

```php
            ->setClientMetadata(ClientMetadata::fromArray([
                'client_id' => $this->config->clientId,
                'client_secret' => $this->config->clientSecret,
                'token_endpoint_auth_method' => 'client_secret_basic',
                'redirect_uris' => [$this->config->redirectUri()],
                // The verifier accepts only this algorithm, never an HMAC one.
                'id_token_signed_response_alg' => $this->config->idTokenAlg,
            ]))
```

Servis sağlayıcı, istemciyi tekil (singleton) olarak kaydeder; HTTP istemcisini ise testlerin değiştirebilmesi için ayrı bir bağlama olarak kaydeder.

## 4. Oturum açmayı başlatın

Her oturum açma işlemi yeni bir `state`, `nonce` ve PKCE doğrulayıcısı üretir ve bunları oturumda saklar:

```php
    public static function newTransaction(): array
    {
        return [
            'state' => self::base64url(random_bytes(32)),
            'nonce' => self::base64url(random_bytes(32)),
            'code_verifier' => self::base64url(random_bytes(32)),
        ];
    }
```

Yetkilendirme isteği, doğrulayıcının S256 özetini ve API'nizin tanımlayıcısını `resource` olarak gönderir (RFC 8707). `resource` olmadan erişim belirteci API'niz için düzenlenmez ve API onu reddeder.

```php
        return $this->service()->getAuthorizationUri($this->client(), [
            // messages:read is a scope of your API; the example API requires it on /api/messages.
            'scope' => 'openid profile email messages:read',
            'state' => $transaction['state'],
            'nonce' => $transaction['nonce'],
            // PKCE (RFC 7636): S256 of the verifier that only this session knows.
            'code_challenge' => self::base64url(hash('sha256', $transaction['code_verifier'], true)),
            'code_challenge_method' => 'S256',
            // RFC 8707: an access token for your API.
            'resource' => $this->config->apiAudience,
        ]);
```

Oturum açma, CSRF belirteciyle `/login` adresine gönderim yapan bir formdur; böylece başka bir site kullanıcı adına oturum açmayı başlatamaz:

```php
    <form action="/login" method="post">
      @csrf
      <button type="submit">Sign in</button>
    </form>
```

## 5. Geri çağrıyı işleyin

Kütüphane `state` denetimini uygulamanıza bırakır. Geri çağrı, işlemi oturumdan alır; böylece bir `state` yalnızca bir kez çalışır ve sabit zamanda karşılaştırılır. Ortamınız her yetkilendirme yanıtında kendini adlandırır (RFC 9207); bu nedenle geri çağrı, `iss` değeri eksik olan ya da başka bir yayımcıyı gösteren yanıtı da reddeder:

```php
        $transaction = $request->session()->pull('keenauth.transaction');
        $state = $request->query('state');
        if (!is_array($transaction) || !is_string($state) || !hash_equals($transaction['state'], $state)) {
            return $this->failure('invalid_state');
        }
        // RFC 9207: the response names its issuer, so a response from another provider is refused.
        $iss = $request->query('iss');
        if ($keenauth->issuerInResponse() && $iss !== $keenauth->config->issuer) {
            return $this->failure('invalid_issuer');
        }
```

Geri çağrıdan yalnızca kod alınır. Kütüphane kodu PKCE doğrulayıcısıyla değiştirir ve ID belirtecini denetler: ortamınızın anahtarlarıyla imzasını, `iss`, `aud`, `exp`, `iat`, algoritmayı ve bu oturum açmaya ait `nonce` değerini.

```php
        return $this->service()->callback($this->client(), ['code' => $code], $this->config->redirectUri(),
            AuthSession::fromArray($transaction));
```

Uygulama bundan sonra yeni bir oturum kimliği başlatır ve kullanıcıyı ile belirteçleri sunucu tarafındaki oturumda tutar:

```php
        // A new session id after sign-in prevents session fixation.
        $request->session()->regenerate(destroy: true);
```

Oturum ayarları `config/session.php` dosyasındadır: `file` sürücüsü ve şifrelenmiş, HttpOnly, `SameSite=Lax` bir çerez; bu çerez `Secure` olur, ancak `SESSION_SECURE_COOKIE=false` değerini `http://localhost` için ayarlarsanız farklı olur. Birden çok sunucu kullanıyorsanız `redis` ya da `database` sürücüsünü kullanın.

## 6. Sayfaları koruyun ve API'nizi çağırın

Kullanıcı gerektiren rotalar `keenauth` ara katmanını kullanır. Örnekte yenileme belirteci yoktur: erişim belirteci sona erdiğinde kullanıcı yeniden oturum açar.

```php
Route::get('/dashboard', [DashboardController::class, 'show'])->middleware('keenauth');
```

Kontrol paneli, API'nizi erişim belirteciyle taşıyıcı belirteç olarak çağırır. Belirteç sunucuda kalır; tarayıcı yalnızca HTML alır.

```php
        $response = $http->sendRequest(new HttpRequest('GET', $keenauth->config->apiUrl . '/api/messages', [
            'Authorization' => 'Bearer ' . $request->session()->get('keenauth.access_token'),
            'Accept' => 'application/json',
        ]));
```

## 7. Oturumu kapatın

Oturum kapatma, CSRF belirteciyle gönderilen bir `POST`'dır. Uygulama yerel oturumu sonlandırır ve tarayıcıyı ortamınızın çıkış uç noktasına (OpenID Connect RP-Initiated Logout) yönlendirir; bunu `id_token_hint`, `client_id` ve `post_logout_redirect_uri` ile yapar ve tarayıcı uygulamanıza geri döner:

```php
        $idToken = $request->session()->get('keenauth.id_token');
        $request->session()->invalidate();
        $request->session()->regenerateToken();
```

## 8. Uygulamayı çalıştırın

Express hızlı başlangıcındaki API'yi (ya da bu belirteçleri kabul eden herhangi bir API'yi) başlatın ve uygulamayı çalıştırın:

```sh
php artisan serve
```

`http://localhost:8000` adresini açın, **Giriş yap** seçeneğini seçin, KeenAuth'ta oturum açın ve kontrol panelini açın.

## 9. Ağ olmadan test edin

Testler ağ bağlantısı ya da bir KeenAuth hesabı gerektirmez. Sahte bir PSR-18 istemcisi HTTP istemcisinin yerini alır ve ortamınızı (keşif, test çalıştırması için üretilmiş bir anahtar kümesi, belirteç uç noktası) ve API'yi taklit eder. Testler `APP_ENV=ci` ile çalışır, çünkü Laravel CSRF denetimini `testing` altında atlar ve bu testler onu denetler.

```php
    public function test_callback_with_an_unknown_state_is_refused_before_any_token_request(): void
    {
        $this->startSignIn();
        $this->returnFromKeenAuth(['state' => 'not-the-state'])->assertStatus(401)->assertSee('invalid_state');
```

```sh
vendor/bin/phpunit
php ../scripts/php_deps.php    # exact pins, commits and licences
```

Testler şunları denetler: yapılandırmanın doğrulanması (ve hiçbir hata iletisinde gizli bilginin bulunmaması); oturum açmanın CSRF belirteci gerektirmesi ve PKCE S256 göndermesi, `state`, `nonce` ve `resource`; kodun eşleşen doğrulayıcıyla ve istemci kimlik doğrulamasıyla değiştirilmesi; erişim belirtecinin `Authorization` başlığıyla API'ye gönderilmesi ve hiçbir zaman bir çereze yazılmaması; bilinmeyen ya da yeniden kullanılmış bir `state`, eksik ya da yanlış bir `iss`, başka bir istemci için ya da farklı bir nonce ile üretilmiş ID belirteci ve bir yetkilendirme hatasının hiçbir oturum oluşturmadan reddedilmesi; oturumu olmayan bir sayfanın ana sayfaya yönlendirilmesi; ve oturum kapatmanın CSRF belirteci gerektirmesi ve `id_token_hint` ile çıkış uç noktasına gitmesi.

Gerçek bir tarayıcıyla tam oturum açma için bir KeenAuth ortamı gerekir. Bu, çevrimdışı testlerin parçası değildir.

## Bir proxy ya da yük dengeleyici arkasında

`APP_BASE_URL` değerini uygulamanın herkese açık URL'sine ayarlayın; yönlendirme URI'si bundan oluşturulur, hiçbir zaman `Host` başlığından oluşturulmaz. Bir proxy TLS'yi sonlandırıyorsa, Laravel'in güvenilen proxy ayarını yalnızca o proxy için yapılandırın.

## Bugünkü sınırlar

- Yenileme belirteci ve sessiz oturum açma yoktur: erişim belirteci sona erdiğinde kullanıcı yeniden oturum açar.
- İstemci, bir istemci gizli anahtarıyla kimlik doğrular. Kütüphane `private_key_jwt` yöntemini de destekler; örnek bunu göstermez.

## Sonraki adımlar

- [Express API'sini koruyun](/docs/quickstarts/express): bu uygulamanın çağırdığı API.
- [Belirteç doğrulama](/docs/sdk/token-verification): API'nizin ara katmanının altındaki doğrulayıcı.
