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.appbiçimindedir. - Ortamda bir web uygulaması (Uygulama oluşturun) ve bir istemci gizli anahtarı (
client_secret_basic).developmentortamında, yönlendirme URI'sihttp://localhost/auth/callbackve oturum kapatma sonrası yönlendirme URI'sihttp://localhost/kaydedin; geri döngü URI'si bağlantı noktası olmadan kaydedilir ve8000gibi herhangi bir bağlantı noktasıyla eşleşir. - Ortamda kayıtlı bir API (API oluşturun) ve
messages:readkapsamı.identifierdeğeri, örneğinhttps://api.example.com, erişim belirteçlerinin hedef kitlesidir. Express hızlı başlangıcı, 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
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:
$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:
$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);
->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:
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.
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:
<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:
$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.
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:
// 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.
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.
$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:
$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:
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.
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');
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_jwtyöntemini de destekler; örnek bunu göstermez.
Sonraki adımlar
- Express API'sini koruyun: bu uygulamanın çağırdığı API.
- Belirteç doğrulama: API'nizin ara katmanının altındaki doğrulayıcı.