We detected you are likely not from a Russian-speaking region. Would you like to switch to the international version of the site?

К списку статей

Обеспечение безопасности API: интеграция OAuth 2.0 и OpenID Connect в Laravel и FastAPI с Keycloak

02 фев 2026 в 00:30 Усачёв Денис Евгеньевич

Современные веб‑приложения всё чаще работают в виде микросервисов и открытых API. Защита таких точек входа требует надёжного механизма аутентификации и авторизации. Наиболее популярным решением является комбинация OAuth 2.0 и OpenID Connect (OIDC), реализованная через Identity Provider – в нашем случае Keycloak. В статье рассматривается, как быстро и безопасно интегрировать эту схему в два популярных стека: Laravel (PHP) и FastAPI (Python).

OAuth 2.0 и OpenID Connect: основные понятия

OAuth 2.0 – протокол делегирования доступа, позволяющий клиенту получать токен доступа (access_token) от авторизационного сервера без передачи пользовательских учётных данных. OpenID Connect – надстройка над OAuth 2.0, добавляющая слой аутентификации: в токен id_token включается информация о пользователе (claims).

  • Authorization Code Flow – лучший выбор для серверных приложений и SPA.
  • Client Credentials Flow – используется для машинного взаимодействия (service‑to‑service).
  • Refresh Token – позволяет обновлять access_token без повторного логина.

Keycloak поддерживает все стандартные потоки, а также предоставляет UI для управления клиентами, ролями и политиками доступа.

Keycloak как централизованный провайдер идентификации

Перед тем как приступить к интеграции, необходимо подготовить Keycloak:

  1. Создать Realm – изолированную область безопасности.
  2. Внутри Realm добавить два Client:
    • laravel-api – тип confidential, включить Standard Flow и Direct Access Grants.
    • fastapi-service – тип bearer-only (если FastAPI будет только проверять токены) либо confidential для собственного токен‑выпуска.
  3. Определить нужные Roles и привязать их к клиентам.
  4. Сгенерировать client secret и сохранить его в безопасном месте.

Интеграция с Laravel

В Laravel есть два популярных подхода:

  • Использовать laravel/passport – полностью реализует OAuth 2.0, но требует собственного сервера токенов.
  • Воспользоваться socialiteproviders/keycloak (или обычным league/oauth2-client) – Laravel остаётся клиентом Keycloak.

Ниже пример минимальной конфигурации через socialiteproviders/keycloak:

composer require socialiteproviders/keycloak

// config/services.php
return [
    'keycloak' => [
        'client_id' => env('KEYCLOAK_CLIENT_ID'),
        'client_secret' => env('KEYCLOAK_CLIENT_SECRET'),
        'redirect' => env('KEYCLOAK_REDIRECT_URI'),
        'base_url' => env('KEYCLOAK_BASE_URL'), // https://keycloak.example.com/auth
        'realm' => env('KEYCLOAK_REALM'),
    ],
];

Маршрут для редиректа:

// routes/web.php
Route::get('login/keycloak', function () {
    return Socialite::driver('keycloak')->redirect();
});

Route::get('login/keycloak/callback', function () {
    $user = Socialite::driver('keycloak')->user();
    // Поиск/создание локального пользователя
    $localUser = \App\Models\User::firstOrCreate([
        'email' => $user->getEmail(),
    ], [
        'name' => $user->getName(),
        'keycloak_id' => $user->getId(),
    ]);
    Auth::login($localUser);
    return redirect('/home');
});

Для защиты API‑эндпоинтов создаём middleware, которое проверяет Bearer токен через публичный ключ Keycloak:

// app/Http/Middleware/KeycloakToken.php
namespace App\Http\Middleware;

use Closure;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class KeycloakToken
{
    public function handle($request, Closure $next)
    {
        $authHeader = $request->header('Authorization');
        if (!$authHeader || !preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
            return response()->json(['error' => 'Unauthenticated'], 401);
        }
        $token = $matches[1];
        // Получаем публичный ключ из Keycloak (можно кэшировать)
        $jwks = json_decode(file_get_contents(config('services.keycloak.base_url').'/realms/'.config('services.keycloak.realm').'/protocol/openid-connect/certs'));
        foreach ($jwks->keys as $jwk) {
            try {
                $payload = JWT::decode($token, new Key($jwk->x5c[0], $jwk->alg));
                // При необходимости проверяем роли
                $request->attributes->set('jwt_payload', (array) $payload);
                return $next($request);
            } catch (\Exception $e) {
                // пробуем следующий ключ
            }
        }
        return response()->json(['error' => 'Invalid token'], 401);
    }
}

Регистрация middleware в app/Http/Kernel.php и применение к роутам:

protected $routeMiddleware = [
    // ... другие middleware
    'keycloak.auth' => \App\Http\Middleware\KeycloakToken::class,
];

// routes/api.php
Route::middleware('keycloak.auth')->group(function () {
    Route::get('/user/profile', [UserController::class, 'profile']);
});

Интеграция с FastAPI

Для Python‑стека удобно использовать python-keycloak (клиент) совместно с Authlib или готовый пакет fastapi-keycloak. Ниже показан пример без внешних зависимостей – только python-jose для валидации JWT.

# Установка зависимостей
pip install fastapi uvicorn python-jose[cryptography] httpx

Файл keycloak_auth.py – утилита для получения JWK и проверки токена:

import httpx
from jose import jwt, JWTError
from functools import lru_cache

KEYCLOAK_URL = "https://keycloak.example.com/auth"
REALM = "myrealm"

@lru_cache()
def get_jwks():
    resp = httpx.get(f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/certs")
    resp.raise_for_status()
    return resp.json()["keys"]

def verify_token(token: str):
    jwks = get_jwks()
    for jwk in jwks:
        try:
            payload = jwt.decode(
                token,
                jwk,
                algorithms=[jwk["alg"]],
                audience="fastapi-service",
                issuer=f"{KEYCLOAK_URL}/realms/{REALM}"
            )
            return payload
        except JWTError:
            continue
    raise JWTError("Invalid token")

Middleware в FastAPI, которое добавляет payload в запрос:

from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

class KeycloakMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        auth = request.headers.get('Authorization')
        if not auth or not auth.lower().startswith('bearer '):
            raise HTTPException(status_code=401, detail='Unauthenticated')
        token = auth.split(' ', 1)[1]
        try:
            payload = verify_token(token)
            request.state.user = payload
        except JWTError as e:
            raise HTTPException(status_code=401, detail='Invalid token')
        response = await call_next(request)
        return response

app.add_middleware(KeycloakMiddleware)

@app.get('/protected')
async def protected_endpoint(request: Request):
    return {"user": request.state.user}

Для сервис‑to‑service взаимодействия (Client Credentials Flow) достаточно запросить токен через httpx:

async def get_service_token():
    data = {
        'grant_type': 'client_credentials',
        'client_id': 'fastapi-service',
        'client_secret': 'YOUR_CLIENT_SECRET',
    }
    resp = await httpx.post(
        f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/token",
        data=data,
    )
    resp.raise_for_status()
    return resp.json()['access_token']

Практические рекомендации

  • Храните client_secret в безопасных переменных окружения (Docker secrets, .env файлы с ограниченным доступом).
  • Кешируйте JWK‑ключи (например, на 5‑10 минут) – уменьшит нагрузку на Keycloak.
  • Включайте проверку exp, nbf и aud в каждом сервисе.
  • Для микросервисов используйте Client Credentials Flow и отдельные клиентские роли.
  • Регулярно обновляйте зависимости (особенно библиотеки JWT) для защиты от уязвимостей.
  • Логи безопасности: фиксируйте неудачные попытки аутентификации и истёкшие токены.

Заключение

Интеграция OAuth 2.0 и OpenID Connect через Keycloak предоставляет единый, масштабируемый и проверенный способ защиты API как в PHP‑стеке (Laravel), так и в Python‑стеке (FastAPI). Правильная настройка клиента, валидные токены и соблюдение best‑practice позволяют построить надёжную архитектуру, готовую к росту и соответствию требованиям GDPR, PCI‑DSS и другим нормативам.

Услуги RybinskLab

RybinskLab предлагает полный цикл разработки защищённых API: от архитектурного проектирования и настройки Keycloak до внедрения кастомных middleware в Laravel и FastAPI. Мы поможем ускорить вывод продукта на рынок, обеспечив при этом высокий уровень безопасности и соответствие отраслевым стандартам. Свяжитесь с нами, чтобы обсудить ваш проект.

* Материал подготовлен с использованием ИИ-ассистента, проверен и отредактирован экспертом RybinskLab.

Поделиться материалом:

Нужна сложная разработка?

Усачёв Денис Евгеньевич — проектирование архитектуры, бэкенд на PHP/Python, интеграции API и базы данных.

Обсудить проект